---
description: 
globs: 
alwaysApply: true
---
# OpenSearch: AI-Powered Multi-Round Research Agent

## Project Overview

OpenSearch is an AI-powered research agent that conducts sophisticated multi-round web research by:
1. **Generating optimized search queries** from user research topics
2. **Executing web searches** via Exa API to gather information
3. **Reflecting on results** to identify knowledge gaps and determine sufficiency  
4. **Iteratively searching** with follow-up queries until comprehensive information is gathered
5. **Synthesizing comprehensive answers** with proper citations and footnotes

## Architecture Overview

The project implements **two parallel architectures** for different use cases:

### 1. CLI Application (`src/cli/`)
- **Interactive terminal interface** using React + Ink
- **Real-time progress visualization** for each research step
- **Event-driven architecture** with EventEmitter for UI updates
- **Direct BAML integration** for immediate AI function calls
- **Entry point**: `bun run cli`

### 2. Inngest Workflow (`src/inngest/`)
- **Scalable background processing** using Inngest functions
- **Real-time pub/sub** with channels for live updates
- **Workflow orchestration** with step functions and retry policies
- **Production-ready** with proper error handling and concurrency limits
- **API integration ready** for web applications

## Key Components & File Structure

```
opensearch/
├── src/cli/                    # CLI Application
│   ├── app.tsx                 # Main React component with UI orchestration
│   ├── agent.ts                # Core agent logic with EventEmitter
│   ├── components/             # UI Components
│   │   ├── research-input.tsx  # User input interface
│   │   ├── query-generation.tsx # Query generation display
│   │   ├── search-results.tsx  # Search execution progress
│   │   ├── reflection.tsx      # Analysis & knowledge gap display
│   │   ├── final-answer.tsx    # Answer synthesis display
│   │   └── markdown-renderer.tsx # Markdown rendering with citations
│   ├── types.ts                # TypeScript types for workflow steps
│   └── index.tsx               # CLI entry point
├── src/inngest/                # Backend Workflow
│   ├── functions/
│   │   ├── deep-research.ts    # Main research orchestration function
│   │   ├── execute-searches.ts # Web search execution with rate limiting
│   │   └── index.ts            # Function exports
│   ├── client.ts               # Inngest client configuration
│   └── connection.ts           # Function registration
├── baml_src/                   # AI Function Definitions
│   ├── generate_query.baml     # Search query generation
│   ├── reflect.baml            # Result analysis & gap identification
│   ├── create_answer.baml      # Answer synthesis with citations
│   ├── types.baml              # Structured data schemas
│   ├── clients.baml            # LLM client configurations
│   └── generators.baml         # Code generation settings
├── baml_client/                # Auto-generated (DO NOT EDIT)
│   └── [generated TypeScript files]
└── src/utils.ts                # Shared utilities
```

## Research Workflow

### Step-by-Step Process

1. **Input Collection**
   - User provides research topic
   - Topic validation and preprocessing

2. **Query Generation** (`GenerateQuery` BAML function)
   - AI analyzes research topic
   - Generates optimized search queries
   - Creates **query plan**: specific questions that need answering
   - Returns `SearchQueryList` with queries, rationale, and query plan

3. **Web Search Execution**
   - Parallel execution of search queries via Exa API
   - Rate limiting (5 requests/second) and error handling
   - Collection of search results with highlights and full text

4. **Reflection Analysis** (`Reflect` BAML function)
   - AI analyzes search results for **knowledge gap closure assessment**
   - Tracks answered vs. unanswered questions from query plan
   - Identifies relevant sources containing useful information
   - Determines if current knowledge gap was closed (if any)
   - Assesses overall research sufficiency

5. **Concurrent Processing Phase**
   - **Knowledge Gap Analysis** (`AnalyzeKnowledgeGaps` BAML function):
     - Strategic analysis of knowledge gap history and status
     - Smart abandonment decisions (3-round rule, thoroughness assessment)
     - Selection of next knowledge gap to research
     - Decision on whether to continue research or generate final answer
   - **Fact Extraction** (`ExtractRelevantFacts` BAML function):
     - Extract concise, relevant facts from sources identified by reflection
     - Reduce context length while preserving key information
     - Maintain source attribution for proper citations

6. **Follow-up Query Generation** (`GenerateFollowUpQueries` BAML function)
   - Generate diverse queries targeting specific knowledge gaps
   - Avoid repetition of previous failed query attempts
   - Strategic query diversity using different angles and terminology
   - Focus on closing identified knowledge gaps

7. **Iteration Logic**
   - If research insufficient AND gaps worth pursuing: continue with follow-up queries
   - If research sufficient OR all gaps resolved/abandoned: proceed to answer generation
   - Progress tracking with round numbers and knowledge gap status

8. **Answer Synthesis** (`CreateAnswer` or `CreateAnswerFromFacts` BAML functions)
   - AI synthesizes comprehensive answer from extracted facts or search results
   - Markdown formatting with proper structure
   - Automatic footnote generation with source citations
   - Confidence indicators and knowledge gap acknowledgments

## BAML Integration Details

### Core BAML Functions

#### `GenerateQuery(args: GenerateQueryArgs) -> SearchQueryList`
- **Purpose**: Transform research topic into search queries + structured plan
- **Model**: Gemini 2.5 Flash (fast, cost-effective)
- **Output**: Search queries, rationale, and comprehensive question plan
- **Key Features**: SMART question criteria, current date awareness

#### `Reflect(summaries, topic, date, queryPlan, answered, unanswered, currentGap, round, maxRounds) -> Reflection`
- **Purpose**: Analyze search results for knowledge gap closure assessment
- **Model**: Gemini 2.5 Flash (fast analysis)
- **Focus**: Gap closure evaluation, question tracking, source identification
- **Output**: Gap closure status, answered questions, relevant sources
- **Key Features**: Conservative gap closure assessment, evidence-based evaluation

#### `AnalyzeKnowledgeGaps(gapHistory, reflection, queryPlan, currentRound) -> KnowledgeGapAnalysis`
- **Purpose**: Strategic analysis of knowledge gap history and research continuation
- **Model**: Gemini 2.5 Pro (advanced reasoning)
- **Logic**: Smart abandonment criteria, gap prioritization, research strategy
- **Output**: Continue/stop decision, next gap selection, updated gap history
- **Key Features**: 3-round abandonment rule, thoroughness assessment, gap tracking

#### `ExtractRelevantFacts(relevantSources, topic, queryPlan, reflection, date) -> ExtractedFact[]`
- **Purpose**: Extract concise facts from relevant sources to reduce context length
- **Model**: Gemini 2.5 Flash (efficient extraction)
- **Logic**: Extract only facts relevant to query plan, avoid duplication
- **Output**: Concise facts with source attribution for citations
- **Key Features**: Context optimization, fact relevance filtering, citation preservation

#### `GenerateFollowUpQueries(targetGap, previousQueries, reflection, queryPlan, currentRound) -> FollowUpQueryGeneration`
- **Purpose**: Generate diverse queries for specific knowledge gaps
- **Model**: Gemini 2.5 Flash (fast query generation)
- **Logic**: Query diversity strategy, avoid repetition, gap-focused targeting
- **Output**: Diverse follow-up queries with strategy explanation
- **Key Features**: Query diversification, gap history awareness, strategic targeting

#### `CreateAnswer(date, topic, summaries) -> string`
- **Purpose**: Synthesize comprehensive research answer from search results
- **Model**: Gemini 2.5 Pro (high-quality generation)
- **Output**: Markdown-formatted answer with citations
- **Key Features**: Footnote system, confidence indicators, source attribution

#### `CreateAnswerFromFacts(date, topic, extractedFacts) -> string`
- **Purpose**: Synthesize comprehensive research answer from extracted facts
- **Model**: Gemini 2.5 Pro (high-quality generation)
- **Output**: Markdown-formatted answer with citations
- **Key Features**: Optimized for extracted facts, maintains citation accuracy

### Data Structures (baml_src/types.baml)

```typescript
// Core workflow types
SearchQueryList: { queryPlan: string[], query: string[], rationale: string }
Reflection: { 
  isSufficient: bool, 
  answeredQuestions: int[], 
  unansweredQuestions: int[],
  relevantSummaryIds: string[],
  currentGapClosed: bool,
  newGapsIdentified?: string[]
}
SearchResult: { url, id, title, highlights, text, highlightScores }
ExtractedFact: { sourceId, relevantFacts: string[], summary }

// Knowledge gap tracking types
KnowledgeGapHistory: { gaps: AttemptedKnowledgeGap[], currentGapIndex?: int }
AttemptedKnowledgeGap: { 
  description, status: "active"|"abandoned"|"resolved", 
  attemptCount, previousQueries: string[], 
  relatedQuestionIds: int[], firstAttemptedRound, lastAttemptedRound 
}
KnowledgeGapAnalysis: { 
  shouldContinueResearch: bool, nextGapToResearch?, 
  gapStatus: "new"|"continuing"|"switching"|"complete", 
  reasoning, updatedGapHistory: AttemptedKnowledgeGap[] 
}
FollowUpQueryGeneration: { queries: string[], rationale, queryStrategy }
```

## CLI Application Architecture

### Event-Driven Flow (`src/cli/agent.ts` + `app.tsx`)

```typescript
// Main workflow steps
type Step = 
  | 'input'                        // User input received
  | 'queries-generated'            // Search queries created
  | 'searching'                    // Web searches in progress  
  | 'search-results'               // Search results collected
  | 'reflection-complete'          // Gap closure analysis finished
  | 'knowledge-gap-analysis'       // Strategic gap analysis complete
  | 'followup-query-generation'    // Follow-up queries generated
  | 'summarization'                // Fact extraction (generating/complete)
  | 'max-steps-reached'            // Round limit reached
  | 'answer'                       // Final answer ready
```

**Key Features:**
- **Real-time UI updates** via EventEmitter
- **Progress visualization** with round tracking and gap status
- **State management** for complex multi-step workflow with knowledge gap history
- **Concurrent processing** of gap analysis and fact extraction
- **Strategic follow-up query generation** with diversity optimization
- **Error boundaries** and graceful degradation

### UI Components

- **ResearchInput**: Text input with submission handling
- **QueryGeneration**: Shows initial + follow-up queries with rationale and round context
- **SearchResults**: Real-time search progress with query status
- **ReflectionStep**: Gap closure analysis and question progress tracking
- **KnowledgeGapAnalysis**: Gap history, abandonment decisions, and next gap selection
- **FollowUpQueryGeneration**: Targeted query generation with diversity strategy
- **FactExtraction**: Concurrent fact extraction progress and results
- **FinalAnswer**: Answer synthesis with extracted facts or search results
- **MarkdownRenderer**: Citation rendering with link handling

### Event Emission Patterns (`src/cli/agent.ts` → `app.tsx`)

**CRITICAL**: The CLI uses two different event emission patterns that must be used correctly to avoid duplicate UI components:

#### `state-update` (Append Pattern)
- **Purpose**: Adds new steps to the workflow
- **Usage**: For genuinely new workflow steps or initial emissions
- **Example**: `eventEmitter.emit('state-update', { type: 'input', data: topic })`

#### `state-replace` (Replace Pattern)  
- **Purpose**: Updates existing steps of the same type (replaces the last occurrence)
- **Usage**: For progress updates or when transitioning from "generating" to "complete" states
- **Example**: `eventEmitter.emit('state-replace', { type: 'searching', data: updatedStatus })`

#### Common Anti-Patterns to Avoid

1. **Duplicate UI Components**: 
   - ❌ **Wrong**: Emitting both `followup-query-generation` AND `queries-generated` with same data
   - ✅ **Correct**: Only emit `followup-query-generation` for follow-up queries

2. **Progress Updates**:
   - ❌ **Wrong**: Using `state-update` for search query status changes (creates multiple search components)
   - ✅ **Correct**: Use `state-replace` to update search progress in-place

3. **Generating → Complete Transitions**:
   - ❌ **Wrong**: `state-update` for fact extraction completion (shows both generating + complete)
   - ✅ **Correct**: `state-replace` to transition from generating to complete state

#### UI Rendering Logic (`app.tsx`)

The UI handles "generating" states by checking if steps exist in future indices:
```typescript
const hasFollowUpStep = steps
  .slice(index + 1)
  .some((s) => s.type === 'followup-query-generation');

// Show generating state only if no complete step exists yet
{!hasFollowUpStep && isLastStep && shouldContinue && (
  <Component isGenerating={true} />
)}
```

**Key Rule**: If a UI component shows a "generating" state that will be replaced by a "complete" state, the complete state MUST use `state-replace`, not `state-update`.

## Inngest Workflow Architecture

### Function Structure (`src/inngest/functions/deep-research.ts`)

```typescript
// Main orchestration function
deepResearch = inngest.createFunction(
  { 
    id: 'deep-research',
    concurrency: { limit: 20 }
  },
  async ({ event, step, logger, publish }) => {
    // 1. Generate initial queries
    // 2. Execute searches in parallel
    // 3. Reflect on results
    // 4. Generate follow-ups or final answer
    // 5. Publish real-time updates
  }
)
```

**Key Features:**
- **Real-time channels** for live updates
- **Step functions** with automatic retries
- **Concurrent execution** with rate limiting
- **Error handling** with NonRetriableError
- **Logging** throughout workflow

### Real-time Channels

```typescript
resultsChannel(uuid)
  .addTopic('initialQueries', SearchQueryList)
  .addTopic('webSearchResults', SearchResult[])
  .addTopic('reflection', Reflection)
  .addTopic('finalAnswer', string)
```

## Key Utilities (`src/utils.ts`)

### Citation System
- **processFootnotes()**: Converts random IDs to numbered footnotes
- **insertCitationMarkers()**: Adds citation links to text
- **getCitations()**: Extracts citations from Gemini responses

### Helper Functions
- **nanoid()**: Generate unique IDs for search results
- **requireEnvironment()**: Environment variable validation

## Configuration & Environment

### Required Environment Variables
```bash
EXA_API_KEY=           # Exa search API key
GOOGLE_API_KEY=        # Google AI (Gemini) API key
ANTHROPIC_API_KEY=     # Anthropic (Claude) API key (optional)
OPENAI_API_KEY=        # OpenAI API key (optional)
INNGEST_BASE_URL=      # Inngest endpoint (for workflow version)
```

### LLM Client Options (baml_src/clients.baml)
- **Primary**: Google AI (Gemini 2.5 Pro/Flash)
- **Fallback**: OpenAI GPT-4o, Anthropic Claude 3.5 Sonnet
- **Retry policies**: Exponential backoff, constant delay

## Testing Strategy

### Integration Tests
- **Manual testing script**: `scripts/test-follow-up-queries.ts`
- **BAML function tests**: `test/generate-query.test.ts`
- **Real API integration**: Uses actual Exa + BAML calls

### Test Coverage
- Follow-up query generation and execution
- Multi-round workflow progression
- Question tracking and completion
- Citation processing and footnote generation

## Usage Examples

### CLI Usage
```bash
bun run cli                    # Start interactive CLI
bun run dev                    # Development mode with watching
bun test                       # Run all tests
bun test test/specific.test.ts # Run specific test
```

### Complex Research Example
```
Topic: "Compare fintech vs healthtech startup funding in 2024"

Round 1: General funding queries
├── Reflection: Missing healthtech-specific data
Round 2: Healthtech-focused follow-up queries  
├── Reflection: Missing comparative analysis
Round 3: Comparative analysis queries
└── Final Answer: Comprehensive comparison with metrics
```

## Production Considerations

### Rate Limiting
- **Exa API**: 5 requests/second (handled automatically)
- **LLM APIs**: Retry policies with exponential backoff
- **Concurrency**: Limited to 20 concurrent workflows

### Error Handling
- **Network failures**: Automatic retries with backoff
- **API limits**: Graceful degradation and queuing
- **Malformed responses**: Validation and fallbacks
- **Max rounds**: Prevents infinite loops

### Monitoring
- **Inngest dashboard**: Workflow execution monitoring
- **Step-by-step logging**: Detailed execution traces
- **Real-time channels**: Live progress updates

## Development Workflow

### Code Organization
- **CLI logic**: Event-driven with React components
- **Backend logic**: Step functions with proper error handling  
- **BAML functions**: Declarative AI function definitions
- **Shared utilities**: Citation processing and helpers
- **Type safety**: Strict TypeScript throughout

### Key Design Principles
- **Iterative research**: Multi-round approach mimics human research
- **Conservative early, decisive late**: Reflection strategy adapts by round
- **Source attribution**: Proper citations with footnotes
- **Real-time feedback**: Progress visibility throughout process
- **Graceful degradation**: Handles API failures and edge cases

This architecture provides both immediate interactive research (CLI) and scalable background processing (Inngest) while maintaining consistent AI-powered research quality through BAML function integration.
