# Delegation

Declare specialists in the constructor, delegate isolated tasks, and understand permissions and recovery.

Declare specialists when creating an orchestrator. HarnessX handles creating,
running, and closing the specialist for each task; you do not write or register
delegation wrappers.

```python
import asyncio
from harnessx import Agent, AgentConfig, Limits, SubAgent
from harnessx.builtin.web import fetch_url

async def main():
    async with Agent(
        config=AgentConfig(
            system_prompt="Delegate research when useful, then synthesize the findings.",
        ),
        subagents=[
            SubAgent(
                name="researcher",
                description="Read public URLs and summarize their evidence.",
                config=AgentConfig(
                    system_prompt="Read the supplied URLs and cite your sources.",
                    limits=Limits(max_iterations=5),
                ),
                tools=[fetch_url],
            ),
        ],
    ) as orchestrator:
        result = await orchestrator.run("Read https://example.com and summarize it.")
        result.raise_for_status()  # RunFailed, RunAwaitingInput, or RunCancelled otherwise
        print(result.output)

asyncio.run(main())
```

## Definition and routing

`SubAgent` is a definition, not a running agent. Required fields:

- `name`: 1–55 letters, digits, underscores, or hyphens, starting with a letter or underscore.
- `description`: tells the orchestrator when this specialist is useful.
- `config`: an explicit `AgentConfig`; its system prompt instructs the specialist.

Optional fields:

- `tools`: the same functions, built-in names, definitions, or registry formats accepted by `Agent`.
- `skills`: a list of skill paths, loaded separately for each task.
- `permission`: permission to delegate; omitted means inherit the parent's manager.
- `timeout_seconds`: whole-task timeout, default `DEFAULT_TIMEOUT_SECONDS` (300 seconds), including child model and tool calls.

The SDK exposes `delegate_<name>(task: str)` to the model internally. The schema
and description tell the orchestrator how to request a specialist. Do not also
register a tool with that name; collisions and duplicate specialist names fail
during construction. No separate orchestrator class is necessary.

The model decides whether to delegate. Naming specialists does not force a fixed
sequence; write workflow code when you need a mandatory sequence. Use
`examples/multi_agent.py` for research, code review, and scoped file analysis.

## Context, resources, and permissions

Each delegated task gets fresh memory, configuration, and a tool registry. It sees
only its own prompt and the assigned task. Include the code, question, URLs, paths,
or other context it needs in the task. The child's model/provider comes from its
explicit config; it does not inherit the parent's model or injected provider.

Handler functions remain shared application objects: separate conversations do
not isolate files, databases, closures, or arbitrary Python code. Child-owned
providers and memory close on completion, failure, and cancellation. Skills may
add their own tool and prompt catalog; do not also register a `Skill` tool.

The parent's permission manager is shared, including its defaults, name overrides,
session grants, and approval callback. Generated delegation permission is separate
from each child tool's permission. Approving delegation does not approve its tools.
The same tool-name override applies in parent and child. An ordinary manager defaults
to ALLOW; use explicit permissions or another manager to change that policy.

Parent tools, hooks, middleware, extensions, MCP connections, and sandboxes are not
automatically copied. The generated call uses the parent's normal tool pipeline;
the child uses its own ordinary engine. Use the supported manual-wrapper pattern
for advanced child dependencies and their lifecycle management. It remains useful
for a specialist with custom tracing extensions or an injected provider.

## Results, streaming, and limits

The child returns its completed output to the orchestrator, which continues and
synthesizes a response. There is no conversation handoff. Non-completed child
statuses and child failures become delegation errors; partial output is not
reported as successful specialist work.

Generated delegation tools are non-concurrent and use manual replay policy. The
current engine serializes a tool batch containing such a delegation. Both
`run` and `run_stream` support the same behavior. Streaming reports ordinary
`tool_call_start`, `tool_call_complete`, and `tool_result` events for delegation;
child text does not appear as parent answer text.

Each specialist has its own iteration and cost limits. Parent `RunResult.usage`
and guardrail totals remain parent-only, not a combined team budget. Set child
limits explicitly with `AgentConfig(limits=Limits(...))`. A timeout cancels
waiting and closes child resources, but
cannot reverse external effects or forcibly stop a synchronous tool thread.

## Durable execution boundary

`AgentRuntime` persists the outer delegation as an ordinary manual-replay tool
call. Committed results are reused on recovery. An interrupted delegation with an
unknown outcome stops for manual recovery rather than automatically starting a
new specialist. Live model reruns are not deterministic.

The child uses an ordinary, non-durable run. Child progress, internal approvals,
intermediate artifacts, and conversations are not independently persisted by the
parent runtime. A child ASK tool uses the approval callback and fails closed
without one; it does not create a resumable approval in the parent runtime.
Treat output referring to child-local artifacts accordingly; durable child-artifact
transfer is not supplied by this API.

Rebind the same `subagents` definitions when restoring sessions or constructing
versioned runtime factories, just as you rebind tools. Session snapshots do not
serialize Python handlers. Keep definition changes under the application's normal
versioned deployment controls.

Independent child recovery, persistent specialist conversations, nested declarative
teams, handoffs, and child-token streaming are outside this initial API.
