Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
9fe27fc
feat: add contact-extractor-agent example with initial configuration …
VISHWAJ33T Sep 22, 2025
21f89b0
feat: implement contact-extractor-agent with layout, styling, and cha…
VISHWAJ33T Sep 22, 2025
7149a9a
feat: add new components and context providers for enhanced chat func…
VISHWAJ33T Sep 22, 2025
a41e2b8
feat: add custom hook useIsMobile for mobile responsiveness in contac…
VISHWAJ33T Sep 22, 2025
a4cbd19
feat: add utility functions and REST SDK for enhanced data handling i…
VISHWAJ33T Sep 22, 2025
1ed64dc
feat: add new SVG icons for contact-extractor-agent including file, g…
VISHWAJ33T Sep 22, 2025
4003975
feat: implement system agent and middleware for message context manag…
VISHWAJ33T Sep 22, 2025
129dc21
feat: implement main AI router for contact extraction with integrated…
VISHWAJ33T Sep 22, 2025
c824c65
feat: add helper modules for contact extraction including JSDOM, sche…
VISHWAJ33T Sep 22, 2025
10b7c83
feat: implement contact extraction agents including single contact ex…
VISHWAJ33T Sep 22, 2025
9ff1035
feat: add contact extraction dashboard with data table and persona an…
VISHWAJ33T Sep 22, 2025
dd71516
feat: add AI component map for contact extraction tools integration
VISHWAJ33T Sep 22, 2025
b2c2ede
feat: implement contacts dashboard with data table, persona modal, an…
VISHWAJ33T Sep 22, 2025
c2c8241
feat: create contacts dashboard page with responsive layout and conta…
VISHWAJ33T Sep 22, 2025
b5b8afd
feat: add API routes for contact retrieval and persona analysis in co…
VISHWAJ33T Sep 22, 2025
79ad559
feat: add comprehensive documentation for `ai-router` including archi…
VISHWAJ33T Sep 22, 2025
7b01d07
refactor: remove persona agent implementation from contact-extractor-…
VISHWAJ33T Sep 22, 2025
6c3f07c
fix: update glob pattern in debugging rules to target AI-related files
VISHWAJ33T Sep 22, 2025
70f13c3
fix: update glob pattern in debugging rules for ai-router issues
VISHWAJ33T Sep 22, 2025
fa1d2a3
refactor: enhance contact document formatting and improve code readab…
VISHWAJ33T Sep 22, 2025
68e2686
feat: add refresh functionality to contacts table for improved data h…
VISHWAJ33T Sep 22, 2025
edbe816
refactor: streamline persona analysis function and enhance contact co…
VISHWAJ33T Sep 22, 2025
dd90ec7
fix: improve error handling and enhance scraping logic in deepPersona…
VISHWAJ33T Sep 22, 2025
ada0103
refactor: update state management documentation for clarity and consi…
VISHWAJ33T Sep 22, 2025
af9aed4
Merge branch 'main' into vwjt_contact_extractor_agent
VISHWAJ33T Sep 26, 2025
066799e
chore: update package-lock.json to remove unused dependencies and upd…
VISHWAJ33T Sep 26, 2025
1901820
feat: add ai-router-cli package with build and dev commands
VISHWAJ33T Sep 30, 2025
b72fe15
feat: enhance contact extractor agent with new routing and middleware…
VISHWAJ33T Sep 30, 2025
a2f4962
fix: update ai-router dependencies and improve exports in index and r…
VISHWAJ33T Sep 30, 2025
97db333
chore: update package-lock.json and package.json to include new depen…
VISHWAJ33T Sep 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .cursor/rules/01-architecture.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
glob: "**/app/ai/**/*.{ts,tsx}"
description: "Core architectural patterns of the `ai-router` framework, including Orchestrators, Workers, and the dual nature of agents as tools and APIs."
alwaysApply: true
---
# 01: Core Architecture & Philosophy

This document outlines the core architectural patterns of the `ai-router` framework. Understanding these principles is essential for building robust and scalable AI agents.

## Core Philosophy: A Framework for Composition

`ai-router` is not just a tool; it's a framework for composing complex AI workflows from smaller, reusable components. The core architectural pattern is the separation of concerns between **Orchestrators** and **Workers**.

- **Orchestrator Agents (`/app/ai/index.ts`)**: An orchestrator is the "brain" of a complex operation. It understands the user's high-level goal and is responsible for breaking it down into a sequence of tasks. It manages the master state of the operation and delegates tasks to specialized worker agents.

- **Worker Agents (`/app/ai/agents/**/*.ts`)**: Workers are specialized tools that perform a single, well-defined task (e.g., scraping a URL, parsing HTML, calling an external API). They are the building blocks of your AI system.

---

## The Dual Nature of Agents: Tools and APIs

A key feature of `ai-router` is that every agent can serve two purposes:

1. **As an Internal Tool**: Agents can be called by other agents using `ctx.next.callAgent()`. This is the primary way that orchestrators delegate tasks to workers. When used this way, agents can return large amounts of data without incurring significant token costs, as the data is passed directly between agents on the server.

2. **As a Standalone API/Tool**: Agents can also be exposed to the main orchestrator (or even to the outside world) as standalone tools using the `.actAsTool()` method. When an agent is used this way, its `outputSchema` should be carefully designed to be as concise as possible. Returning large amounts of data from a tool call can be very expensive, as the entire output is serialized and sent back to the orchestrating AI.

**Example Scenario: A Web Search Agent**
Imagine an orchestrator agent that performs web research.

- It might have two worker agents: `fastSearch.ts` and `deepSearch.ts`. These workers are the internal tools that call the Brave Search API and return a rich set of results.
- The orchestrator (`braveResearch/index.ts`) calls these workers using `ctx.next.callAgent()`. It takes their detailed output and saves it to `ctx.state.researchData`.
- Crucially, when it's done, it only returns a simple `{ status: 'Research Completed!' }` message to the main AI that called it. This is a critical optimization that saves a massive amount of tokens.
116 changes: 116 additions & 0 deletions .cursor/rules/02-workflow.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
---
glob: "**/app/ai/**/*.ts"
description: "A step-by-step guide to the `ai-router` development workflow, from defining schemas to creating and integrating agents."
alwaysApply: true
---
# 02: Agent Development Workflow

This document provides a step-by-step guide for building new agents and capabilities within the `ai-router` framework.

---

## The Golden Rules of Agent Development

1. **Be Explicit, Be Rude with Prompts**: Do not be polite or conversational with the AI. Use strong, imperative language (e.g., "You MUST...", "Do NOT..."). Structure prompts with clear sections (`# Rules`, `# Task`) and use system prompts to define the AI's persona and mission.
2. **Never Assume**: The AI will invent information if not strictly forbidden. Always include a rule like: "You MUST only select from the list provided. Do not create, modify, or assume any URLs."
3. **Schema is Your Guardrail**: Use Zod schemas to rigorously define the expected output of every AI call. This is your primary defense against data hallucinations.

---

## Step 1: Define Your Schemas (`/helpers/schema.ts`)

Before writing any agent logic, define the data structures you'll be working with.
- **Input Schema**: What data does your agent need to do its job?
- **Output Schema**: What data will your agent produce?
- Use Zod for strong type validation. For recursive or self-referential structures, use `z.lazy()`.

---

## Step 2: Create Your Worker Agents (`/app/ai/agents/your-agent`)

Build your specialized worker agents. Each worker should have a single, well-defined responsibility.

```typescript
// Example: /app/ai/agents/your-agent/parser.agent.ts
import { AiRouter } from '@microfox/ai-router';
// ... other imports

const aiRouter = new AiRouter();

export const parsingAgent = aiRouter.agent('/', async (ctx) => {
const { html, url } = ctx.request.params;
// ... perform pure, logic-based parsing ...
return { emails, socials, otherLinks };
});
```

---

## Step 3: Create Your Orchestrator (`/app/ai/agents/your-agent/index.ts`)

Build your orchestrator agent. This agent will define the high-level workflow and call your worker agents using `ctx.next.callAgent()`.

```typescript
// Example: /app/ai/agents/your-agent/index.ts
import { AiRouter } from '@microfox/ai-router';
import { scrapingAgent } from './scraping.agent';
import { parsingAgent } from './parsing.agent';

const aiRouter = new AiRouter();

export const yourOrchestratorAgent = aiRouter
.agent('/scrape', scrapingAgent)
.agent('/parse', parsingAgent)
.agent('/', async (ctx) => {
// ... orchestrator logic ...
const scrapeResult = await ctx.next.callAgent('/scrape', { url: '...' });
// ...
const parseResult = await ctx.next.callAgent('/parse', { html: '...' });
// ...
});
```

---

## Step 4: Expose as a Tool (`actAsTool`)

Use `.actAsTool()` to expose your orchestrator as a tool to the main AI. Be thoughtful about the `outputSchema` to minimize token usage.

```typescript
// In your orchestrator file, e.g., /app/ai/agents/your-agent/index.ts
export const yourOrchestratorAgent = new AiRouter()
// ... agent definitions ...
.actAsTool('/', {
id: 'yourToolId',
name: 'Your Tool Name',
description: 'A detailed description of what this tool does.',
inputSchema: yourInputSchema, // Defined in schema.ts
outputSchema: z.object({ status: z.string() }), // Keep it concise!
});
```

---

## Step 5: Integrate into the Main Router (`/app/ai/index.ts`)

Finally, attach your new agent to the main `aiRouter` and update the main AI's prompts to teach it how and when to use your new tool.

```typescript
// /app/ai/index.ts
import { yourOrchestratorAgent } from './agents/your-agent';

const aiRouter = new AiRouter();

const aiMainRouter = aiRouter
.agent('/your-agent', yourOrchestratorAgent)
// ... other agents

// ... in the main AI agent ...
const { object: analysis, usage } = await generateObject({
model: google('gemini-2.5-pro'),
tools: {
...ctx.next.agentAsTool('/your-agent'), // Make the tool available
},
prompt: `Based on the user's request, decide which tool to use.`,
});
```
80 changes: 80 additions & 0 deletions .cursor/rules/03-state-management.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
glob: '**/app/ai/**/*.ts'
description: 'Best practices for state management in `ai-router`, covering `ctx.state`, token optimization, and safe parallelism.'
alwaysApply: true
---

# 03: State Management & Advanced Patterns

`ctx.state` is a powerful tool for sharing data between agents, but it must be used with care to avoid bugs and maintain a clean architecture.

---

## The Three Ways to Execute Agents

There are three distinct ways to execute an agent, and each has a different implication for how you should handle return values and state.

1. **`ctx.next.callAgent()` (Internal Call)**
- **Description**: This is for server-side, inter-agent communication. One agent calls another.
- **Return Value**: Can return large, complex data objects. Since the data never leaves the server, there are no token costs.
- **Pathing**: Supports nested paths (`'/sub-agent'`) but **not** backtracking (`'../'`). For root-based paths from deeply nested agents, use the `'@'` alias (e.g., `'@/main-agent/worker'`).

2. **`ctx.next.agentAsTool()` (Exposing to AI)**
- **Description**: This exposes an agent as a "tool" that the main orchestrating AI can choose to call.
- **Return Value**: **MUST** be minimal. The entire output is serialized to JSON and sent back to the AI, which consumes a large number of tokens. The best practice is to save the rich output to `ctx.state` and return only a simple status object (e.g., `{ status: 'Completed' }`).

3. **HTTP API Call (GET Request)**
- **Description**: Every agent is also a standard API endpoint that can be called via a GET request. The URL structure is `/api/studio/chat/agent/{your-agent-path}`. All input parameters must be passed as URL query parameters.
- **Return Value**: Must return all the data the client needs, as the client does not have access to the server-side `ctx.state`.
- **Example**:

```typescript
const contactId = '123';
const urls = ['https://example.com', 'https://another.com'];
const response = await fetch(
`/api/studio/chat/agent/extract/deep-persona?contactId=${contactId}&urls=${urls.join(',')}`,
);

const result = await response.json();

const agentResponse = result[0]?.parts[0]?.output;
console.log(agentResponse);
```

---

## The Golden Rules of State Management

1. **State is for Sharing and Cost Reduction**: The primary purpose of `ctx.state` is to allow a sequence of agents to share rich data _on the server_ without passing it back to the AI. This is a critical pattern for reducing token costs.

2. **The Orchestrator Owns the Master State**: The main orchestrator (typically in `/app/ai/index.ts`) is responsible for initializing and managing the "master" state for the entire user session.

3. **Design State for Parallelism**: This is the most critical rule. When you call multiple agents in parallel, they will all share the _same_ `ctx.state` object. Your state must be designed to be "additive" to prevent race conditions.
- **Use `Set` for unique lists**: If multiple parallel workers are adding to a list of visited URLs, use a `Set`. Multiple workers can safely call `ctx.state.visitedUrls.add(url)` without overwriting each other.
- **Use arrays for results**: If workers are adding results to a list, they can safely `push` to the same array.
- **Avoid simple properties**: Do not have parallel workers trying to update the same simple property (e.g., `ctx.state.status = 'in-progress'`). This will lead to race conditions.

4. **Isolate When Necessary**: If a worker agent needs to manage its own internal state during a complex, multi-step operation (like a recursive search), it should use local variables, not `ctx.state`. It should then return a self-contained result that the orchestrator can safely merge back into the master state.

---

### Example: State-Safe Parallelism

```typescript
// In the Orchestrator
ctx.state.visitedUrls = new Set<string>(); // Use a Set for safe parallel adds
const promises = urls.map((url) =>
ctx.next.callAgent('/worker', {
url,
masterVisitedUrls: Array.from(ctx.state.visitedUrls),
}),
);
const results = await Promise.all(promises);

for (const result of results) {
if (result.ok) {
// Safely merge results
result.data.visitedUrls.forEach((url) => ctx.state.visitedUrls.add(url));
}
}
```
114 changes: 114 additions & 0 deletions .cursor/rules/04-building-agent-uis.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
glob: "**/components/ai/**/*.{ts,tsx}"
description: "A guide to building type-safe UIs for `ai-router` agents, including the `aiComponentMap`, component structure, and using `AiRouterTools`."
alwaysApply: true
---
# 04: Building Agent UIs

The `ai-router` provides a powerful system for creating type-safe, component-based UIs for your agents.

---

## 1. The `aiComponentMap`

The file `/components/ai/index.tsx` is the central registry for all agent UIs. It exports an `aiComponentMap` object that maps the `id` of a tool (from `.actAsTool()`) to the React component that should render its output.

```typescript
// /components/ai/index.tsx
'use client';

import { yourAgentComponentMap } from "./your-agent";
import { anotherAgentComponentMap } from "./another-agent";

export const aiComponentMap = {
tools: {
...yourAgentComponentMap,
...anotherAgentComponentMap,
},
};
```

---

## 2. UI Component Structure

For each agent, you should create a corresponding directory in `/components/ai`. Inside this directory, an `index.ts` file will define the mapping for that specific agent's components.

A tool's UI can have multiple parts:
- `full`: The main component for rendering the tool's output. This is the most common part you will define.
- `header_sticky`: A component that can stick to the top of the message UI.
- `footer_sticky`: A component that can stick to the bottom of the message UI.

```typescript
// /components/ai/your-agent/index.ts
import { YourAgentDashboard } from './Dashboard';

export const yourAgentComponentMap = {
yourToolId: { // This ID must match the 'id' in actAsTool
full: YourAgentDashboard, // The main UI component
},
};
```

---

## 3. `actAsTool` Metadata

The `metadata` object in the `.actAsTool()` configuration is how you control the appearance of your tool in the UI *before* it's rendered.

- `title`: The main title of the tool shown in the UI.
- `parentTitle`: If provided, this will group the tool under a category. For example, multiple search tools could have a `parentTitle` of "Web Search".
- `icon`: A URL to the icon for the tool.

```typescript
// In your orchestrator agent, e.g., /app/ai/agents/your-agent/index.ts
.actAsTool('/', {
id: 'yourToolId',
// ... other properties
metadata: {
icon: 'https://.../your-icon.svg',
title: 'Your Tool Name',
parentTitle: 'Your Category',
},
});
```

---

## 4. Type-Safe UI Components

To connect your UI components to the backend schemas, the main orchestrator (`/app/ai/index.ts`) must export a generated `AiRouterTools` type. Your UI components can then import this type to get full type safety and autocompletion for the tool's output.

**Step 1: Export the type from the orchestrator.**
```typescript
// /app/ai/index.ts
const aiRouterRegistry = aiMainRouter.registry();
const aiRouterTools = aiRouterRegistry.tools;
// This is a magical generated type that understands all your tools
type AiRouterTools = InferUITools<typeof aiRouterTools>;
export { aiRouterTools, type AiRouterTools };
```

**Step 2: Import the type and use it in your component.**
```typescript
// /components/ai/your-agent/Dashboard.tsx
import { AiRouterTools } from '@/app/ai';
import { ComponentType } from 'react';
import { ToolUIPart } from 'ai';

// Use the generated type to create a type-safe prop for your component
export const YourAgentDashboard: ComponentType<{
tool: ToolUIPart<Pick<AiRouterTools, 'yourToolId'>>;
}> = (props) => {
const { tool } = props;

// Now, 'tool.output' is fully typed based on your Zod schema!
const { status, results } = tool.output;

return (
<div>
{/* ... your rendering logic ... */}
</div>
);
};
```
Loading