# Agent as Tool

URL: https://cogitator.app/docs/tools/agent-as-tool

> Wrap agents as tools with agentAsTool() to enable hierarchical delegation between agents.

## Overview

The `agentAsTool()` function wraps a Cogitator agent as a tool that other agents can call. This enables hierarchical delegation -- a manager agent can delegate subtasks to specialist agents, each with their own models, instructions, and tool sets.

```typescript
import { Cogitator, Agent, agentAsTool, webSearch, webScrape } from '@cogitator-ai/core';

const cog = new Cogitator({
  llm: {
    providers: {
      openai: { apiKey: process.env.OPENAI_API_KEY! },
    },
  },
});

const researcher = new Agent({
  name: 'researcher',
  model: 'openai/gpt-6.1-sol',
  instructions: 'You research topics thoroughly and return detailed findings.',
  tools: [webSearch, webScrape],
});

const writer = new Agent({
  name: 'writer',
  model: 'openai/gpt-6.1-sol',
  instructions: 'You write clear, well-structured articles from research notes.',
});

const manager = new Agent({
  name: 'manager',
  model: 'openai/gpt-6.1-sol',
  instructions:
    'You coordinate research and writing tasks. Delegate research to the researcher and writing to the writer.',
  tools: [
    agentAsTool(cog, researcher, {
      name: 'research',
      description: 'Delegate a research task. Provide a clear research question.',
    }),
    agentAsTool(cog, writer, {
      name: 'write_article',
      description: 'Delegate writing an article. Provide the research notes and topic.',
    }),
  ],
});

const result = await cog.run(manager, {
  input: 'Write a comprehensive article about WebAssembly in production',
});
```

***

## Function Signature

```typescript
function agentAsTool(
  cogitator: Cogitator,
  agent: Agent,
  options: AgentAsToolOptions
): Tool<{ task: string }, AgentToolResult>;
```

The returned tool has a single `task` parameter -- a string that becomes the input to the inner agent's `cog.run()` call. Each call is a separate run on a new thread: the inner agent sees only the task, not the parent's conversation, and keeps no memory between calls. The run inherits the parent call's abort signal and `userId`, so cancelling the parent run cancels the delegated one and thread ownership stays with the same user.

The tool declares `sideEffects: ['external']`.

***

## Options

```typescript
interface AgentAsToolOptions {
  name: string;
  description: string;
  timeout?: number;
  includeUsage?: boolean;
  includeToolCalls?: boolean;
  onApproval?: RunOptions['onApproval'];
}
```

| Field              | Type      | Required | Description                                         |
| ------------------ | --------- | -------- | --------------------------------------------------- |
| `name`             | `string`  | Yes      | Tool name visible to the parent agent               |
| `description`      | `string`  | Yes      | What this agent-tool does (shown to the LLM)        |
| `timeout`          | `number`  | No       | Override timeout for the inner agent run (ms)       |
| `includeUsage`     | `boolean` | No       | Include token usage and cost in the result          |
| `includeToolCalls` | `boolean` | No       | Include the list of tool calls the inner agent made |
| `onApproval`       | function  | No       | Decides inner tool calls that need approval         |

### Approvals Inside Delegated Runs

A delegated run cannot pause and wait for a person. Calls of the inner agent that need [approval](/docs/tools/approvals) are therefore declined by default (the inner agent is told "no one can approve calls of a delegated agent" and answers accordingly). Pass `onApproval` to decide them yourself:

```typescript
agentAsTool(cog, billingAgent, {
  name: 'billing',
  description: 'Handle refunds and invoices.',
  onApproval: async (call) =>
    call.toolName === 'refund' && Number(call.arguments.amount) <= 50
      ? { approved: true }
      : { approved: false, reason: 'refunds over $50 need a manager' },
});
```

Returning `'pause'` from this `onApproval` declines the call like the default. If the inner run pauses anyway, the tool reports `success: false` with an `error` naming the tools that waited for approval, never an empty success. To let a person approve delegated work, use [handoffs](/docs/core/agents#handoffs) instead: a handed-over run can pause and resume.

### Writing Good Descriptions

The `description` is what the parent agent reads to decide when to delegate. Be specific about what the wrapped agent specializes in and what kind of input it expects:

```typescript
agentAsTool(cog, sqlAgent, {
  name: 'query_database',
  description:
    'Run a natural language query against the database. Provide the question in plain English -- the agent will generate and execute the SQL.',
});
```

***

## Result Shape

The wrapped agent returns an `AgentToolResult`:

```typescript
interface AgentToolResult {
  output: string;
  success: boolean;
  error?: string;
  usage?: {
    inputTokens: number;
    outputTokens: number;
    totalTokens: number;
    cost: number;
    duration: number;
  };
  toolCalls?: Array<{ name: string; arguments: unknown }>;
}
```

On success, `output` contains the agent's final text response. On failure, `success` is `false` and `error` contains the error message. The parent agent sees this result and can decide how to proceed -- retry, try a different approach, or report the failure.

***

## Patterns

### Manager-Worker Hierarchy

One manager agent coordinates multiple specialist workers:

```typescript
const codeReviewer = new Agent({
  name: 'code-reviewer',
  model: 'openai/gpt-6.1-sol',
  instructions: 'Review code for bugs, security issues, and style problems.',
});

const testWriter = new Agent({
  name: 'test-writer',
  model: 'openai/gpt-6.1-sol',
  instructions: 'Write comprehensive unit tests for the provided code.',
  tools: [fileRead, fileWrite],
});

const leadDev = new Agent({
  name: 'lead-developer',
  model: 'openai/gpt-6.1-sol',
  instructions: 'You are a tech lead. Review code with the reviewer, then have tests written.',
  tools: [
    agentAsTool(cog, codeReviewer, {
      name: 'review_code',
      description: 'Get a code review. Send the code or file path to review.',
    }),
    agentAsTool(cog, testWriter, {
      name: 'write_tests',
      description: 'Generate unit tests for code. Send the code to test.',
    }),
  ],
});
```

### Routing by Capability

Use different models for different sub-agents based on task complexity:

```typescript
const cheapSummarizer = new Agent({
  name: 'summarizer',
  model: 'openai/gpt-6-luna',
  instructions: 'Summarize text concisely.',
});

const deepAnalyst = new Agent({
  name: 'analyst',
  model: 'anthropic/claude-sonnet-5-5',
  instructions: 'Perform deep analysis with nuanced reasoning.',
});

const router = new Agent({
  name: 'router',
  model: 'openai/gpt-6-luna',
  instructions:
    'Route tasks: use summarize for simple summaries, use analyze for complex analysis.',
  tools: [
    agentAsTool(cog, cheapSummarizer, {
      name: 'summarize',
      description: 'Quick text summarization. Use for straightforward content.',
    }),
    agentAsTool(cog, deepAnalyst, {
      name: 'analyze',
      description: 'Deep analysis with reasoning. Use for complex questions.',
      includeUsage: true,
    }),
  ],
});
```

### Tracking Inner Agent Usage

Enable `includeUsage` and `includeToolCalls` to give the parent agent visibility into what the sub-agent did:

```typescript
const researchTool = agentAsTool(cog, researcher, {
  name: 'research',
  description: 'Research a topic thoroughly',
  includeUsage: true,
  includeToolCalls: true,
  timeout: 120_000,
});
```

The parent agent receives:

```json
{
  "output": "WebAssembly (Wasm) is a binary instruction format...",
  "success": true,
  "usage": {
    "inputTokens": 2340,
    "outputTokens": 1856,
    "totalTokens": 4196,
    "cost": 0.042,
    "duration": 8500
  },
  "toolCalls": [
    { "name": "web_search", "arguments": { "query": "WebAssembly production use cases 2025" } },
    { "name": "web_scrape", "arguments": { "url": "https://..." } }
  ]
}
```

***

## Error Handling

`agentAsTool` catches all errors from the inner agent run and returns them as structured results instead of throwing. The parent agent always gets a response it can work with:

```typescript
// if the inner agent times out or fails
{
  "output": "",
  "success": false,
  "error": "Run timed out after 30000ms"
}
```

The parent agent sees `success: false` and can decide to retry with simpler instructions, try a different agent, or inform the user.

***

## Timeout Behavior

The timeout resolution follows this priority:

1. `timeout` in `AgentAsToolOptions` (if set) -- also used as the tool's own timeout
2. `timeout` in the inner agent's config
3. Cogitator runtime's `limits.defaultTimeout`, then 120 seconds

For long-running sub-agents (research, code generation), explicitly set a generous timeout:

```typescript
agentAsTool(cog, researcher, {
  name: 'deep_research',
  description: 'Extensive multi-source research',
  timeout: 300_000,
});
```
