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

# Auto-Discovery

> Automatic discovery and registration of agents and ensembles

## Overview

Auto-discovery is Conductor's **zero-config** approach to loading agents and ensembles. Instead of manually importing and registering each agent, Conductor automatically discovers them from your project directory at build time.

### Before Auto-Discovery

```typescript theme={null}
// ❌ Manual registration (old approach)
import greetConfig from './agents/greet/agent.yaml'
import greetHandler from './agents/greet/index.ts'
import analyzeConfig from './agents/analyze/agent.yaml'
import analyzeHandler from './agents/analyze/index.ts'
// ... import 50 more agents ...

const loader = new AgentLoader({ env, ctx })
loader.registerAgent(greetConfig, greetHandler)
loader.registerAgent(analyzeConfig, analyzeHandler)
// ... register 50 more agents ...
```

### After Auto-Discovery

```typescript theme={null}
// ✅ Auto-discovery (new approach)
import { createAutoDiscoveryAPI } from '@ensemble-edge/conductor/api'
import { agents } from 'virtual:conductor-agents'
import { ensembles } from 'virtual:conductor-ensembles'

export default createAutoDiscoveryAPI({
  agents,
  ensembles,
  autoDiscover: true,
})
```

That's it! All agents and ensembles are automatically discovered and registered.

***

## How It Works

Auto-discovery uses **Vite plugins** that scan your project directory at build time and generate **virtual modules** containing all your agents and ensembles.

<Steps>
  ### Step 1: Vite Plugin Scans Project

  During the build process, Vite plugins scan your project:

  * `vite-plugin-agent-discovery` → Scans `agents/**/*.yaml`
  * `vite-plugin-ensemble-discovery` → Scans `ensembles/**/*.yaml`

  ### Step 2: Virtual Modules Generated

  The plugins generate virtual modules that bundle:

  ```typescript theme={null}
  // virtual:conductor-agents (generated at build time)
  export const agents = [
    {
      name: 'greet',
      config: '...yaml content...',
      handler: () => import('./agents/greet/index.ts'),
    },
    {
      name: 'analyze',
      config: '...yaml content...',
      handler: () => import('./agents/analyze/index.ts'),
    },
    // ... all discovered agents
  ]
  ```

  ### Step 3: Runtime Auto-Discovery

  At runtime, `AgentLoader.autoDiscover()` processes the virtual modules:

  1. Parses YAML configs
  2. Loads handler functions (if present)
  3. Creates agent instances
  4. Registers them for execution
</Steps>

***

## Quick Start

### Installation

Auto-discovery is built into Conductor templates. If you're starting a new project:

```bash theme={null}
npx @ensemble-edge/edgit init my-project
cd my-project
```

The template includes auto-discovery by default.

### Manual Setup

If you have an existing project, add these plugins to `vite.config.ts`:

```typescript theme={null}
import { agentDiscoveryPlugin } from './scripts/vite-plugin-agent-discovery'
import { ensembleDiscoveryPlugin } from './scripts/vite-plugin-ensemble-discovery'

export default defineConfig({
  plugins: [
    agentDiscoveryPlugin(), // Discovers agents/**/*.yaml
    ensembleDiscoveryPlugin(), // Discovers ensembles/**/*.yaml
  ],
})
```

### Using Auto-Discovery

Create your entry point (`src/index.ts`):

```typescript theme={null}
import { createAutoDiscoveryAPI } from '@ensemble-edge/conductor/api'
import { ExecutionState, HITLState } from '@ensemble-edge/conductor/cloudflare'
import { agents } from 'virtual:conductor-agents'
import { ensembles } from 'virtual:conductor-ensembles'

export default createAutoDiscoveryAPI({
  // Virtual modules from build-time discovery
  agents,
  ensembles,

  // Enable auto-discovery (default: true)
  autoDiscover: true,

  // Authentication
  auth: {
    allowAnonymous: true, // Disable in production
  },

  // Logging
  logging: true,
})

// Export Durable Objects
export { ExecutionState, HITLState }
```

***

## File Structure

Auto-discovery expects this structure:

```
my-project/
├── agents/               # Auto-discovered
│   ├── greet/
│   │   ├── agent.yaml    # ✅ Discovered
│   │   └── index.ts      # ✅ Handler (optional)
│   ├── analyze/
│   │   └── agent.yaml    # ✅ Discovered (no handler)
│   └── generate-docs/    # ❌ Excluded by default
│       └── docs.yaml
├── ensembles/            # Auto-discovered
│   ├── blog-workflow.yaml  # ✅ Discovered
│   └── user-onboarding.yaml # ✅ Discovered
├── pages/                # Auto-discovered (separate plugin)
├── docs/                 # Auto-discovered (separate plugin)
└── src/
    └── index.ts          # Entry point
```

### What Gets Discovered

<Accordion title="Agents">
  **Pattern:** `agents/**/*.yaml`

  **Included:** `agents/examples/**` (by default, set `includeExamples: false` to exclude)

  **Excluded:** `agents/generate-docs/**` (by default)

  **Handler Detection:** If `agents/{name}/index.ts` exists, it's auto-loaded as the handler

  **Example:**

  ```
  agents/
  ├── greet/
  │   ├── agent.yaml      # Config
  │   └── index.ts        # Handler (optional)
  ├── analyze/
  │   └── agent.yaml      # Config only
  └── examples/           # ✅ Included by default
      └── hello/
          └── agent.yaml
  ```
</Accordion>

<Accordion title="Ensembles">
  **Pattern:** `ensembles/**/*.yaml`

  **No exclusions**

  **Example:**

  ```
  ensembles/
  ├── blog-workflow.yaml
  └── workflows/
      └── user-onboarding.yaml
  ```
</Accordion>

***

## API Reference

### `createAutoDiscoveryAPI(config)`

Creates a Conductor API with auto-discovery.

<ParamField path="config" type="AutoDiscoveryAPIConfig">
  Configuration options
</ParamField>

<ParamField path="config.autoDiscover" type="boolean" default={true}>
  Enable auto-discovery of agents and ensembles
</ParamField>

<ParamField path="config.agents" type="AgentDefinition[]">
  Virtual agents module from `virtual:conductor-agents`
</ParamField>

<ParamField path="config.ensembles" type="EnsembleDefinition[]">
  Virtual ensembles module from `virtual:conductor-ensembles`
</ParamField>

<ParamField path="config.auth" type="AuthConfig">
  Authentication configuration
</ParamField>

<ParamField path="config.logging" type="boolean" default={true}>
  Enable request logging
</ParamField>

<ParamField path="config.cors" type="CORSConfig">
  CORS configuration
</ParamField>

**Returns:** `ExportedHandler<Env>`

### `getAgentLoader()`

Get the initialized `AgentLoader` instance.

```typescript theme={null}
import { getAgentLoader } from '@ensemble-edge/conductor/api'

const loader = getAgentLoader()
if (loader) {
  console.log(`Loaded agents: ${loader.getAgentNames().join(', ')}`)
}
```

**Returns:** `AgentLoader | null`

### `getEnsembleLoader()`

Get the initialized `EnsembleLoader` instance.

```typescript theme={null}
import { getEnsembleLoader } from '@ensemble-edge/conductor/api'

const loader = getEnsembleLoader()
if (loader) {
  console.log(`Loaded ensembles: ${loader.getEnsembleNames().join(', ')}`)
}
```

**Returns:** `EnsembleLoader | null`

***

## Advanced Usage

### Including/Excluding Examples

By default, agents in `agents/examples/` **are included** in discovery. To exclude example agents:

```typescript theme={null}
// vite.config.ts
export default defineConfig({
  plugins: [
    agentDiscoveryPlugin({
      includeExamples: false, // Exclude agents/examples/
    }),
  ],
})
```

### Custom Exclusions

Exclude specific directories from agent discovery:

```typescript theme={null}
// vite.config.ts
export default defineConfig({
  plugins: [
    agentDiscoveryPlugin({
      agentsDir: 'agents',
      excludeDirs: ['generate-docs', 'internal', 'experimental'],
    }),
  ],
})
```

### Custom File Extensions

Support `.yml` instead of `.yaml`:

```typescript theme={null}
export default defineConfig({
  plugins: [
    agentDiscoveryPlugin({ fileExtension: '.yml' }),
    ensembleDiscoveryPlugin({ fileExtension: '.yml' }),
  ],
})
```

### Manual Loader Access

Use the loaders directly for advanced use cases:

```typescript theme={null}
import { getAgentLoader, getEnsembleLoader } from '@ensemble-edge/conductor/api'

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext) {
    const agentLoader = getAgentLoader()
    const ensembleLoader = getEnsembleLoader()

    if (!agentLoader || !ensembleLoader) {
      return new Response('Loaders not initialized', { status: 503 })
    }

    // Custom logic using loaders
    const agents = agentLoader.getAllAgents()
    const ensembles = ensembleLoader.getAllEnsembles()

    return Response.json({
      agents: agents.length,
      ensembles: ensembles.length,
    })
  },
}
```

***

## Troubleshooting

### Agents Not Found

**Problem:** Auto-discovery doesn't find your agents

**Solution:**

1. Check file structure matches `agents/**/*.yaml`
2. Ensure agents aren't in excluded directories
3. Check Vite plugin is registered in `vite.config.ts`
4. Rebuild: `pnpm run build`

### TypeScript Errors

**Problem:** `Cannot find module 'virtual:conductor-agents'`

**Solution:**

Add type declarations:

```typescript theme={null}
// src/virtual-modules.d.ts
declare module 'virtual:conductor-agents' {
  export interface AgentDefinition {
    name: string
    config: string
    handler?: () => Promise<any>
  }

  export const agents: AgentDefinition[]
  export const agentsMap: Map<string, AgentDefinition>
}

declare module 'virtual:conductor-ensembles' {
  export interface EnsembleDefinition {
    name: string
    config: string
  }

  export const ensembles: EnsembleDefinition[]
  export const ensemblesMap: Map<string, EnsembleDefinition>
}
```

### Build Errors

**Problem:** Build fails with "Directory not found"

**Solution:**

Create empty directories if needed:

```bash theme={null}
mkdir -p agents ensembles
```

The Vite plugins handle missing directories gracefully but some bundlers may require them to exist.

***

## Migration Guide

### From Manual Registration

<Steps>
  ### Step 1: Remove Manual Imports

  Delete all manual agent/ensemble imports from your entry point.

  ### Step 2: Add Virtual Module Imports

  ```typescript theme={null}
  import { agents } from 'virtual:conductor-agents'
  import { ensembles } from 'virtual:conductor-ensembles'
  ```

  ### Step 3: Replace with `createAutoDiscoveryAPI`

  ```typescript theme={null}
  export default createAutoDiscoveryAPI({
    agents,
    ensembles,
    autoDiscover: true,
    auth: { allowAnonymous: true },
  })
  ```

  ### Step 4: Rebuild and Test

  ```bash theme={null}
  pnpm run build
  npx wrangler dev
  ```
</Steps>

***

## Discovery Registries (ctx API)

In addition to auto-discovering agents and ensembles at build time, Conductor provides runtime access to discovery registries through the `ctx` API. This allows agents to introspect what's available in the project.

### ctx.agentRegistry

Access the agent registry to list and inspect available agents:

#### ctx.agentRegistry.list()

List all available agents in the project:

```typescript theme={null}
// agents/inspector/index.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default async function listAgents(ctx: AgentExecutionContext) {
  // List all available agents
  const agents = await ctx.agentRegistry.list()

  return {
    total: agents.length,
    agents: agents.map(a => ({
      name: a.name,
      description: a.description
    }))
  }
}
```

#### ctx.agentRegistry.get(name)

Get a specific agent definition:

```typescript theme={null}
// agents/inspector/index.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default async function inspectAgent(ctx: AgentExecutionContext) {
  const { agentName } = ctx.input as { agentName: string }

  // Get agent definition
  const agent = await ctx.agentRegistry.get(agentName)

  return {
    name: agent.name,
    description: agent.description,
    operation: agent.operation,
    schema: agent.schema
  }
}
```

### ctx.ensembleRegistry

Access the ensemble registry to list and inspect available ensembles:

#### ctx.ensembleRegistry.list()

List all available ensembles in the project:

```typescript theme={null}
// agents/ensemble-lister/index.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default async function listEnsembles(ctx: AgentExecutionContext) {
  // List all available ensembles
  const ensembles = await ctx.ensembleRegistry.list()

  return {
    total: ensembles.length,
    ensembles: ensembles.map(e => ({
      name: e.name,
      description: e.description,
      triggers: e.trigger?.map(t => t.type)
    }))
  }
}
```

#### ctx.ensembleRegistry.get(name)

Get a specific ensemble definition:

```typescript theme={null}
// agents/ensemble-inspector/index.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default async function inspectEnsemble(ctx: AgentExecutionContext) {
  const { ensembleName } = ctx.input as { ensembleName: string }

  // Get ensemble definition
  const ensemble = await ctx.ensembleRegistry.get(ensembleName)

  return {
    name: ensemble.name,
    description: ensemble.description,
    triggers: ensemble.trigger,
    flow: ensemble.flow?.map(step => ({
      name: step.name,
      agent: step.agent,
      operation: step.operation
    }))
  }
}
```

### ctx.config

Access the project's Conductor configuration:

```typescript theme={null}
// agents/config-inspector/index.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default async function inspectConfig(ctx: AgentExecutionContext) {
  // Access project configuration
  const config = ctx.config

  return {
    version: config.version,
    name: config.name,
    components: {
      schemas: Object.keys(config.components?.schemas || {}),
      prompts: Object.keys(config.components?.prompts || {}),
      configs: Object.keys(config.components?.configs || {})
    }
  }
}
```

### Complete Example: Dynamic Routing Agent

```typescript theme={null}
// agents/dynamic-router/index.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default async function routeRequest(ctx: AgentExecutionContext) {
  const { action } = ctx.input as { action: string }

  // Check if agent exists for this action
  const agents = await ctx.agentRegistry.list()
  const targetAgent = agents.find(a => a.name === `handle-${action}`)

  if (!targetAgent) {
    return {
      success: false,
      error: `No agent found for action: ${action}`,
      availableAgents: agents.map(a => a.name)
    }
  }

  return {
    success: true,
    agent: targetAgent.name,
    description: targetAgent.description
  }
}
```

### Complete Example: Documentation Generator

```typescript theme={null}
// agents/docs-generator/index.ts
import type { AgentExecutionContext } from '@ensemble-edge/conductor'

export default async function generateDocs(ctx: AgentExecutionContext) {
  // List all agents and ensembles
  const agents = await ctx.agentRegistry.list()
  const ensembles = await ctx.ensembleRegistry.list()

  // Generate documentation
  const docs = {
    project: ctx.config.name,
    version: ctx.config.version,
    agents: agents.map(a => ({
      name: a.name,
      description: a.description,
      operation: a.operation,
      inputs: a.schema?.input,
      outputs: a.schema?.output
    })),
    ensembles: ensembles.map(e => ({
      name: e.name,
      description: e.description,
      triggers: e.trigger?.map(t => t.type),
      steps: e.flow?.length || 0
    }))
  }

  return docs
}
```

### Use Cases

Discovery registries enable powerful introspection patterns:

1. **Dynamic Routing**: Route requests to agents based on runtime conditions
2. **Documentation Generation**: Auto-generate API docs from agent definitions
3. **Validation**: Check if required agents exist before execution
4. **Monitoring**: List all available capabilities for observability
5. **Development Tools**: Build CLI tools that inspect project structure

## Benefits

<CardGroup cols={2}>
  <Card title="Zero Config" icon="magic">
    No manual imports or registration required
  </Card>

  <Card title="Fast Development" icon="bolt">
    Add new agents by creating files—no code changes
  </Card>

  <Card title="Type Safe" icon="shield">
    Full TypeScript support with virtual module types
  </Card>

  <Card title="Production Ready" icon="rocket">
    Build-time discovery ensures zero runtime overhead
  </Card>
</CardGroup>

***

## Next Steps

<Card title="Your First Agent" icon="play" href="/conductor/getting-started/your-first-agent">
  Create your first auto-discovered agent
</Card>

<Card title="Your First Ensemble" icon="layer-group" href="/conductor/getting-started/your-first-ensemble">
  Create your first auto-discovered ensemble
</Card>

<Card title="Deployment" icon="cloud" href="/conductor/getting-started/your-first-deployment">
  Deploy your auto-discovery project to production
</Card>
