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

# Orchestration

> Compose agents with other agents and skills

orchagent supports two types of composition: **skill composition** (inject knowledge into prompts) and **agent composition** (call other agents at runtime).

<Tip>
  **Want a step-by-step tutorial?** See [Building an Orchestrator](/building-agents/building-orchestrators) for a hands-on guide to building your first orchestrator agent, including complete file examples, workspace secrets, and timeout budgeting.
</Tip>

## Skill Composition

Skills are passive knowledge that can be injected into agents. Skills work for all agent execution patterns:

* **For direct LLM and managed loop agents:** skill content is prepended to the agent's prompt before the LLM call
* **For tool types:** skills are mounted as files at `$ORCHAGENT_SKILLS_DIR` (see [Agent Types](/building-agents/agent-types#skills-in-tool-types))

### Using default\_skills

Agents can declare skills they always use:

```json theme={null}
{
  "name": "code-reviewer",
  "type": "prompt",
  "default_skills": [
    "yourorg/react-best-practices",
    "yourorg/writing-style"
  ],
  "prompt_template": "Review this code:\n\n{{code}}"
}
```

When called, the agent's prompt becomes: `[skill 1 content] + [skill 2 content] + [agent prompt]`

### Caller Override

Callers can modify skill behavior (unless the author has locked skills):

```bash theme={null}
# Add more skills to agent's defaults
orch run yourorg/code-reviewer --skills yourorg/security-rules

# Replace default_skills entirely
orch run yourorg/code-reviewer --skills-only yourorg/security-rules

# Ignore all skills
orch run yourorg/code-reviewer --no-skills
```

### Locking Skills

Authors can lock skills to prevent caller overrides. This is useful for agents where specific skills are critical for correct behavior (security rules, compliance guidelines, etc.).

```json theme={null}
{
  "name": "secure-reviewer",
  "type": "prompt",
  "default_skills": ["yourorg/owasp-rules", "yourorg/security-guidelines"],
  "skills_locked": true
}
```

When `skills_locked: true`:

* **Cloud (`orch run`)**: Caller override flags (`--skills`, `--skills-only`, `--no-skills`) are silently ignored
* **Local (`orch run --local`)**: CLI shows a warning and asks for confirmation before overriding

<Note>
  `skills_locked` is immutable after publish. To change it, publish a new version.
</Note>

### Skills vs Agent Dependencies

|                            | Skills                                | Agent Dependencies                  |
| -------------------------- | ------------------------------------- | ----------------------------------- |
| **What they are**          | Knowledge injected into prompt        | Agents called at runtime            |
| **When evaluated**         | Before LLM call                       | During execution                    |
| **Declared in**            | `default_skills`                      | `manifest.dependencies`             |
| **Can be locked**          | Yes (`skills_locked: true`)           | Always locked                       |
| **Override when unlocked** | Yes (--skills flags)                  | No (never)                          |
| **Best for**               | Style guides, rules, domain knowledge | Complex workflows, multi-step tasks |

***

## Agent Composition

Orchestrator agents call other agents to build complex workflows.

### Concepts

| Term                   | Definition                                                                     |
| ---------------------- | ------------------------------------------------------------------------------ |
| **Orchestrator agent** | An agent that calls other agents. Declares dependencies in its manifest.       |
| **Leaf agent**         | An agent with no dependencies. Does one focused task.                          |
| **Dependency**         | An agent that an orchestrator is allowed to call. Must be declared explicitly. |

### How It Works

```
┌─────────────────────────────────────────────────────────┐
│                      Caller                              │
│                        │                                 │
│                        ▼                                 │
│              ┌─────────────────┐                        │
│              │ security-review │  (orchestrator)        │
│              └────────┬────────┘                        │
│                       │                                 │
│         ┌─────────────┼─────────────┐                  │
│         ▼             ▼             ▼                  │
│  ┌────────────┐ ┌────────────┐ ┌────────────┐         │
│  │leak-finder │ │vuln-scanner│ │license-chk │ (leaves)│
│  └────────────┘ └────────────┘ └────────────┘         │
└─────────────────────────────────────────────────────────┘
```

**Key points:**

* Gateway handles all routing, auth, and limits
* Caller's rate limit: 1 call (top-level only)
* Sub-agent calls: validated against manifest limits and hop rules
* Call chains are tracked to prevent cycles
* Timeouts propagate through the chain

## Creating an Orchestrator

### Step 1: Declare Dependencies

Add a `manifest` section to your `orchagent.json`:

```json theme={null}
{
  "name": "security-review",
  "type": "tool",
  "description": "Comprehensive security review combining multiple scanners",
  "runtime": {
    "command": "python main.py"
  },
  "manifest": {
    "manifest_version": 1,
    "dependencies": [
      { "id": "joe/leak-finder", "version": "v1" },
      { "id": "joe/vuln-scanner", "version": "v1" }
    ],
    "max_hops": 2,
    "timeout_ms": 120000,
    "per_call_downstream_cap": 100
  }
}
```

### Step 2: Use the SDK

The orchagent SDK is available for both **Python** and **JavaScript/TypeScript**. Use whichever matches your agent's language.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    import asyncio, json, sys
    from orchagent import AgentClient

    async def main():
        input_data = json.loads(sys.stdin.read())
        client = AgentClient()  # Reads service key + context from env vars

        # Call dependencies
        secrets = await client.call("joe/leak-finder@v1", {"url": input_data["repo_url"]})
        vulns = await client.call("joe/vuln-scanner@v1", {"url": input_data["repo_url"]})

        print(json.dumps({
            "secrets": secrets,
            "vulnerabilities": vulns,
            "summary": f"Found {len(secrets)} secrets, {len(vulns)} vulnerabilities",
        }))

    asyncio.run(main())
    ```

    Install: `pip install orchagent-sdk`
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    const { AgentClient } = require('orchagent-sdk');
    const fs = require('fs');

    async function main() {
      const input = JSON.parse(fs.readFileSync('/dev/stdin', 'utf-8'));
      const client = new AgentClient();

      // Call dependencies
      const secrets = await client.call('joe/leak-finder@v1', { url: input.repo_url });
      const vulns = await client.call('joe/vuln-scanner@v1', { url: input.repo_url });

      console.log(JSON.stringify({
        secrets,
        vulnerabilities: vulns,
        summary: `Found ${secrets.length} secrets, ${vulns.length} vulnerabilities`,
      }));
    }

    main();
    ```

    Install: `npm install orchagent-sdk`
  </Tab>
</Tabs>

**The SDK handles automatically:**

* Service key authentication
* Call chain propagation
* Deadline propagation
* Max hops decrement
* Error wrapping

<Note>
  Most orchestrators use stdin/stdout with `AgentClient()`. If you're building a custom FastAPI server instead, use `AgentClient.from_request(request)` to propagate call chain and deadline from HTTP headers. See the [SDK Documentation](/building-agents/sdk) for details.
</Note>

<Tip>For complete SDK reference including error handling and environment variables, see the [SDK Documentation](/building-agents/sdk).</Tip>

### Step 3: Set Environment Variables

```bash theme={null}
export ORCHAGENT_SERVICE_KEY="sk_agent_..."
export ORCHAGENT_GATEWAY_URL="https://api.orchagent.io"
```

### Step 4: Publish

```bash theme={null}
orch publish
```

The gateway validates:

* All declared dependencies exist
* No cycles in dependency graph
* `max_hops >= 1` when dependencies declared

## Calling Dependencies

### Basic Call

```python theme={null}
result = await client.call("org/agent@version", {"input": "data"})
```

### With Custom Endpoint

```python theme={null}
result = await client.call("org/agent@v1", {"url": repo}, endpoint="deep-scan")
```

### With Timeout Override

```python theme={null}
result = await client.call("org/agent@v1", {"url": repo}, timeout=30.0)
```

### Error Handling

```python theme={null}
from orchagent import (
    DependencyCallError,
    TimeoutExceededError,
    CallChainCycleError,
    LocalExecutionError,
)

try:
    result = await client.call("org/agent@v1", input_data)
except DependencyCallError as e:
    print(f"Agent returned error: {e.status_code}")
    print(f"Response: {e.response_body}")
except TimeoutExceededError:
    print("Deadline exceeded")
except CallChainCycleError:
    print("Cycle detected in call chain")
except LocalExecutionError as e:
    print(f"Local execution failed: exit {e.exit_code}")
```

## Common Patterns

orchagent provides templates to scaffold these patterns instantly:

```bash theme={null}
orch init my-agent --template fan-out     # Parallel calls
orch init my-agent --template pipeline    # Sequential calls
orch init my-agent --template map-reduce  # Split, process, aggregate
```

### Serial Calls (Pipeline)

Call agents one after another, feeding each step's output into the next:

```python theme={null}
step1 = await client.call("org/parser@v1", {"file": input.file})
step2 = await client.call("org/analyzer@v1", {"parsed": step1})
step3 = await client.call("org/reporter@v1", {"analysis": step2})
return step3
```

### Parallel Calls (Fan-out)

Call multiple agents concurrently:

```python theme={null}
import asyncio

secrets, vulns, licenses = await asyncio.gather(
    client.call("joe/leak-finder@v1", {"url": repo}),
    client.call("joe/vuln-scanner@v1", {"url": repo}),
    client.call("joe/license-checker@v1", {"url": repo}),
)

return {"secrets": secrets, "vulns": vulns, "licenses": licenses}
```

### Map-Reduce

Split input into items, process each in parallel, aggregate:

```python theme={null}
import asyncio

# Map: process each item in parallel
mapped = await asyncio.gather(
    *[client.call("org/processor@v1", {"item": item}) for item in items]
)

# Reduce: aggregate results
reduced = await client.call("org/aggregator@v1", {"results": mapped})
return reduced
```

### Conditional Calls

Call based on previous results:

```python theme={null}
scan = await client.call("org/quick-scan@v1", {"url": repo})

if scan["risk_level"] == "high":
    # Only do deep scan if quick scan found issues
    deep = await client.call("org/deep-scan@v1", {"url": repo})
    return {"quick": scan, "deep": deep}

return {"quick": scan}
```

## Local Development

When you run an orchestrator locally with `orch run`, the CLI detects dependencies:

```
$ orch run joe/security-review .

Checking dependencies...

This agent has dependencies:
  joe/leak-finder@v1 (downloadable)
  joe/vuln-scanner@v1 (downloadable)
  acme/premium-scan@v1 (cloud-only)

Options:
  [1] Run on cloud (orch run) - recommended
  [2] Download 2 available deps, run locally
  [3] Cancel

Choose [1/2/3]:
```

**Auto-download dependencies:**

```bash theme={null}
orch run joe/security-review --with-deps .
```

<Note>Local mode requires the [orchagent-sdk](/building-agents/sdk) package to be installed for dependency calls to work correctly.</Note>

## Rate Limits

| What            | Limit Applied                        |
| --------------- | ------------------------------------ |
| Top-level call  | Caller's 1000/day limit              |
| Sub-agent calls | Orchestrator's manifest + hop limits |
| Nested calls    | Same orchestrator's limits           |

**Example:** User calls `security-review` which calls 3 agents:

* User's daily count: **+1** (just the top-level)
* `security-review`'s downstream calls: **+3** (no billing)

## Troubleshooting

### DEPENDENCY\_NOT\_ALLOWED

Your agent tried to call an agent not in its manifest.

**Fix:** Add the dependency to your manifest's `dependencies` array.

### MAX\_HOPS\_EXCEEDED

Call chain is too deep.

**Fix:** Increase `max_hops` in manifest, or refactor to reduce nesting.

### MAX\_HOPS\_TOO\_LOW

Caller's `max_hops` is lower than your agent's `min_required_hops`.

**Fix:** Either lower your `min_required_hops` or document that callers need higher limits.

### DEPENDENCY\_CYCLE

A→B→C→A detected.

**Fix:** Restructure your agents to avoid circular dependencies.

### STRICT\_MODE\_BASH\_DISABLED

Your orchestrator tried to use `bash` but is running in strict mode.

**Fix:** Use your custom tools (dependency agents) instead. If you need bash, set `orchestration_mode: "flexible"` in your manifest and republish.

### STRICT\_MODE\_DEPENDENCY\_REQUIRED

Your orchestrator tried to submit results without calling any dependency.

**Fix:** Call at least one custom tool before submitting. In strict mode, the agent must delegate to its declared dependencies.

### TIMEOUT

Request exceeded deadline.

**Fix:** Increase `timeout_ms` or optimize your agent's logic.

## Strict vs Flexible Mode

Managed-loop orchestrators with dependencies run in one of two orchestration modes: **strict** or **flexible**. This controls whether the agent must delegate to its declared dependencies or can solve tasks directly with built-in tools.

### Strict Mode (Default)

Orchestrators with dependencies default to strict mode. In strict mode:

* **`bash` is not available** — removed from the tool list entirely
* **Dependency calls are required** — the agent must call at least one custom tool (dependency) before submitting results
* Built-in file tools (`read_file`, `write_file`, `list_files`) remain available

This ensures the declared dependency graph matches runtime behavior — if you declare dependencies, the platform enforces that they're actually used.

### Flexible Mode

Use flexible mode when your orchestrator needs the freedom to solve some tasks directly:

```json theme={null}
{
  "manifest": {
    "orchestration_mode": "flexible",
    "dependencies": [{ "id": "yourorg/scanner", "version": "v1" }]
  }
}
```

In flexible mode, all built-in tools (including `bash`) remain available. The agent can choose to use dependencies or solve tasks directly. Dependency allowlist enforcement still applies — the agent can only call agents listed in `dependencies`.

### When to Use Each

| Use strict when...                                      | Use flexible when...                                      |
| ------------------------------------------------------- | --------------------------------------------------------- |
| Your agent exists to coordinate other agents            | Your agent sometimes needs bash for lightweight tasks     |
| You want predictable dependency-path execution          | Dependencies are optional enhancements, not core workflow |
| You need the runtime DAG to match declared dependencies | You're prototyping and want maximum freedom               |

### Chain Inheritance

In multi-agent chains, strict mode propagates **one-way downward**:

```
strict parent → strict child   (always — child cannot downgrade)
flexible parent → child's own mode applies
```

If a strict orchestrator calls a child that is configured as flexible, that child **runs as strict** for that execution. A child can never downgrade to flexible when called from a strict parent.

This means a top-level strict orchestrator guarantees dependency-path execution through the entire chain.

### Error Codes

If your agent violates strict mode rules, you'll see these errors in the agent's output:

| Error                             | Cause                                                        | Fix                                                                              |
| --------------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------- |
| `STRICT_MODE_BASH_DISABLED`       | Agent attempted to use `bash` in strict mode                 | Use custom tools (dependencies) instead, or set `orchestration_mode: "flexible"` |
| `STRICT_MODE_DEPENDENCY_REQUIRED` | Agent tried to submit results without calling any dependency | Call at least one custom tool before submitting                                  |

### Checking Your Mode

When you publish, the CLI echoes the effective orchestration mode:

```bash theme={null}
$ orch publish
Published yourorg/security-review@v3
  orchestration_mode: strict (defaulted — set "flexible" to opt out)
```

To use flexible mode, set `orchestration_mode: "flexible"` in your manifest and republish.

***

## Best Practices

1. **Keep agents focused** - Each agent does one thing well
2. **Declare all deps** - Undeclared calls are blocked
3. **Pin versions** - Use exact versions (`v1`), not `latest`
4. **Handle errors** - Wrap calls in try/except
5. **Set reasonable timeouts** - Account for all dependency calls
6. **Use parallel calls** - Fan-out when deps are independent
7. **Test locally first** - Use `orch run --with-deps` during development

## Next Steps

<CardGroup cols={3}>
  <Card title="Build an Orchestrator" icon="hammer" href="/building-agents/building-orchestrators">
    Step-by-step tutorial with complete examples
  </Card>

  <Card title="SDK Reference" icon="code" href="/building-agents/sdk">
    Complete API reference for the orchagent-sdk package
  </Card>

  <Card title="Agent Types" icon="shapes" href="/building-agents/agent-types">
    Learn about different agent types and when to use them
  </Card>
</CardGroup>
