# Project export: Riley

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: UC Berkeley AI Hackathon 2025
- Tagline: Save the number that might just save you. Riley is an AI crisis specialist providing instant, 24/7 support through phone calls for remote injuries and evacuation situations—advancing UN SDGs 3 & 11.
- Devpost: https://devpost.com/software/riley-0w5yts
- GitHub: https://github.com/WeeeHung/riley
- Video: https://www.youtube.com/embed/hhWxcleyB_U?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — weehung-livex (1 commits)

## Devpost submission (written by the team)

### Inspiration

SDG 3 (Good Health and Well-being): Focuses on ensuring healthy lives and promoting well-being for all ages. Key targets include reducing maternal and child mortality, ending epidemics of major infectious diseases, and reducing premature mortality from non-communicable diseases. SDG 11 (Sustainable Cities and Communities): Aims to make cities and human settlements inclusive, safe, resilient, and sustainable. This involves ensuring access to adequate, safe, and affordable housing and basic services, improving transportation systems, enhancing urban planning, and reducing the environmental impact of cities. The devastating LA fires and escalating global tensions reminded us that crises don't wait for business hours. When disaster strikes—whether it's fleeing flames, managing PTSD from conflict zones, or treating injuries in remote areas—people need immediate, expert guidance. Current emergency systems are overwhelmed, understaffed, and often unreachable when needed most. We built Riley to ensure no one faces a crisis alone, advancing UN SDGs 3 & 11 by providing universal healthcare access and building resilient communities that can respond to any emergency, anywhere, anytime.

### What it does

Riley is an AI-powered crisis response system accessible 24/7 via phone calls. It provides specialized support across three critical areas: Mental Health Crisis: Suicide prevention, PTSD support, panic attacks, and trauma counseling Remote Medical Emergencies: First aid guidance, injury assessment, and medical triage for isolated locations Disaster Evacuation: Fire, flood, earthquake response, and emergency evacuation planning Riley remembers every caller through persistent memory tied to phone numbers, building trusted relationships across multiple interactions. Crisis management boards, insurance companies, hospitals, and emergency institutions can access the platform for better crisis coordination and resource allocation.

### How we built it

Voice Infrastructure: VAPI provides low-latency, empathetic phone call handling with natural speech recognition and generation. Memory System: Letta's multi-user architecture creates unique user profiles for each phone number, ensuring persistent context across all interactions. Multi-Agent Architecture: Specialized Letta agents for mental health, medical emergencies, and evacuation work collaboratively while sharing user memory, reducing hallucination and improving response accuracy. Escalation Tools: Agentic tools automatically detect high-priority situations and escalate calls to human operators when necessary. Analytics Dashboard: Real-time crisis categorization and management interface for institutions to track patterns and optimize response strategies.

### Challenges we ran into

Memory Persistence: Implementing sticky memory across phone sessions required careful user management in Letta's multi-user system. Agent Coordination: Ensuring multiple specialized agents could share context without conflicting advice or losing conversation flow. Syncing Letta and Vapi was abit of a challenge as well. Emergency Escalation: Building reliable triggers to identify when AI should immediately transfer to human crisis counselors. Voice Latency: Optimizing VAPI integration to maintain empathetic, real-time conversations during high-stress situations. Crisis Detection: Developing algorithms to accurately categorize and prioritize different types of emergencies.

### Accomplishments we're proud of

Universal Access: Created a system that works anywhere with phone service, eliminating geographic and infrastructure barriers to crisis support. Persistent Relationships: Successfully implemented memory retention that allows callers to build ongoing relationships with their AI crisis specialist. Multi-Modal Crisis Response: Built the first AI system that handles mental health, medical, and evacuation emergencies in one unified platform. Institutional Integration: Designed scalable analytics that help crisis management organizations optimize their response strategies. Real-Time Escalation: Implemented seamless human handoff for situations requiring immediate professional intervention.

### What we learned

Crisis Communication: The importance of empathetic, non-judgmental language in emergency situations cannot be overstated. Memory Architecture: Persistent AI relationships significantly improve trust and response effectiveness in crisis scenarios. Multi-Agent Coordination: Specialized agents working together provide more accurate, contextual advice than single generalist models. Scalability Matters: AI crisis response can provide insights into community risk patterns that improve overall emergency preparedness. Human-AI Collaboration: The most effective crisis response combines AI availability with human expertise for complex situations.

### What's next

Global Expansion: Deploy Riley in multiple languages and regions, partnering with international crisis response organizations. Predictive Analytics: Use aggregated crisis data to predict and prevent community-wide emergencies before they escalate. IoT Integration: Connect with smart home devices, medical wearables, and environmental sensors for proactive crisis detection. Training Platform: Develop Riley as a training tool for human crisis counselors and emergency responders. Policy Impact: Work with governments to integrate Riley into national emergency response frameworks, advancing SDGs 3 & 11 at scale. Specialized Verticals: Create industry-specific versions for schools, workplaces, and high-risk environments like oil rigs or remote research stations. Riley represents the future of crisis response—where help is always just a phone call away, building healthier, more resilient communities worldwide.

## README (from the GitHub repository)

# Emergency Triage Phone App 🚑

A 24/7 emergency triaging phone call application that connects injured or endangered individuals with AI agents for situation assessment and guidance. The system persists caller memory across sessions and provides a real-time dashboard for monitoring active calls.

## 🏗️ Architecture Overview

### Tech Stack

- **Frontend**: React + TypeScript + Mantine UI
- **Backend**: Node.js + Express + TypeScript
- **Database**: PostgreSQL with emergency call schemas
- **Voice API**: VAPI integration for phone calls
- **AI Agents**: Letta (formerly MemGPT) for persistent memory
- **Real-time**: WebSocket connections for dashboard updates
- **Hosting**: Vercel (frontend) + Railway/Render (backend)

### System Components

```
┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   Emergency     │    │   VAPI Voice     │    │   Dashboard     │
│   Caller        │◄──►│   Assistant      │◄──►│   Operators     │
└─────────────────┘    └──────────────────┘    └─────────────────┘
                              │                          │
                              ▼                          ▼
┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   Letta AI      │◄──►│   Backend API    │◄──►│   Real-time     │
│   Memory Agent  │    │   (Node.js)      │    │   WebSocket     │
└─────────────────┘    └──────────────────┘    └─────────────────┘
                              │
                              ▼
                    ┌──────────────────┐
                    │   PostgreSQL     │
                    │   Database       │
                    └──────────────────┘
```

## 🚀 Quick Start

### Prerequisites

- Node.js 18+ and npm
- PostgreSQL 14+
- VAPI account and API key
- Letta account and API key

### 1. Clone and Setup

```bash
# Clone the repository
git clone <repository-url>
cd emergency-triage

# Install backend dependencies
cd backend
npm install

# Install frontend dependencies
cd ../frontend
npm install
```

### 2. Environment Configuration

Create `.env` files for both backend and frontend:

**Backend `.env`:**

```env
DATABASE_URL=postgresql://username:password@localhost:5432/emergency_triage
VAPI_API_KEY=your_vapi_api_key
VAPI_PHONE_NUMBER=your_emergency_number
LETTA_API_KEY=your_letta_api_key
JWT_SECRET=your_jwt_secret
PORT=3001
FRONTEND_URL=http://localhost:3000
```

**Frontend `.env`:**

```env
REACT_APP_API_BASE_URL=http://localhost:3001
REACT_APP_WS_URL=http://localhost:3001
```

### 3. Database Setup

```bash
# Create PostgreSQL database
createdb emergency_triage

# Run setup and test script
npx ts-node setup-and-test.ts
```

### 4. Start the Application

```bash
# Terminal 1: Start backend
cd backend
npm run dev

# Terminal 2: Start frontend
cd frontend
npm start
```

### 5. Access the Dashboard

Open http://localhost:3000 to access the real-time emergency dashboard.

## 📋 Key Features

### Emergency Call Handling

- **Instant Response**: < 3 second call answering
- **AI Triage**: Automated situation assessment
- **Multi-language**: English, Spanish, French support, etc
- **Severity Classification**: CRITICAL, HIGH, MEDIUM, LOW levels
- **Real-time Transcription**: Live speech-to-text processing

### Persistent Memory System

- **Caller Profiles**: Automatic identification via phone number
- **Medical History**: Persistent health condition tracking
- **Emergency History**: Previous call outcomes and patterns
- **Location Data**: GPS coordinates and address storage
- **Risk Assessment**: Dynamic risk profile calculation

### Real-time Dashboard

- **Live Call Monitoring**: Active call status and transcriptions
- **Emergency Alerts**: Critical situation notifications
- **Caller Context**: Historical data and risk profiles
- **Escalation Controls**: Human operator handoff
- **Analytics**: Call volume, response times, outcomes

### Emergency Escalation

- **Auto-escalation**: Based on severity and caller history
- **Human Operators**: Specialized emergency response staff
- **Emergency Services**: Direct 911/emergency services dispatch
- **Supervisor Alerts**: Complex situation management

## 🔧 API Documentation

### Core Endpoints

```bash
# Calls Management
POST   /api/calls/initiate        # Start emergency call
GET    /api/calls/active          # Get active calls
GET    /api/calls/history         # Get call history
POST   /api/calls/escalate        # Escalate to human operator
POST   /api/calls/webhook/vapi    # VAPI webhook handler

# User & Memory Management
GET    /api/users/:id             # Get user profile
POST   /api/users                 # Create user profile
GET    /api/users/:id/memory      # Get caller memory
PUT    /api/users/:id/memory      # Update caller memory

# Dashboard & Analytics
GET    /api/dashboard/stats       # Dashboard statistics
GET    /api/dashboard/active-calls # Active calls for dashboard
GET    /api/dashboard/recent-calls # Recent call history
```

### WebSocket Events

```javascript
// Client-side event listeners
socket.on("newCall", (call) => {
  /* Handle new emergency call */
});
socket.on("callUpdate", (event) => {
  /* Handle call status update */
});
socket.on("callEscalated", (data) => {
  /* Handle escalation */
});
socket.on("emergencyAlert", (alert) => {
  /* Handle critical alert */
});
```

## 🗄️ Database Schema

### Core Tables

```sql
-- User profiles and caller information
users (id, phone_number, risk_profile, preferred_language, created_at)

-- Emergency call records
calls (id, user_id, emergency_type, severity_level, status, transcription, outcome, created_at)

-- Persistent caller memory
memory_records (id, user_id, memory_type, content, created_at, updated_at)

-- Escalation tracking
escalation_log (id, call_id, escalation_type, reason, created_at)

-- Emergency services dispatch
emergency_dispatch_log (id, call_id, service_type, dispatch_time, status)
```

## 🤖 AI Agent Configuration

### Letta Memory Agent

The system uses Letta AI agents for persistent caller memory:

```typescript
// Caller memory structure
interface CallerMemory {
  userId: string;
  phoneNumber: string;
  medicalHistory: string[];
  previousEmergencies: EmergencyRecord[];
  location: LocationData;
  emergencyContacts: Contact[];
  riskProfile: "LOW" | "MEDIUM" | "HIGH" | "CRITICAL";
  lastCallSummary: string;
}
```

### Emergency Assessment Protocol

1. **Immediate Assessment**: Severity classification within 30 seconds
2. **Contextual Analysis**: Historical pattern recognition
3. **Escalation Logic**: Automated decision tree for human handoff
4. **Memory Updates**: Real-time learning from each interaction

## 📞 VAPI Integration

### Voice Assistant Configuration

```javascript
// Emergency triage assistant settings
{
  "name": "Emergency Triage Assistant",
  "model": "gpt-4",
  "voice": "professional-calm",
  "systemMessage": "Emergency triage AI with medical protocols...",
  "functions": ["escalate_to_human", "dispatch_emergency_services"],
  "recordingEnabled": true,
  "maxDurationSeconds": 1800
}
```

### Call Flow Process

1. **Incoming Call**: VAPI answers with emergency greeting
2. **Situation Assessment**: Structured triage questions
3. **Memory Retrieval**: Access caller's historical context
4. **AI Analysis**: Severity determination and guidance
5. **Escalation Decision**: Human/emergency services if needed
6. **Memory Update**: Store call outcomes and learnings

## 🚨 Emergency Response Protocols

### Severity Levels

- **CRITICAL**: Life-threatening (heart attack, stroke, severe bleeding)
- **HIGH**: Serious but stable (broken bones, difficulty breathing)
- **MEDIUM**: Concerning but manageable (moderate injury, infection)
- **LOW**: Minor issues (small cuts, mild symptoms)

### Auto-Escalation Triggers

- Critical severity assessment
- High-risk caller with concerning symptoms
- Extended call duration (>10 minutes)
- Caller becomes unresponsive
- Specific keywords ("can't breathe", "unconscious")

## 📊 Monitoring & Analytics

### Dashboard M

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 12 recognized source files, 87 KB.
- Express (technology) — detected in the code
- JavaScript (language) — detected in the code
- PostgreSQL (technology) — detected in the code
- TypeScript (language) — detected in the code
- React (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (17 of 17)

```
.gitignore
backend/package.json
backend/src/super-simple.js
backend/voice-agent/config/config.js
backend/voice-agent/package.json
backend/voice-agent/server/index.js
backend/voice-agent/start-clean.js
backend/voice-agent/test-optimization.js
backend/voice-agent/test-phone-mapping.js
backend/voice-agent/test-proxy.js
backend/voice-agent/test-race-conditions.js
backend/voice-agent/utils/lettaService.js
backend/voice-agent/utils/vapiService.js
railway.toml
README.md
setup-and-test.ts
vercel.json
```

### Dependencies

- backend/package.json: @types/axios@^0.9.36, @types/bcryptjs@^2.4.6, @types/cors@^2.8.19, @types/express@^5.0.3, @types/jsonwebtoken@^9.0.10, @types/node@^24.0.3, @types/pg@^8.15.4, @types/uuid@^10.0.0, axios@^1.10.0, bcryptjs@^3.0.2, cors@^2.8.5, dotenv@^16.5.0, express@^5.1.0, jsonwebtoken@^9.0.2, nodemon@^3.1.10, pg@^8.16.2, socket.io@^4.8.1, ts-node@^10.9.2, typescript@^5.8.3, uuid@^11.1.0
- backend/voice-agent/package.json: axios@^1.6.0, cors@^2.8.5, dotenv@^16.3.1, express@^4.18.2, node-cron@^3.0.3, nodemon@^3.0.1, uuid@^9.0.1

### Recent commits (newest first)

- Initial commit: Project setup with frontend and backend

## Key source files (fetched from GitHub, selected and truncated for size)

### backend/package.json

```
{
  "name": "backend",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "dev": "node src/super-simple.js",
    "dev-old": "nodemon src/index.ts",
    "build": "tsc",
    "start": "node dist/index.js",
    "setup": "ts-node ../setup-and-test.ts",
    "test": "ts-node src/test/emergency-flow-test.ts",
    "test:emergency-flow": "ts-node src/test/emergency-flow-test.ts"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "@types/axios": "^0.9.36",
    "@types/bcryptjs": "^2.4.6",
    "@types/cors": "^2.8.19",
    "@types/express": "^5.0.3",
    "@types/jsonwebtoken": "^9.0.10",
    "@types/node": "^24.0.3",
    "@types/pg": "^8.15.4",
    "@types/uuid": "^10.0.0",
    "axios": "^1.10.0",
    "bcryptjs": "^3.0.2",
    "cors": "^2.8.5",
    "dotenv": "^16.5.0",
    "express": "^5.1.0",
    "jsonwebtoken": "^9.0.2",
    "nodemon": "^3.1.10",
    "pg": "^8.16.2",
    "socket.io": "^4.8.1",
    "ts-node": "^10.9.2",
    "typescript": "^5.8.3",
    "uuid": "^11.1.0"
  }
}

```

### backend/voice-agent/package.json

```
{
  "name": "riley-voice-agent",
  "version": "1.0.0",
  "description": "VAPI + Letta multi-agent emergency triage system",
  "main": "server/index.js",
  "scripts": {
    "start": "node start-clean.js",
    "start-direct": "node server/index.js", 
    "dev": "nodemon server/index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "express": "^4.18.2",
    "cors": "^2.8.5",
    "dotenv": "^16.3.1",
    "axios": "^1.6.0",
    "uuid": "^9.0.1",
    "node-cron": "^3.0.3"
  },
  "devDependencies": {
    "nodemon": "^3.0.1"
  },
  "keywords": [
    "vapi",
    "letta",
    "emergency",
    "triage",
    "multi-agent"
  ],
  "author": "Riley Emergency System",
  "license": "MIT"
}
```

### backend/voice-agent/server/index.js

```javascript
const express = require("express");
const cors = require("cors");
const config = require("../config/config");
const LettaService = require("../utils/lettaService");
const VAPIService = require("../utils/vapiService");

class VoiceAgentServer {
  constructor() {
    this.app = express();
    this.lettaService = new LettaService();
    this.vapiService = new VAPIService(this.lettaService);

    this.setupMiddleware();
    this.setupRoutes();
  }

  setupMiddleware() {
    // CORS configuration
    this.app.use(
      cors({
        origin: config.server.corsOrigins,
        credentials: true,
      })
    );

    this.app.use(express.json());
    this.app.use(express.urlencoded({ extended: true }));

    // Request logging
    this.app.use((req, res, next) => {
      console.log(`🌐 ${req.method} ${req.path} - ${new Date().toISOString()}`);
      next();
    });
  }

  setupRoutes() {
    // Health check
    this.app.get("/health", (req, res) => {
      const assistantConfig = this.vapiService.getCurrentAssistantConfig();
      res.json({
        status: "Riley Voice Agent System Running",
        timestamp: new Date().toISOString(),
        phoneToAgentMappings: this.lettaService.phoneToAgentMap.size,
        activeCalls: this.vapiService.getActiveCallsStatus().activeCallCount,
        currentAssistantConfig: assistantConfig
      });
    });

    // Update VAPI assistant for a phone number (using existing assistant)
    this.app.post("/api/update-assistant", async (req, res) => {
      try {
        const { assistantId, phoneNumber } = req.body;

        if (!assistantId || !phoneNumber) {
          return res
            .status(400)
            .json({ error: "Assistant ID and phone number required" });
        }

        const updatedAssistantId =
          await this.vapiService.updateAssistantForPhone(
            assistantId,
            phoneNumber
          );
        const lettaWebhookUrl =
          this.lettaService.getLettaWebhookUrl(phoneNumber);

        res.json({
          success: true,
          phoneNumber,
          assistantId: updatedAssistantId,
          lettaWebhookUrl,
        });
      } catch (error) {
        console.error("❌ Failed to update assistant:", error);
        res.status(500).json({
          error: error.message,
          success: false,
        });
      }
    });

    // Letta Proxy endpoint for VAPI calls - handles authentication and conversation saving
    this.app.post('/letta-proxy/:phoneNumber', async (req, res) => {
      try {
        const phoneNumber = decodeURIComponent(req.params.phoneNumber);
        console.log(`🔄 Letta proxy call from VAPI for phone: ${phoneNumber}`);
        console.log(`📋 Request body:`, JSON.stringify(req.body, null, 2));

        // Get the Letta agent for this phone number
        const agentId = this.lettaService.phoneToAgentMap.get(phoneNumber);
        if (!agentId) {
          console.error(`❌ No agent found for phone number ${phoneNumber}`);
          return res.status(404).json({ error: 'Agent not found for this phone number' });
        }

        // If it's a mock agent, return mock response
        if (agentId.startsWith('mock_agent_')) {
          const mockResponse = `Hello! I'm handling your call. This is a mock response for ${phoneNumber}.`;
          return res.json({ message: mockResponse });
        }

        // Extract message from VAPI request body
        const message = req.body.message || req.body.text || '';
        if (!message) {
          console.error(`❌ No message found in VAPI request`);
          return res.status(400).json({ error: 'Message required' });
        }

        // Use our chat method which properly saves conversations
        console.log(`💬 Processing message: "${message}"`);
        const response = await this.lettaService.chatWithAgent(phoneNumber, message, {
          vapiRequest: true,
          requestBody: req.body,
          timestamp: new Date().toISOString()
        });

        console.log(`✅ Letta response received for ${phoneNumber}`);
        
        // Return in format VAPI expects
        res.json({ message: response });

      } catch (error) {
        console.error('❌ Letta proxy error:', error.message);
        if (error.response) {
          console.error(`   Status: ${error.response.status}`);
          console.error(`   Response: ${JSON.stringify(error.response.data, null, 2)}`);
        }
        
        res.status(500).json({ 
          error: error.message,
          message: "I apologize, I'm having technical difficulties. Please repeat your message."
        });
      }
    });

    // VAPI Webhook endpoints

    // Call start webhook
    this.app.post("/webhook/call", async (req, res) => {
      try {
        console.log("📞 Incoming emergency call webhook");
        const result = await this.vapiService.handleCallWebhook(req.body);
        res.json(result);
      } catch (error) {
        console.error("❌ Call webhook error:", error);
        res.status(500).json({ success: false, error: error.message });
      }
    });

    // Call message webhook
    this.app.post("/webhook/call/message", async (req, res) => {
      try {
        const { callId, message } = req.body;
        console.log(`💬 Message from call ${callId}: ${message}`);
        const result = await this.vapiService.handleCallMessage(
          callId,
          message,
          req.body
        );
        res.json(result);
      } catch (error) {
        console.error("❌ Message webhook error:", error);
        res.status(500).json({ success: false, error: error.message });
      }
    });

    // Call end webhook
    this.app.post("/webhook/call/end", async (req, res) => {
      try {
        const { callId } = req.body;
        console.log(`📱 Call ended: ${callId}`);
        const result = await this.vapiService.handleCallEnd(callId, req.body);
        res.json(result);
      } catch (error) {
        console.error("❌ End webhook error:", error);
        res.statu
[truncated — 7722 more characters]
```

### setup-and-test.ts

```typescript
#!/usr/bin/env ts-node

/**
 * Emergency Triage System - Setup and Test Script
 * 
 * This script sets up the database and runs basic tests to ensure
 * the emergency triage system is working correctly.
 */

import { DatabaseService } from './backend/src/services/DatabaseService';
import { runAllTests } from './backend/src/test/emergency-flow-test';
import { readFileSync } from 'fs';
import { join } from 'path';

async function setupDatabase(): Promise<void> {
  console.log('🗄️  Setting up Emergency Triage Database...\n');
  
  const db = DatabaseService.getInstance();
  
  try {
    // Read and execute main schema
    console.log('📋 Creating main database schema...');
    const mainSchema = readFileSync(join(__dirname, 'backend/src/models/database.sql'), 'utf8');
    await db.query(mainSchema);
    console.log('✅ Main schema created\n');
    
    // Read and execute escalation schema
    console.log('🚨 Creating escalation system schema...');
    const escalationSchema = readFileSync(join(__dirname, 'backend/src/models/escalation-schema.sql'), 'utf8');
    await db.query(escalationSchema);
    console.log('✅ Escalation schema created\n');
    
    console.log('🎉 Database setup completed successfully!\n');
    
  } catch (error) {
    console.error('❌ Database setup failed:', error);
    console.log('\n📝 Make sure you have:');
    console.log('  1. PostgreSQL running');
    console.log('  2. Created a database for the emergency triage system');
    console.log('  3. Set DATABASE_URL in your .env file');
    console.log('  4. Database user has CREATE permissions\n');
    throw error;
  }
}

async function testSystemConnections(): Promise<void> {
  console.log('🔗 Testing System Connections...\n');
  
  // Test database connection
  console.log('💾 Testing database connection...');
  try {
    const db = DatabaseService.getInstance();
    const result = await db.query('SELECT NOW() as current_time');
    console.log(`   ✅ Database connected: ${result.rows[0].current_time}\n`);
  } catch (error) {
    console.error('   ❌ Database connection failed:', error);
    throw error;
  }
  
  // Test API keys (non-blocking)
  console.log('🔑 Checking API key configuration...');
  const vapiKey = process.env.VAPI_API_KEY;
  const lettaKey = process.env.LETTA_API_KEY;
  
  console.log(`   VAPI API Key: ${vapiKey ? '✅ Configured' : '⚠️  Not configured'}`);
  console.log(`   Letta API Key: ${lettaKey ? '✅ Configured' : '⚠️  Not configured'}`);
  
  if (!vapiKey || !lettaKey) {
    console.log('   ℹ️  Some API keys are missing. Tests will run with mock services.\n');
  } else {
    console.log('   🎉 All API keys configured!\n');
  }
}

async function displaySystemInfo(): Promise<void> {
  console.log('📊 Emergency Triage System Information\n');
  console.log('🏥 System Components:');
  console.log('   • Backend API (Node.js + Express + TypeScript)');
  console.log('   • Frontend Dashboard (React + TypeScript + Mantine UI)');
  console.log('   • PostgreSQL Database with emergency call schemas');
  console.log('   • WebSocket for real-time dashboard updates');
  console.log('   • VAPI integration for phone call handling');
  console.log('   • Letta AI agents for persistent memory and triage');
  console.log('   • Emergency escalation and notification system\n');
  
  console.log('🚀 Getting Started:');
  console.log('   1. Start the backend: cd backend && npm run dev');
  console.log('   2. Start the frontend: cd frontend && npm start');
  console.log('   3. Open http://localhost:3000 for the dashboard');
  console.log('   4. Configure VAPI phone number for emergency calls\n');
  
  console.log('📋 Key Features:');
  console.log('   • 24/7 emergency call handling with AI triage');
  console.log('   • Persistent caller memory across sessions');
  console.log('   • Real-time dashboard monitoring');
  console.log('   • Automatic escalation to human operators');
  console.log('   • Emergency services integration');
  console.log('   • Multi-language support');
  console.log('   • Risk profile assessment');
  console.log('   • Call history and analytics\n');
}

async function main(): Promise<void> {
  console.log('🚑 Emergency Triage System - Setup & Test\n');
  console.log('=========================================\n');
  
  try {
    // Display system information
    await displaySystemInfo();
    
    // Test connections first
    await testSystemConnections();
    
    // Setup database
    await setupDatabase();
    
    // Run end-to-end tests
    console.log('🧪 Running End-to-End Tests...\n');
    await runAllTests();
    
    console.log('🎉 All tests passed! Emergency Triage System is ready.\n');
    console.log('📞 To test emergency calls:');
    console.log('   1. Configure your VAPI phone number');
    console.log('   2. Call the emergency number');
    console.log('   3. Monitor the dashboard at http://localhost:3000\n');
    
  } catch (error) {
    console.error('\n❌ Setup failed:', error);
    console.log('\n🔧 Troubleshooting:');
    console.log('   1. Check your .env file configuration');
    console.log('   2. Ensure PostgreSQL is running');
    console.log('   3. Verify database permissions');
    console.log('   4. Check API key validity\n');
    process.exit(1);
  }
}

// Run if called directly
if (require.main === module) {
  main().catch(console.error);
}
```

### backend/voice-agent/start-clean.js

```javascript
const { exec } = require('child_process');
const net = require('net');

async function checkPort(port) {
  return new Promise((resolve) => {
    const server = net.createServer();
    server.listen(port, () => {
      server.close(() => resolve(true));
    });
    server.on('error', () => resolve(false));
  });
}

async function killProcessOnPort(port) {
  return new Promise((resolve) => {
    exec(`lsof -ti:${port}`, (error, stdout) => {
      if (stdout) {
        const pids = stdout.trim().split('\n');
        const killPromises = pids.map(pid => {
          return new Promise((res) => {
            exec(`kill -9 ${pid}`, () => res());
          });
        });
        Promise.all(killPromises).then(() => {
          console.log(`🧹 Killed ${pids.length} process(es) on port ${port}`);
          setTimeout(resolve, 1000); // Wait a second for cleanup
        });
      } else {
        resolve();
      }
    });
  });
}

async function startClean() {
  console.log('🚀 Starting Riley Voice Agent System...\n');
  
  const port = 3002;
  
  // Check if port is available
  const isPortFree = await checkPort(port);
  
  if (!isPortFree) {
    console.log(`⚠️ Port ${port} is in use. Cleaning up...`);
    await killProcessOnPort(port);
    
    // Double-check
    const isNowFree = await checkPort(port);
    if (!isNowFree) {
      console.error(`❌ Failed to free port ${port}. Please manually kill processes and try again.`);
      process.exit(1);
    }
  }
  
  console.log(`✅ Port ${port} is available`);
  console.log('🎙️ Starting voice agent server...\n');
  
  // Start the server
  require('./server/index.js');
}

// Handle cleanup on exit
process.on('SIGINT', () => {
  console.log('\n👋 Shutting down gracefully...');
  process.exit(0);
});

process.on('SIGTERM', () => {
  console.log('\n👋 Shutting down gracefully...');
  process.exit(0);
});

startClean().catch(console.error);
```

### backend/voice-agent/test-phone-mapping.js

```javascript
const axios = require('axios');

// Test script for phone number to Letta agent mapping
async function testPhoneMapping() {
  console.log('🧪 Testing Phone Number to Letta Agent Mapping...\n');

  const baseUrl = 'http://localhost:3002';

  try {
    // Test 1: Health check
    console.log('1️⃣ Testing server health...');
    const healthResponse = await axios.get(`${baseUrl}/health`);
    console.log('✅ Server is running');
    console.log(`   Phone to Agent Mappings: ${healthResponse.data.phoneToAgentMappings}`);
    console.log(`   Active Calls: ${healthResponse.data.activeCalls}\n`);

    // Test 2: Test updating VAPI assistant for a phone number
    console.log('2️⃣ Testing VAPI assistant update...');
    const testPhone = '+1555123456';
    const existingAssistantId = 'your-existing-assistant-id'; // Replace with actual ID
    
    const assistantResponse = await axios.post(`${baseUrl}/api/update-assistant`, {
      assistantId: existingAssistantId,
      phoneNumber: testPhone
    });
    
    console.log('✅ VAPI assistant updated successfully');
    console.log(`   Phone Number: ${assistantResponse.data.phoneNumber}`);
    console.log(`   Assistant ID: ${assistantResponse.data.assistantId}`);
    console.log(`   Letta Webhook URL: ${assistantResponse.data.lettaWebhookUrl}\n`);

    // Test 3: Test chat with agent
    console.log('3️⃣ Testing chat with agent...');
    const chatResponse = await axios.post(`${baseUrl}/api/chat`, {
      phoneNumber: testPhone,
      message: 'Hello, I need help with a medical emergency'
    });
    
    console.log('✅ Chat successful');
    console.log(`   Response: ${chatResponse.data.response?.substring(0, 100)}...\n`);

    // Test 4: Test multiple phone numbers (skip assistant update, just test agent creation)
    console.log('4️⃣ Testing multiple phone number agent creation...');
    const phoneNumbers = ['+1555111111', '+1555222222', '+1555333333'];
    
    for (const phone of phoneNumbers) {
      try {
        console.log(`   Creating agent for ${phone}...`);
        const response = await axios.post(`${baseUrl}/api/chat`, {
          phoneNumber: phone,
          message: 'Test agent creation'
        });
        console.log(`   ✅ ${phone}: Agent created and responding`);
      } catch (error) {
        console.log(`   ❌ ${phone}: Failed - ${error.message}`);
      }
    }

    // Test 5: Check all mappings
    console.log('\n5️⃣ Testing mappings overview...');
    const mappingsResponse = await axios.get(`${baseUrl}/api/mappings`);
    console.log('✅ All mappings retrieved');
    console.log(`   Total Mappings: ${mappingsResponse.data.count}`);
    
    mappingsResponse.data.mappings.forEach((mapping, index) => {
      console.log(`   ${index + 1}. ${mapping.phoneNumber}: Agent ${mapping.agentId?.substring(0, 15)}...`);
      console.log(`      Webhook: ${mapping.webhookUrl?.substring(0, 50)}...`);
    });

    console.log('\n🎉 Phone Mapping Test Summary:');
    console.log(`   📊 Total Phone Numbers: ${mappingsResponse.data.count}`);
    console.log(`   📊 Letta Agents Created: ${mappingsResponse.data.count}`);
    console.log(`   📊 Unique Webhook URLs: ${mappingsResponse.data.count}`);
    
    console.log('\n✅ SUCCESS: Phone number to Letta agent mapping is working!');
    console.log('\n💡 Each phone number now has:');
    console.log('   - Dedicated Letta agent with persistent memory');
    console.log('   - Custom webhook URL for VAPI integration');
    console.log('   - Automatic VAPI assistant creation');

  } catch (error) {
    console.error('❌ Test failed:', error.message);
    if (error.response) {
      console.error(`   Status: ${error.response.status}`);
      console.error(`   Response: ${JSON.stringify(error.response.data, null, 2)}`);
    }
    console.log('\n💡 Make sure the voice agent server is running:');
    console.log('   npm start');
  }
}

// Run the test
testPhoneMapping().catch(console.error);
```

### backend/voice-agent/test-proxy.js

```javascript
const axios = require('axios');

// Test script to verify Letta proxy functionality for conversation saving
async function testLettaProxy() {
  console.log('🧪 Testing Letta Proxy for Conversation Saving...\n');

  const baseUrl = 'http://localhost:3002';

  try {
    // Test 1: Health check
    console.log('1️⃣ Testing server health...');
    const healthResponse = await axios.get(`${baseUrl}/health`);
    console.log('✅ Server is running');
    console.log(`   Phone to Agent Mappings: ${healthResponse.data.phoneToAgentMappings}`);
    console.log();

    // Test 2: Test Letta proxy endpoint directly (simulate VAPI call)
    console.log('2️⃣ Testing Letta proxy endpoint (simulating VAPI call)...');
    const testPhone = '+1555123456';
    
    // Simulate what VAPI would send to our proxy
    const vapiPayload = {
      message: 'Hello, I need emergency help with a medical situation',
      timestamp: new Date().toISOString(),
      // Other VAPI fields...
    };

    const proxyResponse = await axios.post(`${baseUrl}/letta-proxy/${encodeURIComponent(testPhone)}`, vapiPayload);
    
    console.log('✅ Letta proxy call successful');
    console.log(`   Response: ${proxyResponse.data.message?.substring(0, 100)}...\n`);

    // Test 3: Test multiple messages to same phone number (conversation continuity)
    console.log('3️⃣ Testing conversation continuity...');
    
    const messages = [
      'This is my first message',
      'This is my second message, do you remember the first?',
      'This is my third message, what did we talk about?'
    ];

    for (let i = 0; i < messages.length; i++) {
      console.log(`   Sending message ${i + 1}...`);
      
      const response = await axios.post(`${baseUrl}/letta-proxy/${encodeURIComponent(testPhone)}`, {
        message: messages[i],
        timestamp: new Date().toISOString()
      });
      
      console.log(`   ✅ Response ${i + 1}: ${response.data.message?.substring(0, 80)}...`);
    }
    console.log();

    // Test 4: Test different phone number (should be separate conversation)
    console.log('4️⃣ Testing separate conversation for different phone number...');
    const testPhone2 = '+1555987654';
    
    const response2 = await axios.post(`${baseUrl}/letta-proxy/${encodeURIComponent(testPhone2)}`, {
      message: 'Hello, this is a different caller',
      timestamp: new Date().toISOString()
    });
    
    console.log('✅ Different phone number handled');
    console.log(`   Response: ${response2.data.message?.substring(0, 100)}...\n`);

    // Test 5: Check agent mappings
    console.log('5️⃣ Checking phone to agent mappings...');
    const mappingsResponse = await axios.get(`${baseUrl}/api/mappings`);
    console.log('✅ All mappings retrieved');
    console.log(`   Total Mappings: ${mappingsResponse.data.count}`);
    
    mappingsResponse.data.mappings.forEach((mapping, index) => {
      console.log(`   ${index + 1}. ${mapping.phoneNumber}:`);
      console.log(`      Agent ID: ${mapping.agentId?.substring(0, 25)}...`);
      console.log(`      Webhook URL: ${mapping.webhookUrl}`);
    });

    console.log('\n🎉 Letta Proxy Test Summary:');
    console.log('   ✅ Proxy endpoint responding correctly');
    console.log('   ✅ Messages being forwarded to Letta agents');
    console.log('   ✅ Separate agents for different phone numbers');
    console.log('   ✅ VAPI → Our Proxy → Letta Agent flow working');
    
    console.log('\n💡 Key Benefits:');
    console.log('   🔐 Authentication: Our proxy handles Letta API authentication');
    console.log('   💾 Conversation Saving: Using regular chat API that saves properly');
    console.log('   📱 Phone-Based Memory: Each phone number gets dedicated agent');
    console.log('   🔄 VAPI Compatible: Responds in format VAPI expects');

    console.log('\n🚀 Next Steps:');
    console.log('   1. Update your VAPI assistant to use these webhook URLs');
    console.log('   2. Make test calls to verify conversation saving');
    console.log('   3. Check Letta dashboard to confirm conversations are persisted');

  } catch (error) {
    console.error('❌ Test failed:', error.message);
    if (error.response) {
      console.error(`   Status: ${error.response.status}`);
      console.error(`   Response: ${JSON.stringify(error.response.data, null, 2)}`);
    }
    console.log('\n💡 Make sure the voice agent server is running:');
    console.log('   npm start');
  }
}

// Run the test
testLettaProxy().catch(console.error);
```

### backend/voice-agent/test-optimization.js

```javascript
const axios = require('axios');

// Test script to verify VAPI assistant update optimization
async function testOptimization() {
  console.log('🧪 Testing VAPI Assistant Update Optimization...\n');

  const baseUrl = 'http://localhost:3002';

  try {
    // Test 1: Health check to see initial state
    console.log('1️⃣ Initial state...');
    let healthResponse = await axios.get(`${baseUrl}/health`);
    console.log('✅ Current assistant config:', healthResponse.data.currentAssistantConfig);
    console.log();

    // Test 2: Simulate call from Phone A
    console.log('2️⃣ Simulating call from +1555111111...');
    const phoneA = '+1555111111';
    
    // This simulates the webhook call that would come from VAPI
    const callAResponse = await axios.post(`${baseUrl}/webhook/call`, {
      message: {
        customer: { number: phoneA },
        call: { id: 'call-123-a' }
      }
    });
    
    console.log('✅ Call A processed:', callAResponse.data.success);
    
    // Check assistant config after first call
    healthResponse = await axios.get(`${baseUrl}/health`);
    console.log('📍 Assistant now configured for:', healthResponse.data.currentAssistantConfig.currentPhoneNumber);
    console.log();

    // Test 3: Simulate another call from SAME phone number
    console.log('3️⃣ Simulating second call from SAME number (+1555111111)...');
    
    const callA2Response = await axios.post(`${baseUrl}/webhook/call`, {
      message: {
        customer: { number: phoneA },
        call: { id: 'call-124-a' }
      }
    });
    
    console.log('✅ Call A2 processed:', callA2Response.data.success);
    console.log('🔍 Should show "already configured" message in logs above');
    console.log();

    // Test 4: Simulate call from DIFFERENT phone number
    console.log('4️⃣ Simulating call from DIFFERENT number (+1555222222)...');
    const phoneB = '+1555222222';
    
    const callBResponse = await axios.post(`${baseUrl}/webhook/call`, {
      message: {
        customer: { number: phoneB },
        call: { id: 'call-125-b' }
      }
    });
    
    console.log('✅ Call B processed:', callBResponse.data.success);
    
    // Check assistant config after phone number change
    healthResponse = await axios.get(`${baseUrl}/health`);
    console.log('📍 Assistant now configured for:', healthResponse.data.currentAssistantConfig.currentPhoneNumber);
    console.log('🔍 Should show "Phone number changed" message in logs above');
    console.log();

    // Test 5: Call back from Phone A (should trigger update again)
    console.log('5️⃣ Calling back from Phone A (+1555111111) - should trigger update...');
    
    const callA3Response = await axios.post(`${baseUrl}/webhook/call`, {
      message: {
        customer: { number: phoneA },
        call: { id: 'call-126-a' }
      }
    });
    
    console.log('✅ Call A3 processed:', callA3Response.data.success);
    
    // Final state
    healthResponse = await axios.get(`${baseUrl}/health`);
    console.log('📍 Assistant finally configured for:', healthResponse.data.currentAssistantConfig.currentPhoneNumber);
    console.log();

    // Test 6: Check all mappings
    console.log('6️⃣ Checking all phone mappings...');
    const mappingsResponse = await axios.get(`${baseUrl}/api/mappings`);
    console.log(`✅ Total phone mappings created: ${mappingsResponse.data.count}`);
    mappingsResponse.data.mappings.forEach((mapping, index) => {
      console.log(`   ${index + 1}. ${mapping.phoneNumber}: ${mapping.agentId?.substring(0, 20)}...`);
    });

    console.log('\n🎉 Optimization Test Summary:');
    console.log('   ✅ VAPI assistant only updated when phone number changes');
    console.log('   ✅ Same phone number calls skip unnecessary updates');
    console.log('   ✅ Different phone numbers trigger appropriate updates');
    console.log('   ✅ Each phone number gets dedicated Letta agent');
    console.log('\n💡 Check the server logs above to see:');
    console.log('   - "Phone number changed" messages when updates happen');
    console.log('   - "already configured" messages when updates are skipped');

  } catch (error) {
    console.error('❌ Test failed:', error.message);
    if (error.response) {
      console.error(`   Status: ${error.response.status}`);
      console.error(`   Response: ${JSON.stringify(error.response.data, null, 2)}`);
    }
    console.log('\n💡 Make sure the voice agent server is running:');
    console.log('   npm start');
  }
}

// Run the test
testOptimization().catch(console.error);
```

### backend/voice-agent/test-race-conditions.js

```javascript
const axios = require('axios');

// Test script to verify race condition fixes for multiple webhook calls
async function testRaceConditions() {
  console.log('🧪 Testing Race Condition Fixes...\n');

  const baseUrl = 'http://localhost:3002';
  const testPhone = '+1555999000';
  const testCallId = 'race-test-call-123';

  try {
    // Clear any existing state
    console.log('🧹 Clearing existing state...');
    await axios.post(`${baseUrl}/api/clear-mappings`);
    await axios.post(`${baseUrl}/api/clear-assistant-config`);
    console.log('✅ State cleared\n');

    // Test 1: Simulate multiple concurrent webhook calls (VAPI sends multiple events)
    console.log('1️⃣ Simulating concurrent webhook calls...');
    
    const webhookPayload = {
      message: {
        customer: { number: testPhone },
        call: { id: testCallId }
      }
    };

    // Launch 5 concurrent webhook calls
    const promises = [];
    for (let i = 0; i < 5; i++) {
      const promise = axios.post(`${baseUrl}/webhook/call`, webhookPayload)
        .then(response => {
          console.log(`   Webhook ${i + 1}: ${response.data.success ? '✅' : '❌'} - ${response.data.message}`);
          return response.data;
        })
        .catch(error => {
          console.log(`   Webhook ${i + 1}: ❌ Error - ${error.message}`);
          return { success: false, error: error.message };
        });
      promises.push(promise);
    }

    const results = await Promise.all(promises);
    console.log('');

    // Check results
    const successCount = results.filter(r => r.success).length;
    const duplicateCount = results.filter(r => r.message?.includes('Duplicate')).length;
    
    console.log(`📊 Results: ${successCount}/${results.length} successful`);
    console.log(`🔄 Duplicates detected: ${duplicateCount}`);
    console.log('');

    // Test 2: Check agent mapping consistency
    console.log('2️⃣ Checking agent mapping consistency...');
    const mappingsResponse = await axios.get(`${baseUrl}/api/mappings`);
    const mappings = mappingsResponse.data.mappings;
    
    const phoneMapping = mappings.find(m => m.phoneNumber === testPhone);
    if (phoneMapping) {
      console.log(`✅ Agent created for ${testPhone}: ${phoneMapping.agentId}`);
      console.log(`📍 Webhook URL: ${phoneMapping.webhookUrl}`);
    } else {
      console.log(`❌ No agent mapping found for ${testPhone}`);
    }
    console.log('');

    // Test 3: Check system status
    console.log('3️⃣ Checking system status...');
    const statusResponse = await axios.get(`${baseUrl}/api/status`);
    const status = statusResponse.data;
    
    console.log(`📱 Phone to Agent Mappings: ${status.services.letta.phoneToAgentMappings}`);
    console.log(`🔧 Assistant Config: Phone=${status.services.vapi.currentAssistantConfig?.currentPhoneNumber || 'None'}`);
    console.log(`📝 Processed Webhooks: ${status.services.vapi.currentAssistantConfig?.processedWebhooksCount || 0}`);
    console.log(`📞 Active Calls: ${status.services.vapi.currentAssistantConfig?.activeCallsCount || 0}`);
    console.log('');

    // Test 4: Test different phone number (should create separate agent)
    console.log('4️⃣ Testing different phone number...');
    const testPhone2 = '+1555999001';
    const testCallId2 = 'race-test-call-456';
    
    const webhook2Payload = {
      message: {
        customer: { number: testPhone2 },
        call: { id: testCallId2 }
      }
    };

    const response2 = await axios.post(`${baseUrl}/webhook/call`, webhook2Payload);
    console.log(`✅ Different phone number processed: ${response2.data.success}`);
    console.log('');

    // Test 5: Final mapping check
    console.log('5️⃣ Final agent mapping verification...');
    const finalMappingsResponse = await axios.get(`${baseUrl}/api/mappings`);
    const finalMappings = finalMappingsResponse.data.mappings;
    
    console.log(`📊 Total Mappings: ${finalMappings.length}`);
    finalMappings.forEach((mapping, index) => {
      console.log(`   ${index + 1}. ${mapping.phoneNumber}:`);
      console.log(`      Agent ID: ${mapping.agentId}`);
      console.log(`      Webhook: ${mapping.webhookUrl.includes('letta.com') ? 'Direct Letta' : 'Proxy'}`);
    });

    console.log('\n🎉 Race Condition Test Summary:');
    console.log('   ✅ Webhook deduplication working');
    console.log('   ✅ Agent creation locking preventing race conditions');  
    console.log('   ✅ Multiple phone numbers handled correctly');
    console.log('   ✅ System state remains consistent');
    
    console.log('\n💡 Key Improvements:');
    console.log('   🔒 Agent creation uses atomic check-and-set with locking');
    console.log('   🔄 Webhook deduplication prevents duplicate processing');
    console.log('   ⏱️ Timeout-based cleanup prevents memory leaks');
    console.log('   🛡️ Fallback agents ensure system stability');

  } catch (error) {
    console.error('❌ Test failed:', error.message);
    if (error.response) {
      console.error(`   Status: ${error.response.status}`);
      console.error(`   Response: ${JSON.stringify(error.response.data, null, 2)}`);
    }
  }
}

// Run the test
testRaceConditions().catch(console.error);
```

[4 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]