> ## Documentation Index
> Fetch the complete documentation index at: https://docs.profy.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# Subagents and Delegation

> Intra-Expert subagents and the three settings that decide cross-Expert delegation — including allowlist resolution and the specs that get silently dropped

There are two entirely different "division of labor" mechanisms, configured in different places:

* **Subagents** — division of labor **inside your Expert**. You declare sub-roles, each with its own system prompt and tool allowlist, and the main agent dispatches subtasks to them.
* **Delegation** — **cross-Expert** collaboration. Another Expert the user owns comes to yours for help, or the other way around.

The two do not connect: subagents cannot obtain cross-Expert capability. That is a hard boundary, and this page shows exactly how it is enforced.

<Note>
  Verified 2026-08-11. Sources listed at the bottom of this page.
</Note>

***

## Subagents

### Spec shape

Each subagent is an object with five fields:

```json theme={null}
{
  "name": "contract_scanner",
  "description": "Scan the full contract and locate every clause involving amounts, deadlines, or liability for breach",
  "system_prompt": "You only locate and extract; you do not judge risk. Output a JSON array where each item has clause_type / original_text / location.",
  "tools": ["read", "grep", "glob"],
  "model": "claude-sonnet-4"
}
```

| Field           | Required | Notes                                                                     |
| --------------- | -------- | ------------------------------------------------------------------------- |
| `name`          | Yes      | Also the argument the main agent uses to call it; must be identifier-like |
| `description`   | Yes      | Tells the main agent *when* to dispatch here                              |
| `system_prompt` | Yes      | The subagent's own system prompt                                          |
| `tools`         | No       | Tool allowlist, supports globs; omitted means empty list                  |
| `model`         | No       | Pin a model; omitted means it inherits the main agent's default           |

`model` gives you **model heterogeneity**: run scanning subagents on a cheap fast model and judgment subagents on a strong one.

### Three-pass allowlist resolution

You write tool names as text; the runtime resolves them into real tool objects in three fixed passes.

**Pass 1 — reject L1-reserved names.** These six are never granted to a subagent under any circumstance:

| Reserved name                              | Why                                                                                                 |
| ------------------------------------------ | --------------------------------------------------------------------------------------------------- |
| `delegate` / `Delegate`                    | Cross-Expert delegation; only the main agent may initiate                                           |
| `Task` / `task`                            | Top-level dispatcher; subagents cannot spawn nested subagents                                       |
| `plan_write`                               | The plan is session-scoped authority; a subagent writing it creates "whose plan is this?" ambiguity |
| `suggest_plan_mode` / `suggest_build_mode` | Mode transitions are session-scoped authority too                                                   |

This list is a **hardcoded set of names**, not a lookup against the tool registry. The reason is concrete: with a registry lookup, an L1 tool that is not implemented yet would not be found and the guard would silently lapse. If a name is on the list it is reserved, whether or not it exists.

**Pass 2 — expand globs.** `*` and prefix patterns like `mcp__server__*` are matched against the main agent's toolset.

**Pass 3 — exact-match the rest; anything unresolvable is dropped.** The runtime **never invents tools**. A name that does not exist is silently dropped (recorded in the build report), not raised as an error.

Glob results are re-checked against the reserved list — writing `"tools": ["*"]` does not get you `delegate`; the second line of defense catches it.

### Specs that get dropped

In any of these cases your subagent **does not exist at runtime**, and nothing errors:

| Case                                                         | Outcome                          |
| ------------------------------------------------------------ | -------------------------------- |
| Not an object                                                | Dropped                          |
| `name` / `description` / `system_prompt` empty or non-string | Dropped                          |
| `name` contains whitespace or `"` `<` `>` `&` `'`            | Dropped                          |
| `name` collides with an L1-reserved name                     | Dropped                          |
| `tools` present but not an array                             | Dropped                          |
| **Zero tools resolve after the allowlist pass**              | Dropped                          |
| Duplicate name                                               | Dropped (first declaration wins) |

The last two catch people out most often:

* **Zero tools means dropped**, by design: "a subagent with no tools is a pure reasoner that duplicates the general-purpose agent." So if you want a pure-thinking subagent, give it at least one resolvable tool.
* **First-wins on duplicates**, and platform builtin subagents are prepended ahead of yours. A name collision with a builtin drops yours.

<Warning>
  All of these drops are silent — publishing does not fail, the subagent simply is not there. The symptom is "the main agent never dispatches to it." Start debugging by confirming that at least one name in `tools` resolves against the main agent's toolset.
</Warning>

***

## Cross-Expert delegation

Delegation is governed by three settings plus one access-control layer.

### `delegatable` (boolean, default true)

Whether your Expert **can be delegated to** by others. On by default.

It is checked twice: once when building the roster (non-delegatable Experts never enter it), and again when a delegation request actually arrives (defense in depth). The second check returns:

```
Expert is not delegatable
```

<Note>
  Note the HTTP status is 200 — the business result is in the body as `status: "failed"` plus an `error` field. Delegation failure is modeled as a *task result*, not a request error, because the caller is a model rather than a person.
</Note>

### `delegationBrief` (text, nullable)

A capability brief written **for other Experts' models**. It determines whether another Expert thinks to come to you.

The fallback is expressed in SQL:

```sql theme={null}
coalesce(delegation_brief, description, '')
```

Leave it empty and `description` is used instead. But the two have different audiences:

* `description` is written for **humans** — it appears on the marketplace card and should be compelling
* `delegationBrief` is written for **models** — it must state what input you take and what you produce

Contrast:

```
description (for humans)
  Thirty years in accounting. I'll untangle your books and make tax season painless.

delegationBrief (for models)
  Handles bookkeeping and tax questions for small businesses in mainland China.
  Accepts: bank statements, scanned invoices, prior-period ledgers, specific tax
  policy questions.
  Produces: categorized ledgers, tax risk checklists, filing guidance.
  Does not accept: audit opinions, cross-border tax, listed-company compliance.
```

The second markedly improves correct-delegation rate and cuts wasted calls from misrouted work.

### Roster injection policy

Which Experts the main Expert can see is set by the user's delegation policy:

| Policy                | Roster injected                                 |
| --------------------- | ----------------------------------------------- |
| `none`                | Empty                                           |
| `all`                 | Every delegatable Expert the user has access to |
| Whitelist (non-empty) | The full whitelist, capped at **10**            |

Regardless of policy, any Expert the user `@`-mentions in the message is **merged into the roster additionally**, under the same safety conditions.

### Safety conditions (all four required)

To enter the roster, an Expert must satisfy all of:

```ts theme={null}
eq(expertAccess.userId, userId),              // the user has access (purchased or created)
eq(expert.delegatable, true),                 // the creator allows delegation
eq(expert.status, EXPERT_STATUS.PUBLISHED),   // it is live
isNull(expert.deletedAt),                     // not deleted
```

Plus one more: **the current Expert is excluded** — no self-delegation.

The first condition is the important one. Delegation grants the user no additional privilege: an Expert the user has not purchased cannot be reached by delegation either.

***

## The boundary between the two

Subagents cannot obtain the `delegate` tool, and this is enforced at two points: an explicit `delegate` entry is rejected in pass 1, and a `*` glob is caught in pass 2.

The intent is that **privilege must not escape through nesting**. If subagents could delegate, "the user authorized Expert A" would silently become "A's subagents may also invoke B", and the authorization boundary would stop being something you can reason about.

***

## Limits and failure modes

<AccordionGroup>
  <Accordion title="I configured a subagent but the main agent never uses it">
    Check three things in order: (1) whether the spec was dropped — most commonly every entry in `tools` failed to resolve, triggering the zero-tools drop; (2) whether `description` states the applicable situation clearly — the main agent uses it to decide whether to dispatch, and vague text means it never gets picked; (3) whether a name collision with a builtin dropped yours under first-wins.
  </Accordion>

  <Accordion title="Subagent reports a tool is unavailable">
    Names in the allowlist must exist in the **main agent's** toolset. You cannot grant a subagent a tool the main agent does not have — resolution is an intersection against the parent toolset. Confirm the relevant plugin is enabled on this Expert.
  </Accordion>

  <Accordion title="Another Expert gets 'Expert is not delegatable'">
    Your `delegatable` flag is off. Turn it on in Studio. Remember the HTTP status is 200 — do not debug this as a 4xx.
  </Accordion>

  <Accordion title="Delegation returns 'Expert not found or access denied'">
    The **user** initiating delegation has no access to the target Expert. Delegation does not elevate privilege; the user must purchase it first.
  </Accordion>

  <Accordion title="I whitelisted 15 Experts but only 10 work">
    Ten is the cap (`DELEGATION_WHITELIST_MAX`). The overflow is dropped and logged.
  </Accordion>
</AccordionGroup>

***

## Verify

**Subagents**: give a task that clearly belongs to one specific subagent and look for the corresponding subtask call in the conversation's execution trace. If it never appears, the spec was dropped or the `description` is too vague.

**Delegation**: using an account that owns **both** Experts, ask Expert A something that obviously belongs to Expert B's domain and see whether A delegates. For a precise test, `@`-mention Expert B directly — mentions bypass policy and force a roster merge, which separates "policy problem" from "badly written brief".

***

## Sources

<Note>
  Verified 2026-08-11. Sources:

  * Three-pass resolution, L1 reserved list, drop rules: `services/agent-runtime/src/harness/subagent/builder.py`
  * Roster safety conditions, `coalesce` fallback, whitelist cap of 10: `services/core/src/db/service/delegation.ts`, `packages/db/src/schema/config.ts`
  * Delegation request validation and error response shape: `services/core/src/routes/agent-proxy/delegate.ts`
  * Field type definitions: `packages/db/src/schema/marketplace.ts`
</Note>

***

## Next

<CardGroup cols={2}>
  <Card title="Prompt Layers" icon="layer-group" href="/en/creators/expert-config/prompt-layers">
    Where each of the four fields is injected
  </Card>

  <Card title="Case Demos and Workspace Seed" icon="folder-open" href="/en/creators/expert-config/workspace-and-demos">
    Case demo configuration and workspace seed status
  </Card>
</CardGroup>
