Skip to main content

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.