Skip to content

feat(delegate): add background task lifecycle - #4503

Open
KirschBluteX wants to merge 5 commits into
OpenHands:mainfrom
KirschBluteX:feat/background-delegation-2047
Open

feat(delegate): add background task lifecycle#4503
KirschBluteX wants to merge 5 commits into
OpenHands:mainfrom
KirschBluteX:feat/background-delegation-2047

Conversation

@KirschBluteX

@KirschBluteX KirschBluteX commented Aug 15, 2026

Copy link
Copy Markdown

HUMAN:
I designed and implemented process-local background task execution in the existing TaskToolSet/TaskManager, with stable task identities, an explicit queued/running/completed/error/cancelled state machine, synchronization scoped to the task manager and parent conversation, parallel execution of independent tasks, cooperative cancellation, bounded stop/close waiting without force-killing threads, exactly-once terminal settlement and usage accounting, and compatibility with the existing blocking path. I produced the self-contained design document and added deterministic concurrency, cancellation, cleanup, and accounting coverage. I personally ran and reviewed the 165 focused and related tests, scoped pre-commit checks, and the SDK API compatibility check documented below.


AGENT:
AI assistance was limited to targeted secondary review of lifecycle edge cases, tests, documentation, and organization of the verification evidence. I personally reviewed that feedback, made the architecture and implementation decisions, ran the acceptance commands, and verified the final behavior before submission.

Why

The original background attempt kept a separate lifecycle registry beside DelegateExecutor. That duplicated task ownership and left the public API and existing task persistence paths inconsistent. This revision puts the lifecycle on the existing TaskToolSet owner, while keeping blocking task execution fully compatible by default.

Summary

  • Add explicit opt-in TaskAction(run_in_background=True).
  • Add task_output and task_stop tools backed by the same parent-scoped TaskManager.
  • Use queued -> running -> completed | error | cancelled transitions with stable task IDs.
  • Run independent tasks concurrently while rejecting a second active resume of the same task.
  • Use LocalConversation.arun() and cooperative interrupt() cancellation; never force-kill Python threads.
  • Bound stop and manager-close cleanup, including wake-up of blocked output readers.
  • Settle output, exceptions, metrics, and conversation cleanup exactly once.
  • Preserve blocking mode, resume behavior, confirmation handling, persistence, visualizers, agent definitions, and child limits.

Task IDs and live worker state are process-local. After a process restart, an old ID is reported as unknown even when persisted conversation files remain.

API Surface

  • TaskAction(run_in_background=True) returns a TaskObservation with the stable task ID and an initial queued or running status.
  • TaskOutputAction(task_id, block=False, timeout=30.0) is read-only. With block=True, it waits for settlement, stop, or manager close, subject to a finite 0-3600 second timeout.
  • TaskStopAction(task_id) requests LocalConversation.interrupt(), waits up to the bounded cleanup deadline, and reports the resulting status.
  • Unknown IDs, cross-parent access, active resume, and stopping completed or failed tasks produce deterministic error observations. Repeated output reads do not mutate state; repeated stops for a cancelled task are deterministic and do not duplicate metrics.

Issue Number

Addresses #2047 and unblocks the background work tracked by #2186.

How to Test

uv run --no-sync python -X utf8 -m pytest -q \
  tests/tools/task \
  tests/tools/delegate/test_delegation.py \
  tests/tools/workflow/test_workflow_tool.py

Result: 165 passed, 71 warnings.

Scoped pre-commit checks for the changed SDK files passed: Ruff format/check, pycodestyle, Pyright, import dependency rules, and tool registration.

Design Doc

https://htmlpreview.github.io/?https://github.com/KirschBluteX/software-agent-sdk/blob/feat/background-delegation-2047/.pr/design.html

Type

  • Bug fix
  • Feature
  • Refactor
  • Breaking change
  • Docs / chore

Notes

  • Cancellation is cooperative and bounded; an unresponsive child callback cannot be force-killed.
  • Companion documentation: OpenHands/docs#725.
  • The registry intentionally belongs to one TaskManager and parent conversation; persisted conversations do not imply restorable worker threads or task IDs.

Co-authored-by: openhands <openhands@all-hands.dev>
@github-actions

Copy link
Copy Markdown
Contributor

📁 PR Artifacts Notice

This PR contains a .pr/ directory with temporary PR-specific documents. Because this is a fork PR, the directory will be automatically removed from main immediately after merge.

@KirschBluteX
KirschBluteX marked this pull request as ready for review August 15, 2026 14:57
@KirschBluteX

Copy link
Copy Markdown
Author

Maintainer action needed to unblock the remaining gates:

  • GitHub has left the fork pull_request workflows (including Run tests, Pre-commit checks, and Python API breakage checks) in action_required; please approve those workflow runs.
  • Please add the integration-test label, as this changes delegate tool lifecycle behavior and descriptions.
  • If the ready-for-review event did not start OpenHands Cloud review, please request all-hands-bot; the fork author cannot request that reviewer directly.

The PR description contains the completed local test, smoke, pre-commit, and API-compatibility evidence.

Co-authored-by: openhands <openhands@all-hands.dev>
@all-hands-bot

Copy link
Copy Markdown
Collaborator

🚦 CI is currently failing on this PR's latest commit.

Please fix the failing checks before OpenHands reviews it - this is re-checked automatically once you push a new commit. (A maintainer can also request @all-hands-bot as a reviewer to have it reviewed regardless of CI status.)

This is an automated check - no AI was used to generate this comment.

@KirschBluteX

Copy link
Copy Markdown
Author

@enyst, this PR has been substantially redesigned since my earlier CI-gate comment. The current implementation places background execution in the existing TaskToolSet / TaskManager ownership boundary rather than extending the legacy Delegate lifecycle.

The public contract is:

  • Blocking task calls remain unchanged by default.
  • TaskAction(run_in_background=True) returns a stable, process-local task ID.
  • task_output reports or waits for queued, running, completed, error, or cancelled.
  • task_stop uses cooperative LocalConversation.interrupt() with bounded stop/close waiting and never attempts to terminate a Python thread.
    The manager allows independent tasks to run concurrently, rejects a second active resume of the same task, wakes blocked output readers during stop/close, and settles terminal state, final response, metrics, and conversation cleanup exactly once. Task handles are intentionally not restored after process restart; persisted child-conversation data does not represent a live worker.
    Local validation covered 165 focused and related tests (165 passed, 71 warnings), plus scoped Ruff, pycodestyle, Pyright, import-boundary, tool-registration, and API-compatibility checks. The PR also includes deterministic concurrency, cancellation, close-race, worker-cleanup, and exactly-once accounting coverage, together with a self-contained design document. The companion user documentation is in OpenHands/docs#725.
    The remaining GitHub fork workflows still require maintainer approval, so the local results are not being presented as a substitute for CI.
    Could you review whether this TaskToolSet ownership and lifecycle contract matches the intended direction for feat(delegate): Non-blocking background subagent execution #2047? If it does, please enable the required fork workflows and merge the PR once CI and review are complete. This also unblocks the background-agent item tracked in feat(delegation): Advanced Features for Markdown-based Agents #2186.

@all-hands-bot

Copy link
Copy Markdown
Collaborator

🚦 CI is currently failing on this PR's latest commit.

Please fix the failing checks before OpenHands reviews it - this is re-checked automatically once you push a new commit. (A maintainer can also request @all-hands-bot as a reviewer to have it reviewed regardless of CI status.)

This is an automated check - no AI was used to generate this comment.

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.

2 participants