# Agents Guide

Agent primitives in AriaFlow: LLMAgent, FlowAgent, TriageAgent, CompositeAgent.

## Agent Types

### LLMAgent
Standard LLM-based agent with tools and system prompt. Good for free-form conversation.

```ts
const agent: LLMAgentConfig = {
  id: 'my-agent',
  type: 'llm',
  systemPrompt: 'You are a helpful assistant.',
  model: openai('gpt-4o'),
  tools: { myTool },
};
```

### FlowAgent
Node-based agent with defined flow graph and state management. Good for structured, multi-step processes.

```ts
const agent: FlowAgentConfig = {
  id: 'my-flow',
  type: 'flow',
  flow: {
    nodes: [
      { id: 'start', prompt: '...' },
      { id: 'collect_info', prompt: '...' },
    ],
  },
  initialNode: 'start',
  model: openai('gpt-4o'),
  tools: { myTool },
};
```

### TriageAgent
Routes user input to appropriate agent based on conditions or LLM classification.

```ts
const agent: TriageAgentConfig = {
  id: 'triage',
  type: 'triage',
  routes: [
    { agentId: 'sales', condition: 'sales-related' },
    { agentId: 'support', condition: 'support-related' },
  ],
  model: openai('gpt-4o'),
};
```

### CompositeAgent
Orchestrates multiple agents in sequence or parallel with coordination logic.

```ts
const agent: CompositeAgentConfig = {
  id: 'composite',
  type: 'composite',
  agents: ['agent1', 'agent2', 'agent3'],
  orchestration: 'sequential', // or 'parallel'
  model: openai('gpt-4o'),
};
```

## Agent-to-Agent Consultation

Use `runtime.runAgent()` to have one agent consult another directly, enabling **team collaboration**.

### Pattern

```ts
// Specialist agents
const weatherAgent = {
  id: 'weather',
  type: 'llm',
  systemPrompt: 'You are a weather expert.',
  model,
};

const newsAgent = {
  id: 'news',
  type: 'llm',
  systemPrompt: 'You are a news expert.',
  model,
};

// Lead agent with consultation tools
const leadAgent = {
  id: 'lead',
  type: 'llm',
  systemPrompt: 'You have access to weather and news specialists.',
  model,
  tools: {
    consult_weather: createTool({
      description: 'Consult Weather Specialist',
      inputSchema: z.object({ query: z.string() }),
      execute: async (input, options?: ToolExecutionOptions) => {
        const { runtime } = options.experimental_context;
        if (!runtime) throw new Error('Runtime required in tool context');
        
        let result = '';
        for await (const part of runtime.runAgent('weather', input, options.sessionId)) {
          if (part.type === 'text-delta') result += part.text;
        }
        
        return result;
      },
    }),
  },
};

// Runtime orchestrates everything
const runtime = new Runtime({
  agents: [leadAgent, weatherAgent, newsAgent],
  defaultModel: model,
  ctaConfig: { enabled: false }, // Disable CTA for direct agent control
});
```

### How It Works

1. **User talks to**: Lead agent
2. **Lead calls**: `consult_weather` tool
3. **Tool receives**: `runtime` via `experimental_context.runtime`
4. **Tool executes**: `runtime.runAgent('weather', query, sessionId)`
5. **Specialist responds**: Weather agent processes request
6. **Result returned**: Tool result passed back to lead agent
7. **Lead synthesizes**: Combines into ONE unified response

### Benefits

- ✅ **No wrappers** - Tools get runtime directly from context
- ✅ **Type-safe** - Uses proper types (no `as any`)
- ✅ **Session sharing** - Automatic sessionId injection
- ✅ **Team model** - Lead orchestrates specialists as one team
- ✅ **Single response** - Customer sees unified answer, not multiple agents

### When to Use

- **Team collaboration** (specialist pattern): One lead agent + multiple specialists
- **Direct agent control**: Use `runtime.runAgent()` to run specific agents
- **Bypass CTA**: Pass `agentId` to skip triage routing

### Example

See [Agent Consultation Example](../../examples/agents/agent-consultation.ts) for a full working demo.

## Key Types

- `ToolExecutionOptions` - Tool execution options including `experimental_context`
- `ToolExecutionContext` - Context passed to tools (includes `runtime`)
- `ToolExecutionContextWithRuntime` - Extended context with `runtime` field
- `Runtime.runAgent()` - Direct method to run specific agent

## Best Practices

1. **Always check runtime existence**: `if (!context?.runtime) throw new Error(...)`
2. **Use sessionId from options**: `options.sessionId` for consultation continuity
3. **Type-safe access**: Use helper functions like `getRuntimeFromContext()`
4. **Consultation tools should be lightweight**: Return string results, not complex objects
5. **Lead agent combines results**: The orchestrator agent synthesizes specialist responses

## Comparison to Other Patterns

| Pattern | Description | CTA | Handoffs | Wrapper Functions |
|---------|-------------|-----|-----------|------------------|
| Agent Consultation | Tool calls runtime.runAgent() directly | Skip CTA | No handoffs | Direct runtime access |
| Traditional Handoff | TriageAgent routes to agent | CTA | Handoffs | No runtime access to tools | N/A |
| Wrapper Function | Custom function wraps runtime.runAgent() | May not use CTA | N/A | N/A | Boilerplate code |

**Agent Consultation is the cleanest approach** - no wrappers, no handoffs, type-safe runtime access.
