# Project export: Guardian

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: Our app uses AI to detect distress in your voice and automatically alerts emergency contacts with your location. It ensures you’re never alone, providing safety and peace of mind when needed.
- Devpost: https://devpost.com/software/guardian-lw1gmt
- GitHub: https://github.com/RandomCyberCoder/Cal_Hacks_2025.git
- Video: https://player.vimeo.com/video/1095430852?byline=0&portrait=0&title=0#t=
- Team: 3 GitHub contributor(s) — Emmanuel Delgado (17 commits), lukeeskinner (16 commits), Judy Phan (11 commits)

## Devpost submission (written by the team)

### Inspiration

Our inspiration for Guardian draws from a blend of security-focused tools, empathetic voice experiences, and minimalist design principles. Visually, we looked to modern safety apps like Citizen and Apple’s Emergency SOS, which use clean interfaces and assertive iconography to build user trust in urgent moments. We also studied voice-based platforms like Google Assistant and Alexa, where calm, conversational tone pairs with structured guidance to support users effectively. Aesthetically, the beige and white color palette is inspired by wellness and journaling apps, evoking a sense of calm while maintaining clarity and purpose. Together, these references shaped our goal: a voice-powered guardian that feels reassuring, intuitive, and always ready to act when safety matters most.

### What it does

Guardian is a voice-activated safety companion designed to help users feel secure in uncertain or potentially dangerous situations. By simply speaking to the app, users can describe their location, surroundings, or concerns, and Guardian will intelligently interpret the conversation to take action such as identifying their location using Google Maps, analyzing risk levels, and offering advice or support. It can also generate a structured report of the interaction, send transcripts via SMS to a trusted contact, or escalate to emergency services if needed. Built for real-time voice interaction, Guardian empowers users with calm, immediate assistance when it matters most.

### How we built it

We leveraged Vapi to power real-time, two-way voice interaction, enabling users to speak naturally with our Guardian bot while it discreetly keeps them safe. During every call, the bot transcribes the conversation, stores time-stamped notes, and taps Google’s Geocoding API to continually update the user’s location. Custom prompts let the assistant sense distress, recognize when the caller can’t speak freely, and even pivot to low-profile cues like “ordering a pizza” to avoid raising suspicion. The frontend is built in React Native for a seamless mobile experience, while a Node.js/Express backend orchestrates Vapi events and persists data in MongoDB. Together, this stack delivers a calm, always-on safety companion that’s both intuitive and technically robust.

### Challenges we ran into

Getting Vapi to respond correctly: As first-time users, understanding how to configure prompts, voice settings, and handle events was a major learning curve. Getting Vapi to respond correctly: As first-time users, understanding how to configure prompts, voice settings, and handle events was a major learning curve. Implementing SMS messaging: We had to figure out how to send transcripts or alerts to phone numbers using APIs. Implementing SMS messaging: We had to figure out how to send transcripts or alerts to phone numbers using APIs. Adding sentiment analysis: Detecting distress, silence, or uncertainty in a user's voice required us to carefully craft prompts and test edge cases. Adding sentiment analysis: Detecting distress, silence, or uncertainty in a user's voice required us to carefully craft prompts and test edge cases. Location tracking with Google Geocoding: Translating spoken addresses into coordinates and updating location data in real time was entirely new to us. Location tracking with Google Geocoding: Translating spoken addresses into coordinates and updating location data in real time was entirely new to us. Summarizing and logging transcriptions: Structuring raw voice data into readable summaries and saving it reliably challenged our backend design. Summarizing and logging transcriptions: Structuring raw voice data into readable summaries and saving it reliably challenged our backend design. Connecting React Native without Expo: Without Expo, we had to manually configure native modules, handle API requests, and manage permissions ourselves. Connecting React Native without Expo: Without Expo, we had to manually configure native modules, handle API requests, and manage permissions ourselves. Securing API access with authorization: Setting up protected routes and environment-based access controls pushed us to think about real-world data security. Securing API access with authorization: Setting up protected routes and environment-based access controls pushed us to think about real-world data security.

### Accomplishments we're proud of

We’re proud to have built a fully functional full-stack application that brings together both frontend and backend components into a seamless experience. Despite being beginners, we successfully integrated technologies like Vapi, React Native, Node.js, MongoDB, and Google’s Geocoding API to create a working safety assistant. The app can place real-time voice calls, record full transcripts, and log each interaction with time-stamped summaries. More importantly, we created an app designed to help people feel safer when walking alone or navigating uncertain environments, offering peace of mind and discreet support in moments when they may feel vulnerable. Seeing all of these systems work together to serve a meaningful purpose was a huge milestone for our team and a reflection of how much we’ve learned throughout the process.

### What we learned

We learned a lot. As a team of beginners, this project challenged us to step far outside our comfort zones. We gained hands-on experience with full-stack development, learned how to integrate real-time voice AI using Vapi, and figured out how to implement features like SMS messaging, location tracking, and sentiment analysis. We also discovered the importance of API security, backend logging, and debugging across multiple layers of an app. Most importantly, we learned how to collaborate, problem-solve, and build something meaningful from the ground up, even when we didn’t always know where to start.

### What's next

SMS Alerts on Activation Automatically notifies a designated emergency contact via text when the app is activated, including a live update and the conversation transcript. SMS Alerts on Activation Automatically notifies a designated emergency contact via text when the app is activated, including a live update and the conversation transcript. Threat Level Detection (1–5) Rates the severity of the situation based on speech analysis. If the risk level reaches 5, the app will escalate appropriately such as contacting emergency services. Threat Level Detection (1–5) Rates the severity of the situation based on speech analysis. If the risk level reaches 5, the app will escalate appropriately such as contacting emergency services. Dynamic Threat Mapping Visualizes historical data to generate a color-coded map of surrounding danger zones, helping users navigate safer areas in real time. Dynamic Threat Mapping Visualizes historical data to generate a color-coded map of surrounding danger zones, helping users navigate safer areas in real time. Crime Rate API Integration Incorporate real-world crime statistics to improve threat assessments and add context to dynamic safety maps. Crime Rate API Integration Incorporate real-world crime statistics to improve threat assessments and add context to dynamic safety maps. AI-Initiated Emergency Response Train the assistant to independently recognize when a user is in danger even if unspoken and determine the best moment to act, including initiating emergency contact without user command. AI-Initiated Emergency Response Train the assistant to independently recognize when a user is in danger even if unspoken and determine the best moment to act, including initiating emergency contact without user command.

## README (from the GitHub repository)

# Cal_Hacks_2025

## Detected evidence (automated analysis)

Indexed codebase: 48 recognized source files, 144 KB.
- Express (technology) — detected in the code
- JavaScript (language) — detected in the code
- MongoDB (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (58 of 58)

```
.gitignore
README.md
workspaces/backend/agent/my_agent.py
workspaces/backend/debug_env.js
workspaces/backend/index.js
workspaces/backend/models/contact.js
workspaces/backend/models/event.js
workspaces/backend/models/user.js
workspaces/backend/models/zone.js
workspaces/backend/package.json
workspaces/backend/README
workspaces/backend/routes/auth.js
workspaces/backend/routes/contacts.js
workspaces/backend/routes/log.js
workspaces/backend/test_all.js
workspaces/backend/test_api_direct.js
workspaces/backend/test_call.js
workspaces/backend/test_raw_api.js
workspaces/backend/test_server.js
workspaces/backend/test_webhook_vapi.js
workspaces/backend/test_webhook.js
workspaces/backend/Vapi/vapi_server
workspaces/frontend_reactNative/my-app/.gitignore
workspaces/frontend_reactNative/my-app/.vscode/settings.json
workspaces/frontend_reactNative/my-app/app.json
workspaces/frontend_reactNative/my-app/app/_layout.tsx
workspaces/frontend_reactNative/my-app/app/(tabs)/_layout.tsx
workspaces/frontend_reactNative/my-app/app/(tabs)/contacts.tsx
workspaces/frontend_reactNative/my-app/app/(tabs)/index.tsx
workspaces/frontend_reactNative/my-app/app/(tabs)/logs.tsx
workspaces/frontend_reactNative/my-app/app/+not-found.tsx
workspaces/frontend_reactNative/my-app/app/auth/login.tsx
workspaces/frontend_reactNative/my-app/app/auth/register.tsx
workspaces/frontend_reactNative/my-app/components/Collapsible.tsx
workspaces/frontend_reactNative/my-app/components/ExternalLink.tsx
workspaces/frontend_reactNative/my-app/components/HapticTab.tsx
workspaces/frontend_reactNative/my-app/components/HelloWave.tsx
workspaces/frontend_reactNative/my-app/components/ParallaxScrollView.tsx
workspaces/frontend_reactNative/my-app/components/ThemedText.tsx
workspaces/frontend_reactNative/my-app/components/ThemedView.tsx
workspaces/frontend_reactNative/my-app/components/ui/IconSymbol.ios.tsx
workspaces/frontend_reactNative/my-app/components/ui/IconSymbol.tsx
workspaces/frontend_reactNative/my-app/components/ui/TabBarBackground.ios.tsx
workspaces/frontend_reactNative/my-app/components/ui/TabBarBackground.tsx
workspaces/frontend_reactNative/my-app/constants/Colors.ts
workspaces/frontend_reactNative/my-app/contexts/AuthContext.tsx
workspaces/frontend_reactNative/my-app/eas.json
workspaces/frontend_reactNative/my-app/eslint.config.js
workspaces/frontend_reactNative/my-app/hooks/useColorScheme.ts
workspaces/frontend_reactNative/my-app/hooks/useColorScheme.web.ts
workspaces/frontend_reactNative/my-app/hooks/useThemeColor.ts
workspaces/frontend_reactNative/my-app/package.json
workspaces/frontend_reactNative/my-app/README_AUTH.md
workspaces/frontend_reactNative/my-app/README.md
workspaces/frontend_reactNative/my-app/scripts/reset-project.js
workspaces/frontend_reactNative/my-app/services/api.ts
workspaces/frontend_reactNative/my-app/services/locationService.ts
workspaces/frontend_reactNative/my-app/tsconfig.json
```

### Dependencies

- workspaces/backend/package.json: @config-plugins/react-native-webrtc@8.0.0, @daily-co/config-plugin-rn-daily-js@0.0.4, @daily-co/react-native-daily-js@0.59.0, @daily-co/react-native-webrtc@118.0.3-daily.1, @react-native-async-storage/async-storage@^1.22.3, @vapi-ai/react-native@^0.1.7, @vapi-ai/server-sdk@^0.9.1, axios@^1.10.0, bcryptjs@^3.0.2, body-parser@^2.2.0, dotenv@^16.5.0, expo@^50.0.0, express@^5.1.0, express-validator@^7.2.1, install@^0.13.0, jsonwebtoken@^9.0.2, mongoose@^8.16.0, nodemon@^3.1.10, npm@^11.4.2, react-native@^0.73.6, react-native-background-timer@^2.4.1, react-native-get-random-values@^1.11.0
- workspaces/frontend_reactNative/my-app/package.json: @babel/core@^7.25.2, @expo/vector-icons@^14.1.0, @react-native-community/cli@latest, @react-navigation/bottom-tabs@^7.3.10, @react-navigation/elements@^2.3.8, @react-navigation/native@^7.1.6, @types/react@~19.0.10, @vapi-ai/react-native@^0.2.4, axios@^1.7.0, eslint@^9.25.0, eslint-config-expo@~9.2.0, expo@~53.0.12, expo-blur@~14.1.5, expo-constants@~17.1.6, expo-font@~13.3.1, expo-haptics@~14.1.4, expo-image@~2.3.0, expo-linear-gradient@~13.0.2, expo-linking@~7.1.5, expo-location@~18.1.5, expo-router@~5.1.0, expo-secure-store@~13.0.2, expo-splash-screen@~0.30.9, expo-status-bar@~2.2.3, expo-symbols@~0.4.5, expo-system-ui@~5.0.9, expo-web-browser@~14.2.0, react@19.0.0, react-dom@19.0.0, react-native@0.79.4, react-native-dotenv@^3.4.11, react-native-gesture-handler@~2.24.0, react-native-reanimated@~3.17.4, react-native-safe-area-context@5.4.0, react-native-screens@~4.11.1, react-native-web@~0.20.0, react-native-webview@13.13.5, typescript@~5.8.3

### Recent commits (newest first)

- final final commit
- final commit
- Button padding fix
- new auth page
- added logic for logging
- made a new assistant that fetches users location + current status and username.
- changed home ui as well as now trying to integrate calling / sms services.
- google maps geolocation API, now need to give this info to VAPI maybe.
- Working on basic log info.
- reading logs from the backend
- adding entires to events/logs
- WORKING FROM FRONTEND
- update vapi 3
- Added contacts page + new theme
- auth added back in
- routing added back in and minor changes
- Merge branch 'main' of https://github.com/RandomCyberCoder/Cal_Hacks_2025
- vapi update2
- minor changes
- changed tabs

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

### workspaces/frontend_reactNative/my-app/README_AUTH.md

```markdown
# Authentication Setup Guide

## Overview
Your React Native app now has a complete authentication system that integrates with your MongoDB backend. Here's how it works:

## Features
- ✅ **JWT-based authentication** with secure token storage
- ✅ **Login & Registration** with form validation
- ✅ **Automatic token refresh** and expiration handling
- ✅ **Protected routes** - redirects to login when not authenticated
- ✅ **Clean, modern UI** with loading states and error handling
- ✅ **MongoDB integration** using your existing user model

## Setup Instructions

### 1. Install Dependencies
```bash
cd workspaces/frontend_reactNative/my-app
npm install
```

### 2. Configure Backend URL
Update the `.env` file with your backend server URL:
```
EXPO_PUBLIC_API_URL=http://localhost:3000
```

### 3. Start Your Backend
Make sure your MongoDB backend is running:
```bash
cd workspaces/backend
npm run dev
```

### 4. Start Your React Native App
```bash
cd workspaces/frontend_reactNative/my-app
npm start
```

## How It Works

### Authentication Flow
1. **App starts** → Checks for existing token
2. **No token** → Redirects to login screen
3. **User logs in** → Token stored securely, redirected to main app
4. **Token expires** → Automatically redirects to login

### API Integration
- **Login**: `POST /auth/login` with `{ userName, password }`
- **Register**: `POST /auth/register` with `{ userName, password }`
- **Profile**: `GET /auth/profile` (requires authentication)

### Screen Structure
```
app/
├── _layout.tsx          # Root layout with AuthProvider
├── (tabs)/
│   └── index.tsx        # Authenticated home screen
└── auth/
    ├── login.tsx        # Login screen
    └── register.tsx     # Registration screen
```

### Key Components
- **AuthProvider**: Manages authentication state
- **AuthWrapper**: Handles navigation based on auth state
- **API Service**: Manages HTTP requests with automatic token attachment

## User Experience

### Login Screen
- Username/password validation
- Loading states during authentication
- Error handling with user-friendly messages
- Link to registration screen

### Registration Screen
- Username validation (3-20 characters)
- Password validation (minimum 6 characters)
- Password confirmation
- Automatic login after successful registration

### Home Screen
- Personalized welcome message
- User profile information
- Logout functionality
- Clean dashboard layout

## Security Features
- Tokens stored in Expo SecureStore (encrypted)
- Automatic token attachment to API requests
- Token expiration handling
- Input validation and sanitization

## Customization

### Styling
All screens use a consistent design system with:
- Modern card layouts
- Consistent color scheme
- Responsive design
- iOS/Android platform adaptations

### API Configuration
Update `services/api.ts` to modify:
- Base URL
- Request/response interceptors
- Error handling logic

### Navigation
Modify `app/_layout.tsx` to change:
- Authentication flow logic
- Loading screen 
[truncated — 1119 more characters]
```

### workspaces/backend/package.json

```
{
  "name": "backend",
  "version": "1.0.0",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node index.js",
    "dev": "nodemon index.js",
    "test_call": "nodemon test_call.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "@config-plugins/react-native-webrtc": "8.0.0",
    "@daily-co/config-plugin-rn-daily-js": "0.0.4",
    "@daily-co/react-native-daily-js": "0.59.0",
    "@daily-co/react-native-webrtc": "118.0.3-daily.1",
    "@react-native-async-storage/async-storage": "^1.22.3",
    "@vapi-ai/react-native": "^0.1.7",
    "@vapi-ai/server-sdk": "^0.9.1",
    "bcryptjs": "^3.0.2",
    "body-parser": "^2.2.0",
    "dotenv": "^16.5.0",
    "express": "^5.1.0",
    "expo": "^50.0.0",
    "express-validator": "^7.2.1",
    "jsonwebtoken": "^9.0.2",
    "mongoose": "^8.16.0",
    "react-native": "^0.73.6",
    "react-native-background-timer": "^2.4.1",
    "react-native-get-random-values": "^1.11.0"
  },
  "devDependencies": {
    "axios": "^1.10.0",
    "install": "^0.13.0",
    "nodemon": "^3.1.10",
    "npm": "^11.4.2"
  }
}

```

### workspaces/frontend_reactNative/my-app/package.json

```
{
  "name": "my-app",
  "main": "expo-router/entry",
  "version": "1.0.0",
  "scripts": {
    "start": "expo start",
    "reset-project": "node ./scripts/reset-project.js",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web",
    "lint": "expo lint"
  },
  "dependencies": {
    "@expo/vector-icons": "^14.1.0",
    "@react-navigation/bottom-tabs": "^7.3.10",
    "@react-navigation/elements": "^2.3.8",
    "@react-navigation/native": "^7.1.6",
    "@vapi-ai/react-native": "^0.2.4",
    "axios": "^1.7.0",
    "expo": "~53.0.12",
    "expo-blur": "~14.1.5",
    "expo-constants": "~17.1.6",
    "expo-font": "~13.3.1",
    "expo-haptics": "~14.1.4",
    "expo-image": "~2.3.0",
    "expo-linear-gradient": "~13.0.2",
    "expo-linking": "~7.1.5",
    "expo-location": "~18.1.5",
    "expo-router": "~5.1.0",
    "expo-secure-store": "~13.0.2",
    "expo-splash-screen": "~0.30.9",
    "expo-status-bar": "~2.2.3",
    "expo-symbols": "~0.4.5",
    "expo-system-ui": "~5.0.9",
    "expo-web-browser": "~14.2.0",
    "react": "19.0.0",
    "react-dom": "19.0.0",
    "react-native": "0.79.4",
    "react-native-dotenv": "^3.4.11",
    "react-native-gesture-handler": "~2.24.0",
    "react-native-reanimated": "~3.17.4",
    "react-native-safe-area-context": "5.4.0",
    "react-native-screens": "~4.11.1",
    "react-native-web": "~0.20.0",
    "react-native-webview": "13.13.5"
  },
  "devDependencies": {
    "@babel/core": "^7.25.2",
    "@types/react": "~19.0.10",
    "eslint": "^9.25.0",
    "eslint-config-expo": "~9.2.0",
    "typescript": "~5.8.3",
    "@react-native-community/cli": "latest"
  },
  "private": true
}

```

### workspaces/backend/index.js

```javascript
import express from "express";
import mongoose from "mongoose";
import dotenv from "dotenv";
import { VapiClient } from "@vapi-ai/server-sdk";
import User from "./models/user.js";
import { authRouter, authenticateToken } from './routes/auth.js';
import { contactsRouter } from './routes/contacts.js';
import { logRouter } from './routes/log.js';

dotenv.config();

const app = express();
const port = process.env.PORT || 3000;

// Initialize Vapi client - use environment variable or fallback
const token = process.env.VAPI_API_KEY || "1746f9c6-f86c-4152-ba5c-f11ee0016d6d";

console.log("Vapi API Key loaded:", token ? "✓" : "✗");

// Different assistant IDs for different call types
const assistants = {
  general: process.env.VAPI_ASSISTANT_GENERAL_ID || "6f62f7f1-7df6-4d57-bc30-4fafbf97506c",
  contact: process.env.VAPI_ASSISTANT_CONTACT_ID || "e7075a78-807e-4081-994f-b22388bceb0e", // your contact assistant
  emergency: process.env.VAPI_ASSISTANT_EMERGENCY_ID || "6f62f7f1-7df6-4d57-bc30-4fafbf97506c", // fallback to general
};

const phoneNumberId = process.env.VAPI_PHONE_NUMBER_ID || "450135d5-e4e1-4018-9b37-595e5e91dbc0";

console.log("Assistant IDs:", assistants);
console.log("Phone Number ID:", phoneNumberId);
// Middleware
app.use(express.json());

// CORS configuration for React Native app
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
  
  if (req.method === 'OPTIONS') {
    res.sendStatus(200);
  } else {
    next();
  }
});

mongoose.connect(process.env.MONGODB_URL)
.then(() => console.log('Connected to MongoDB'))
.catch(err => console.error('MongoDB connection error:', err));

app.use('/auth', authRouter);
app.use('/contacts', contactsRouter);
app.use('/logs', logRouter);

app.get('/protected', authenticateToken, (req, res) => {
  res.json({ 
    message: 'This is a protected route',
    user: req.user
  });
});

// Basic server setup
app.get("/", (req, res) => {
  res.send("Vapi Backend Server is running!");
});

// In-memory storage for call tracking (in production, use Redis or database)
const callUserMap = new Map();

// Test endpoint to make a call with assistant type and location
app.post("/make-call", authenticateToken, async (req, res) => {
  try {
    const { phoneNumber, callType = 'general', location, contactName } = req.body;
    const userId = req.user.userId; // Get user ID from auth token

    // Fetch actual user data from database
    const user = await User.findById(userId);
    if (!user) {
      return res.status(404).json({
        success: false,
        error: "User not found"
      });
    }

    // Select appropriate assistant based on call type
    const assistantId = assistants[callType] || assistants.general;
    
    console.log("=== INCOMING CALL REQUEST ===");
    console.log("Making call:", {
      phoneNumber: phoneNumber || "+13052408589",
      callType,
      assistantId,
      userName: user.userName,
      location: location ? `${location.latitude}, ${location.longitude}` : 'Not provided',
      contactName,
      userId
    });
    console.log("Raw request body:", JSON.stringify(req.body, null, 2));

    const vapi = new VapiClient({
      token,
    });

    // Prepare call data
    const callData = {
      assistantId,
      phoneNumberId,
      customer: {
        number: phoneNumber || "+13052408589",
        name: user.userName, // Pass user's actual name
      },
    };

    // Add metadata with user context, location and call context
    callData.metadata = {
      userName: user.userName,
      userId: userId,
      callType: callType
    };
    
    if (location) {
      callData.metadata.userLocation = {
        latitude: location.latitude,
        longitude: location.longitude,
        address: location.address || 'Address not available'
      };
    }
    
    if (contactName) {
      callData.metadata.contactName = contactName;
    }
    
    if (callType === 'emergency') {
      callData.metadata.emergencyContext = 'User initiated emergency call through mobile app';
    }

    // Add assistant overrides with context variables
    callData.assistantOverrides = {
      variableValues: {
        user_name: user.userName,
        user_location: location ? location.address || `${location.latitude}, ${location.longitude}` : 'Location not available',
        call_type: callType,
        contact_name: contactName || 'N/A'
      }
    };

    console.log("=== VAPI CALL DATA ===");
    console.log(JSON.stringify(callData, null, 2));

    const call = await vapi.calls.create(callData);

    // Store the mapping of call ID to user ID
    callUserMap.set(call.id, userId);
    console.log("Call tracking stored:", call.id, "->", userId);

    console.log("=== VAPI RESPONSE ===");
    console.log("Call response:", call);
    
    res.json({
      success: true,
      callId: call.id,
      assistantType: callType || 'general',
      assistantId: assistantId,
      userName: user.userName,
      userLocation: location?.address || 'Location not shared',
      message: "Call initiated successfully",
    });
  } catch (error) {
    console.error("=== CALL ERROR ===");
    console.error("Error making call:", error);
    console.error("Error details:", error.response?.data || error.message);
    
    res.status(500).json({
      success: false,
      error: error.response?.data || error.message,
    });
  }
});

// Webhook endpoint for Guardian Alert
app.post("/webhook", (req, res) => {
  try {
    console.log("Guardian Alert webhook received:", req.body);

    // Log the alert data
    const alertData = req.body;
    console.log("Alert Type:", alertData.type);
    console.log("Alert Data:", JSON.stringify(alertData, null, 2));

    // Respond to acknowledge receipt
    res.status(200).json({
      success: true,
      message: "We
[truncated — 3657 more characters]
```

### workspaces/frontend_reactNative/my-app/app/_layout.tsx

```typescript
import React from 'react';
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
import { useFonts } from 'expo-font';
import { Stack } from 'expo-router';
import { StatusBar } from 'expo-status-bar';
import { View, ActivityIndicator } from 'react-native';
import 'react-native-reanimated';

import { useColorScheme } from '@/hooks/useColorScheme';
import { AuthProvider, useAuth } from '@/contexts/AuthContext';
import { useRouter, useSegments } from 'expo-router';
import { useEffect } from 'react';

function AuthWrapper({ children }: { children: React.ReactNode }) {
  const { isAuthenticated, isLoading } = useAuth();
  const router = useRouter();
  const segments = useSegments();

  useEffect(() => {
    if (isLoading) return; // Still checking auth state

    const inAuthGroup = segments[0] === 'auth';

    if (!isAuthenticated && !inAuthGroup) {
      // User is not authenticated and not in auth group, redirect to login
      router.replace('/auth/login');
    } else if (isAuthenticated && inAuthGroup) {
      // User is authenticated and in auth group, redirect to main app
      router.replace('/(tabs)');
    }
  }, [isAuthenticated, isLoading, segments]);

  if (isLoading) {
    return (
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <ActivityIndicator size="large" />
      </View>
    );
  }

  return <>{children}</>;
}

export default function RootLayout() {
  const colorScheme = useColorScheme();
  const [loaded] = useFonts({
    SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
  });

  if (!loaded) {
    // Async font loading only occurs in development.
    return null;
  }

  return (
    <AuthProvider>
    <ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
        <AuthWrapper>
      <Stack>
        <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
            <Stack.Screen name="auth/login" options={{ headerShown: false }} />
            <Stack.Screen name="auth/register" options={{ headerShown: false }} />
        <Stack.Screen name="+not-found" />
      </Stack>
        </AuthWrapper>
      <StatusBar style="auto" />
    </ThemeProvider>
    </AuthProvider>
  );
}

```

### workspaces/frontend_reactNative/my-app/app/(tabs)/_layout.tsx

```typescript
import { Tabs } from 'expo-router';
import React from 'react';
import { Platform } from 'react-native';

import { HapticTab } from '@/components/HapticTab';
import { IconSymbol } from '@/components/ui/IconSymbol';
import TabBarBackground from '@/components/ui/TabBarBackground';
import { Colors } from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useColorScheme';
import {Octicons, MaterialIcons, MaterialCommunityIcons} from '@expo/vector-icons';

export default function TabLayout() {
  const colorScheme = useColorScheme();

  return (
    <Tabs
      screenOptions={{
        tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
        headerShown: false,
        tabBarButton: HapticTab,
        tabBarBackground: TabBarBackground,
        tabBarStyle: Platform.select({
          ios: {
            // Use a transparent background on iOS to show the blur effect
            position: 'absolute',
          },
          default: {},
        }),
      }}>
      <Tabs.Screen
        name="index"
        options={{
          title: 'Dashboard',
          tabBarIcon: ({ color }) => <MaterialCommunityIcons name="tablet-dashboard" size={24} color={color} />,
        }}
      />
      <Tabs.Screen
        name="contacts"
        options={{
          title: 'Contacts',
          tabBarIcon: ({ color }) => <MaterialIcons name="contact-emergency" size={24} color={color} />,
        }}
      />
      <Tabs.Screen
        name="logs"
        options={{
          title: "Logs",
          tabBarIcon: ({ color }) => <Octicons name="log" size={24} color={color} />, 
        }}
      />
    </Tabs>
  );
}

```

### workspaces/frontend_reactNative/my-app/app/(tabs)/index.tsx

```typescript
import React, { useState, useEffect } from 'react';
import { View, StyleSheet, TouchableOpacity, Text, Alert, ActivityIndicator, ScrollView, SafeAreaView, Modal, FlatList } from 'react-native';
import { ThemedText } from '@/components/ThemedText';
import { ThemedView } from '@/components/ThemedView';
import { useAuth } from '@/contexts/AuthContext';
import { Ionicons } from '@expo/vector-icons';
import { vapiAPI, logsAPI, contactsAPI } from '@/services/api';
import { locationService } from '@/services/locationService';
import { useRouter } from 'expo-router';

interface LogEntry {
  _id: string;
  timestamp: string;
  Note: string;
  location: {
    type: string;
    coordinates: [number, number];
  };
}

interface Contact {
  _id: string;
  name: string;
  phoneNumber: string;
  email?: string;
  avatar: string;
}

export default function HomeScreen() {
  const { user, logout } = useAuth();
  const router = useRouter();
  const [isCallLoading, setIsCallLoading] = useState(false);
  const [recentLogs, setRecentLogs] = useState<LogEntry[]>([]);
  const [contacts, setContacts] = useState<Contact[]>([]);
  const [contactsCount, setContactsCount] = useState(0);
  const [loading, setLoading] = useState(true);
  const [showContactModal, setShowContactModal] = useState(false);
  const [userLocation, setUserLocation] = useState<{latitude: number; longitude: number; address?: string} | null>(null);
  const [currentCallId, setCurrentCallId] = useState<string | null>(null);

  const VAPI_API_KEY = process.env.EXPO_PUBLIC_VAPI_API_KEY;

  useEffect(() => {
    loadDashboardData();
    getCurrentLocation();
  }, []); 

  const getCurrentLocation = async () => {
    try {
      const location = await locationService.getCurrentLocation();
      if (location) {
        // Get address for better context
        const address = await locationService.reverseGeocode(location.latitude, location.longitude);
        setUserLocation({
          latitude: location.latitude,
          longitude: location.longitude,
          address: address?.formattedAddress
        });
        console.log('Location obtained:', JSON.stringify({location, address: address?.formattedAddress}, null, 2));
      }
    } catch (error) {
      console.error('Location error:', JSON.stringify(error, null, 2));
    }
  };

  const loadDashboardData = async () => {
    try {
      setLoading(true);
      // Load recent logs
      const logsResponse = await logsAPI.getLogs();
      if (logsResponse.success) {
        setRecentLogs(logsResponse.events.slice(0, 3)); // Get last 3 logs
      }

      // Load contacts count and full contacts list
      const contactsResponse = await contactsAPI.getContacts();
      if (contactsResponse.success) {
        setContacts(contactsResponse.contacts);
        setContactsCount(contactsResponse.contacts.length);
      }
    } catch (error) {
      console.error('Dashboard data error:', JSON.stringify(error, null, 2));
    } finally {
      setLoading(false);
    }
  };

  const handleLogout = () => {
    Alert.alert(
      'Logout',
      'Are you sure you want to logout?',
      [
        { text: 'Cancel', style: 'cancel' },
        { text: 'Logout', style: 'destructive', onPress: logout },
      ]
    );
  };

  const handleMakeCall = async (
    phoneNumber?: string, 
    contactName?: string, 
    callType: 'general' | 'contact' | 'emergency' = 'general'
  ) => {
    const displayNumber = phoneNumber || '+13052408589';
    let callTypeDisplay = 'Assistant';
    
    switch(callType) {
      case 'contact':
        callTypeDisplay = contactName || 'Contact';
        break;
      case 'emergency':
        callTypeDisplay = '911 Emergency';
        break;
      case 'general':
      default:
        callTypeDisplay = 'Assistant';
    }
    
    Alert.alert(
      `Call ${callTypeDisplay}`,
      `Are you sure you want to make a call to ${displayNumber}?${userLocation ? '\n\n📍 Your location will be shared for assistance.' : ''}`,
      [
        { text: 'Cancel', style: 'cancel' },
        { text: 'Call Now', style: 'default', onPress: async () => {
          try {
            setIsCallLoading(true);
            
            // Get fresh location if not available
            let currentLocation = userLocation;
            if (!currentLocation) {
              const location = await locationService.getCurrentLocation();
              if (location) {
                const address = await locationService.reverseGeocode(location.latitude, location.longitude);
                currentLocation = {
                  latitude: location.latitude,
                  longitude: location.longitude,
                  address: address?.formattedAddress
                };
                setUserLocation(currentLocation);
              }
            }
            
            const response = await vapiAPI.makeCall(
              phoneNumber,
              callType,
              currentLocation || undefined,
              contactName
            );
            
            if (response.success) {
              console.log("📞 Call Response:", JSON.stringify(response, null, 2));
              
              // Store the call ID for later summary fetching
              setCurrentCallId(response.callId);
              
              Alert.alert(
                'Call Started', 
                `Call initiated successfully!\nCall ID: ${response.callId}\nAssistant Type: ${response.assistantType}${currentLocation ? `\nLocation: ${currentLocation.address || 'Coordinates shared'}` : ''}`,
                [
                  { 
                    text: 'Call Completed', 
                    onPress: async () => {
                      console.log('🔍 Fetching call summary...');
                      console.log('🔑 VAPI_API_KEY:', VAPI_API_KEY ? 'Present' : 'MISSING!');
                      
                      if (!VAPI_API_KEY) {
                        Alert.alert('Error', 'VAPI API key is missing. Please add EXPO_PUBLIC_V
[truncated — 24397 more characters]
```

### workspaces/backend/test_server.js

```javascript
// Minimal test server to check webhook
import express from "express";

const app = express();
const port = 3001; // Use different port

app.use(express.json());

app.get("/", (req, res) => {
  res.send("Test server running!");
});

app.post("/webhook", (req, res) => {
  console.log("🔔 Webhook received:", req.body);
  res.json({ message: "Webhook test successful!" });
});

app.listen(port, () => {
  console.log(`🧪 Test server running on http://localhost:${port}`);
});

```

### workspaces/backend/test_call.js

```javascript
// Simple test script to make a Vapi call
// Run with: node test_call.js

import fetch from "node-fetch";

const makeTestCall = async () => {
  try {
    const response = await fetch("http://localhost:3000/make-call", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        phoneNumber: "+13052408589", // Valid E.164 format phone number
      }),
    });

    const result = await response.json();
    console.log("📞 Call Result:", result);

    if (result.success) {
      console.log(`✅ Call initiated successfully! Call ID: ${result.callId}`);
    } else {
      console.log("❌ Call failed:", result.error);
    }
  } catch (error) {
    console.error("❌ Error making request:", error.message);
    console.log("💡 Make sure your server is running with: npm run dev");
  }
};

makeTestCall();

```

### workspaces/backend/test_webhook.js

```javascript
// Test the Guardian Alert webhook directly
// Run with: node test_webhook.js

const testWebhook = async () => {
  try {
    const webhookData = {
      message: {
        function: {
          name: "Guardian_Alert_System",
        },
        conditions: [
          { param: "userSilentTooLong", value: "true", operator: "eq" },
          { param: "userInDistress", value: "true", operator: "eq" },
        ],
      },
      metadata: {
        from: "+19167087169",
      },
    };

    console.log("🧪 Testing Guardian Alert webhook...");
    console.log(
      "📤 Sending webhook data:",
      JSON.stringify(webhookData, null, 2)
    );

    const response = await fetch("http://localhost:3000/webhook", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(webhookData),
    });

    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }

    const result = await response.json();
    console.log("🚨 Guardian Alert Test Result:", result);
  } catch (error) {
    console.error("❌ Webhook test error:", error.message);
    console.log("💡 Make sure your server is running");
  }
};

testWebhook();

```

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