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.
The service resolves its parent from the current agent turn, so it works in
CLI, gateway, non-interactive, and kanban-worker sessions. Launching outside an
active agent turn fails closed with No active Hermes parent session.
from agent.subagent_lifecycle import SubagentLaunchRequest
def launch_review(ctx):
# Call from a plugin tool or hook while an agent turn is active.
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. Child construction and
completion use the same host-owned path as delegate_task, including parent
tool-resolution restoration, memory notification, serialized subagent_stop
hooks, resource cleanup, and child-cost rollup. 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.