Skip to main content
Subagents are autonomous agents that run in their own context window. The Explore subagent searches your codebase using WarpGrep, decides what to search next based on results, and returns a structured summary. Your main agent’s context stays clean.

Why?

Codebase exploration is context-heavy. A single WarpGrep call returns relevant code, but understanding how a system works often takes 3-8 searches. The Explore subagent handles this loop autonomously on a cheap/fast model (Haiku), then returns only the summary to your primary agent. The subagent also supports pause-and-ask messaging: if it hits a fork in the road (“Found JWT and OAuth auth. Which should I focus on?”), it can ask your app and wait for a reply before continuing.

Quick Start

import Anthropic from '@anthropic-ai/sdk';
import { MorphClient } from '@morphllm/morphsdk';

const morph = new MorphClient({ apiKey: process.env.MORPH_API_KEY });
const anthropic = new Anthropic();

// Create the subagent
const explore = morph.anthropic.createExploreSubagent({
  client: anthropic,
  model: 'claude-haiku-4-5-20251001',
  repoRoot: '.',
});

// Run an exploration
const session = explore.run('How does the authentication system work?');

session.on('step', (step) => {
  console.log(`Step ${step.step}: searching "${step.searchRequest}"`);
});

const result = await session.result;
console.log(result.summary);   // Concise summary for your agent
console.log(result.contexts);  // Full code contexts for your app

Three Ways to Use

1. As a Tool in a Parent Agent

The subagent exposes a .tool property you can pass to any agent. The parent model calls it like any other tool, and the subagent runs its full search loop internally.
const explore = morph.anthropic.createExploreSubagent({
  client: anthropic,
  model: 'claude-haiku-4-5-20251001',
  repoRoot: '.',
});

// Use alongside other tools
const response = await anthropic.messages.create({
  model: 'claude-sonnet-4-5-20250929',
  max_tokens: 8000,
  tools: [explore.tool, editTool],
  messages: [{ role: 'user', content: 'Find and fix the auth bug' }]
});

// Execute when the model calls it
const toolUse = response.content.find(c => c.type === 'tool_use' && c.name === 'explore');
if (toolUse) {
  const result = await explore.tool.execute(toolUse.input);
  console.log(result.summary);
}

2. Direct Run with Messaging

Call .run() to start an exploration and listen for events. The subagent can pause and ask questions via send_message, and your app replies.
const session = explore.run('Explore the payment processing system');

// Listen for progress
session.on('step', (step) => {
  console.log(`Step ${step.step}: searching "${step.searchRequest}" -> ${step.contextsFound} files`);
});

// Handle pause-and-ask messages
session.on('message', (msg, reply) => {
  console.log(`Subagent asks: ${msg.content}`);
  // e.g. "Found Stripe and PayPal integrations. Which should I focus on?"
  reply('Focus on Stripe');
});

const result = await session.result;
// result.success, result.summary, result.contexts, result.searchCount, result.durationMs

3. Streaming

Use .stream() to get events as an async generator.
for await (const event of explore.stream('Find all API routes')) {
  if (event.type === 'step') {
    console.log(`Searching: ${event.searchRequest} -> ${event.contextsFound} files`);
  }
  if (event.type === 'message') {
    console.log(`Subagent: ${event.content}`);
  }
}

Messaging Protocol

The subagent has two internal tools: codebase_search (WarpGrep) and send_message. The system prompt instructs it to use send_message when it:
  • Hits a fork: “Found auth in both src/middleware/ and legacy/auth/. Should I focus on one or cover both?”
  • Needs clarification: “There are 3 auth strategies (JWT, session, OAuth). Which one?”
  • Has a key finding to share before continuing: “The main handler is in src/auth/index.ts. Continuing to trace the JWT flow.”
When send_message is called, the tool blocks until your app replies (or a timeout fires). The reply is injected back as the tool result, and the subagent continues with that context.
Your App                           Explore Subagent
  |                                      |
  |---- run("How does auth work?") ----->|
  |                                      |-- codebase_search("auth")
  |<-- step: searching "auth" ----------|-- WarpGrep returns results
  |                                      |-- model has a question
  |                                      |-- send_message("Found JWT and OAuth...")
  |<-- message: "Found JWT and..." -----|-- BLOCKS, waiting for reply
  |                                      |
  |---- reply("Focus on JWT") --------->|-- tool returns "Response: Focus on JWT"
  |                                      |-- codebase_search("JWT validation")
  |<-- step: searching "JWT..." --------|-- WarpGrep returns results
  |                                      |-- model has enough info
  |<-- result: ExploreResult ------------|

Configuration

const explore = morph.vercel.createExploreSubagent({
  model: anthropic('claude-haiku-4-5-20251001'),
  repoRoot: '.',
  thoroughness: 'medium',
  replyTimeout: 30000,
  excludes: ['dist', '*.test.ts'],
});
OptionDefaultDescription
model(required)Vercel AI SDK model instance, or model string for Anthropic
client(Anthropic only)Anthropic SDK client instance
repoRoot(required)Root directory of the repository to search
thoroughness'medium''quick' (1-2 searches), 'medium' (2-4), 'thorough' (4-8)
maxTurns(auto)Override the max model turns (defaults based on thoroughness)
timeout(none)Timeout in ms for the entire exploration
replyTimeout30000Timeout in ms for waiting for host reply to send_message
excludes(WarpGrep defaults)Glob patterns to exclude from search
includes(all files)Glob patterns to include in search

Result Shape

interface ExploreResult {
  success: boolean;        // Whether the exploration completed
  summary: string;         // Concise summary (for model consumption)
  contexts: WarpGrepContext[]; // Full code contexts (for your app)
  searchCount: number;     // Number of WarpGrep searches performed
  durationMs: number;      // Total wall-clock time
  error?: string;          // Error message if failed
}
Each context in contexts contains:
interface WarpGrepContext {
  file: string;      // File path relative to repoRoot
  content: string;   // Relevant code with line numbers
  lines?: '*' | Array<[number, number]>;  // Line ranges
}

Direct Import

You can also import the subagent creators directly without MorphClient:
import { createExploreSubagent } from '@morphllm/morphsdk/subagents/anthropic';

const explore = createExploreSubagent({
  client: new Anthropic(),
  model: 'claude-haiku-4-5-20251001',
  morphApiKey: process.env.MORPH_API_KEY,
  repoRoot: '.',
});