feat(plugins): add public subagent lifecycle API

This commit is contained in:
Tony Simons 2026-07-12 13:49:45 -05:00 committed by Teknium
parent 0a2c245cd6
commit 1865fb5fcd
4 changed files with 650 additions and 0 deletions

View file

@ -0,0 +1,54 @@
---
title: Public Subagent Lifecycle API
sidebar_label: Subagent lifecycle API
---
# Public Subagent Lifecycle API
Plugins can launch and supervise fresh Hermes child sessions without importing
`tools.delegate_tool`, gateway internals, TUI state, or `AIAgent` fields.
```python
from agent.subagent_lifecycle import SubagentLaunchRequest
def register(ctx):
service = ctx.subagent_lifecycle
handle = service.launch(SubagentLaunchRequest(
goal="Review this change for regressions.",
context="Only inspect the supplied repository.",
role="leaf",
correlation_id="review-42",
allowed_toolsets=("file",),
))
# Persist handle.to_dict() if desired.
if service.wait(handle, timeout_seconds=2).timed_out:
return handle.to_dict()
return service.result(handle)
```
`SubagentHandle` is serializable and carries a versioned, opaque capability.
Pass it back to `status`, `wait`, `cancel`, `result`, or `reconnect`; malformed
or forged handles return `UNKNOWN`/`UNKNOWN_HANDLE` and cannot access a child.
The stable states are `PENDING`, `STARTING`, `RUNNING`, `SUCCEEDED`, `FAILED`,
`INTERRUPTED`, `CANCEL_REQUESTED`, `CANCELLED`, and `UNKNOWN`.
`cancel(handle, reason=...)` is cooperative: it asks the child agent to
interrupt at its next safe boundary and returns `CANCEL_REQUESTED`; it never
claims completion until `wait` or `result` observes a terminal state. Terminal
results are immutable, idempotent, bounded to 32k characters, omit transcripts
and hidden reasoning, and include a stable result hash.
This API is lifecycle-managed asynchronous execution. It does not change the
synchronous `delegate_task` tool, batch delegation, or its gateway/TUI display.
The initial implementation retains metadata and terminal results in-process for
one hour.
After a process restart, `reconnect` returns `RECONNECT_UNAVAILABLE` and never
starts a replacement child. Running Python threads also cannot survive process
exit; callers must treat those handles as interrupted by process exit.
Requests are fail-closed: goal/context/metadata sizes are capped, unknown or
parent-broadening toolsets are rejected, and per-tool blocks, working-directory
overrides, and per-launch timeouts are explicitly rejected until Hermes can
support them without weakening isolation. Use `allowed_toolsets` to narrow a
child; Hermes's existing unsafe-tool block remains enforced.