June 28, 2026 — Matteo Merola

Subagent Patterns: Scoped Delegation for Complex Tasks

How to spawn scoped sub-tasks from an agent with inherit-never-widen security, budget controls, and sync or background execution.

An agent that can call tools is useful. An agent that can spawn other agents is powerful — and dangerous. The moment you let an agent delegate work to a sub-loop, you've introduced a recursion that can burn through your API budget, access systems it shouldn't, or run indefinitely. The design challenge isn't "how do we spawn subagents" — it's "how do we make spawning safe by default."

This article covers the subagent model in humux: the inherit-never-widen security principle, the two execution modes (sync and background), the budget system that caps runaway loops, and the practical patterns that make delegation useful without making it scary.

Inherit, never widen

The core security principle for subagents is simple: a child can inherit permissions from its parent, but it can never exceed them. If the parent agent has access to tools A, B, and C, the subagent can be given A and B, or just A, or none — but never D. Same for skills, secrets, and GitHub repo access.

This is enforced at spawn time, not at execution time. When a parent agent calls spawn_subagent, the system validates that every requested tool, skill, and secret is in the parent's own scope. If the request asks for something the parent doesn't have, the spawn fails with an error — not silently, not with a degraded scope.

spawn example
# Parent agent (Atlas) has: email, calendar, contacts, scheduling
# This spawn succeeds — email is in the parent's scope
spawn_subagent(
    task="Search for all emails from acme.com in the last week "
         "and summarize the key action items",
    tools=["search_email"],
    skills=["email"],
    max_steps=10,
    max_tokens=50000,
)

# This spawn fails — coding tools are not in Atlas's scope
spawn_subagent(
    task="Fix the bug in api/routes.py",
    tools=["read_file", "edit_file"],  # NOT in parent scope
    skills=["coding"],                  # NOT in parent scope
)

The inherit-never-widen rule makes the trust model composable. You configure trust at the agent level (Atlas can read email but not write code), and every subagent Atlas spawns automatically respects that boundary. You never need to audit what a subagent might do — it's bounded by what its parent can do, which is bounded by what you configured.

Sync vs. background execution

Not every delegated task needs the same execution model. Some tasks are quick lookups: "check the calendar for tomorrow's meetings." Others are long-running research: "read through the last 50 emails and find everything related to the budget review." Forcing both through the same pipe is wasteful.

humux supports two modes:

Sync mode blocks the parent until the subagent completes. The result is returned directly into the parent's tool-use loop, as if it were a tool call result. Use this for tasks where the parent needs the output to continue its reasoning: "look up X so I can decide Y."

Background mode returns immediately with a task ID. The subagent runs independently, and when it completes, a distilled summary is delivered to the originating chat. The parent is free to continue its own conversation. Use this for tasks that take minutes and don't block the current interaction: "compile a weekly report and send it to me when it's done."

execution modes
# Sync: parent waits, gets result inline
result = spawn_subagent(
    task="What meetings do I have tomorrow?",
    tools=["list_events"],
    mode="sync",
    max_steps=5,
)
# result is immediately available for the parent to reason about

# Background: parent continues, result delivered later
spawn_subagent(
    task="Read all emails from this week, group by project, "
         "and write a summary with action items",
    tools=["search_email"],
    skills=["email"],
    mode="background",
    max_steps=30,
    max_tokens=100000,
)
# Returns task_id immediately; summary appears in chat when done

Budget controls

The most important safety mechanism for subagents isn't permission scoping — it's budget limits. Without them, a subagent can enter an infinite tool-use loop and burn through your entire LLM API credit before you notice. humux enforces three types of budgets:

When any budget is exhausted, the subagent is asked to produce a final response with whatever it has so far. It doesn't crash — it wraps up. The parent (or the user) gets a result, even if it's partial. The budget enforcement happens at the orchestrator level, not inside the model — the model can't decide to ignore its budget.

Practical delegation patterns

After running subagents in production, a few patterns have emerged as consistently useful:

Research-then-decide. The parent spawns a sync subagent to gather information, waits for the result, then makes a decision based on what it found. "Check my calendar for Thursday availability, then draft a reply to this meeting request." The subagent handles the mechanical lookup; the parent handles the judgment call.

Parallel investigation. The parent spawns multiple background subagents, each searching a different source: one reads email, another checks the calendar, a third searches the web. The results arrive independently and the parent synthesizes them. This is useful for morning briefings: "what happened overnight?" triggers parallel lookups across all channels.

Narrow-scope specialist. Instead of giving the main agent access to a risky tool, give it the ability to spawn a subagent that has that tool with a tight step budget. The parent can ask the subagent to "run this one command and report the output" without having persistent access to the shell. The subagent's scope is narrowed for one job and then it's done.

Monitoring and cancellation

Running subagents need visibility. In humux, active subagents appear in the admin UI's job manager and can be monitored from Telegram. Each subagent inherits its parent's log stream with a [subagent:] prefix, so you can see exactly what the child is doing in the parent's logs.

Cancellation works from both the admin UI and Telegram. If a background subagent is taking too long or heading in the wrong direction, you cancel it and the parent is notified. No zombie processes, no orphaned API calls. The orchestrator cleans up the subagent's state and delivers a "cancelled" status to the originating chat.

The design constraint

The temptation with subagents is to build an agent that spawns agents that spawn agents — a recursive tree of delegation. In practice, one level of delegation covers every use case we've encountered. The parent has context and judgment; the child has focus and a bounded scope. Going deeper adds complexity without adding capability, because the information still has to flow back up through each layer.

The right mental model for subagents isn't "autonomous agents collaborating" — it's "one agent with the ability to run focused side-quests." The parent stays in control. The children do the work. The budgets ensure nobody runs away with the credit card.

humux supports subagent delegation with inherit-never-widen scoping, budget controls, and Telegram monitoring. Try it →