# Project export: BigBro

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: Cal Hacks 12.0
- Tagline: An unhinged productivity companion
- Devpost: https://devpost.com/software/big-bro
- GitHub: https://github.com/smritirangarajan/bigbro
- Team: 2 GitHub contributor(s) — smriti (44 commits), Jamin Xie (8 commits)

## Devpost submission (written by the team)

### Inspiration

We've all been there - opening our laptop with the best intentions to study or work, only to find ourselves two hours deep into YouTube, Reddit, or endlessly scrolling social media. Traditional productivity apps rely on self-reporting or simple website blockers that are easy to bypass. We wanted to create something different: a system that truly knows when you're slacking off and holds you accountable in ways you can't ignore. The idea came from a simple truth: we're accountable to others in ways we aren't to ourselves. If someone else knows you're wasting time, you're far more likely to get back on track. BigBro brings that external accountability into the digital workspace, using cutting-edge AI and computer vision to monitor both what you're doing and whether you're actually paying attention.

### What it does

BigBro is a comprehensive productivity monitoring system that watches you work in two powerful ways: Task Monitoring: BigBro continuously captures screenshots of your current browser tab and sends it to Claude AI along with the URL and your stated task. Claude analyzes whether you're actually working toward your goal or if you've drifted off-task. If you're caught slacking for more than a few seconds, you accumulate strikes. Attention Monitoring: Using your webcam, BigBro tracks your face in real-time using MediaPipe and OpenCV. It detects three critical states: Sleeping: If your eyes close for more than a few seconds, an aggressive AI-generated wake-up message plays through Fish Audio's text-to-speech Looking Away: If you turn your head away from the screen for more than a few seconds, you get a strike Not Present: If you leave your desk for more than a few seconds, BigBro calls your phone via Vapi to bring you back The Strike System: Accumulate 2 strikes, and BigBro escalates. It automatically calls your accountability partner (yes, your mom) via Vapi's phone API to report that you're off-task. This creates real consequences that can't be dismissed with a click. Analytics Dashboard: A web interface shows your productivity stats, strike history, and session summaries, helping you understand your focus patterns over time.

### How we built it

BigBro combines multiple cutting-edge technologies into a cohesive monitoring pipeline: Chrome Extension (Frontend) Built with Manifest V3 to access browser tabs and capture screenshots Background service orchestrates tab monitoring and coordinates with the vision server Popup interface for task setting and real-time status viewing Vision Analysis System (Python/Flask) Flask server running on port 8080 handles all computer vision processing MediaPipe Face Mesh detects 468 3D facial landmarks in real-time OpenCV solves the Perspective-n-Point problem to calculate precise head orientation Takes 6 key landmarks from MediaPipe (nose, chin, eyes, mouth) Maps 2D pixel coordinates to a 3D face model Computes yaw and pitch angles using Rodrigues transformation Takes 6 key landmarks from MediaPipe (nose, chin, eyes, mouth) Maps 2D pixel coordinates to a 3D face model Computes yaw and pitch angles using Rodrigues transformation Eye Aspect Ratio (EAR) calculated from MediaPipe landmarks to detect eye closure State machine with four states: focused, sleeping, looking_away, not_present AI Integration Claude (Anthropic): Primary AI for sophisticated task analysis, handles nuanced "on-task" vs "off-task" decisions Alert Systems Fish Audio: Text-to-speech for immediate local wake-up calls Vapi: Phone call automation that contacts accountability partners with actual phone calls Data Layer Supabase (PostgreSQL): Real-time database storing user settings, strikes, sessions, and events REST API for seamless integration across extension, vision server, and dashboard Row-level security for data protection

### Challenges we ran into

3D Head Pose Estimation: Getting accurate head orientation from webcam footage was far more complex than expected. We needed to understand the mathematical relationship between MediaPipe's 2D landmark coordinates and 3D head rotation. After researching computer vision literature, we implemented OpenCV's solvePnP (Perspective-n-Point) algorithm, which solves for camera pose given corresponding 3D-2D point pairs. The Rodrigues transformation then converts the rotation vector into interpretable yaw/pitch angles. State Machine Priority Logic: Users can be in multiple "bad" states simultaneously (e.g., sleeping with head tilted down looks like both sleeping AND looking away). We had to implement a priority system where sleeping takes precedence over looking away, and each state transition properly resets timers for other states to prevent duplicate alerts. Real-time Performance: Using MediaPipe face detection and OpenCV's 3D geometry calculations, we optimized to ~2 FPS processing rate and ensured the entire pipeline from camera capture to state determination completes in under 500ms. MediaPipe's CPU-only operation was crucial for accessibility. AI Prompt Engineering: Getting Claude to reliably classify "on-task" vs "off-task" behavior required extensive prompt refinement. We needed it to understand nuanced scenarios - is reading documentation on-task for a programming project? Is watching a tutorial video productive or procrastination? The context of URL + screenshot + stated task was critical. Chrome Extension Manifest V3 Migration: Google's transition to Manifest V3 required reworking background processes as service workers, which have different lifecycle management than persistent background pages. Coordinating screenshot capture, API calls, and strike tracking within these constraints required careful architectural planning.

### Accomplishments we're proud of

The MediaPipe + OpenCV Integration: We successfully combined two powerful computer vision libraries in a way that leverages each one's strengths. MediaPipe provides fast, accurate landmark detection, while OpenCV handles the complex 3D geometry mathematics. The result is precise head pose estimation that runs in real-time on standard hardware. Multi-Tier Accountability System: We built escalating consequences that actually work. Starting with gentle notifications, progressing to database-logged strikes, and culminating in real phone calls creates genuine accountability. The sleep detection system bypasses everything for immediate intervention - we're particularly proud of how aggressive and effective those AI-generated wake-up messages are. Seamless Cross-Component Integration: Getting the Chrome extension, Python vision server, and web dashboard to work together through Supabase was a major achievement. All three components stay synchronized, with features like "dashboard starts monitoring → extension shows active status → vision server begins analysis" working flawlessly. Real-World Impact: Early testing showed this actually works. The combination of AI task analysis and attention monitoring catches procrastination that traditional tools miss. You can't fool BigBro - it knows when you're actually working.

### What we learned

Computer Vision is Powerful: We gained deep understanding of facial landmark detection, 3D geometry transformations, and real-time video processing. The math behind head pose estimation (solvePnP, Rodrigues transformations, Euler angles) was fascinating and gave us new appreciation for the complexity behind "simple" face tracking. State Machines for Complex Logic: Implementing the priority-based state machine taught us how to handle overlapping conditions elegantly. The clear priority hierarchy (sleeping > looking_away > focused > not_present) prevents edge cases and ensures consistent behavior. Accountability Works: The most important lesson was behavioral: external accountability is incredibly powerful. When consequences involve other people (phone calls to mom), users actually modify their behavior. Technology motivates if designed correctly. API Integration Complexity: Coordinating multiple external services (Claude, Fish Audio, Vapi, Supabase) taught us about error handling, fallback strategies, and managing API rate limits. Each service has different authentication, request formats, and response structures.

### What's next

Adaptive Threshold Tuning: Use Letta AI's context memory to learn individual user patterns and automatically adjust thresholds. Some users naturally look away more while thinking; others have different baseline eye closure patterns. Personalized thresholds would reduce false positives. Mobile Companion App: Extend monitoring to mobile devices. When BigBro detects you're away from your desk, track what you're doing on your phone. Are you answering work emails or doom-scrolling Instagram? Team Accountability Features: Allow groups to monitor each other. Study groups could see collective productivity stats, and the system could call the most productive member when someone slacks off - peer pressure at scale. Sentiment Analysis: Integrate facial expression analysis to detect frustration, confusion, or boredom. When users appear stuck, BigBro could proactively suggest breaks or offer help. BigBro represents a new paradigm in productivity tools: not just tracking what you do, but truly understanding whether you're focused and holding you accountable in ways that actually change behavior.

## README (from the GitHub repository)

# BigBro

![Demo](assets/demo.png)

A Chrome extension, web app, and Python vision server that work together to monitor your focus. It analyzes your on-screen activity with AI and your attention via webcam + computer vision, escalating consequences (strikes, calls) when you drift off-task.

## Getting Started

### Prerequisites
- Google Chrome
- Python 3.10+
- A Supabase project (URL + anon key)
- Relevant API keys (Claude required; optional services enable extras)

### 1) Configure keys and environment
- Copy templates and fill values:
  - `cp config_template.js config.js`
  - `cp webapp/config_template.js webapp/config.js`
- Copy the environment example, then edit your values:
  - `cp .env.example .env`
- See [SECURITY.md](SECURITY.md) for sensitive file handling.

### 2) Start the vision server (Python)
```bash
cd vision
python -m venv venv
# macOS/Linux
source venv/bin/activate
# Windows PowerShell
venv\Scripts\Activate.ps1

pip install -r requirements.txt
python vision_server.py
```
- Verify: open http://localhost:8080/status  
- Server entry: [vision/vision_server.py](vision/vision_server.py)

### 3) Run the web app (static)
```bash
cd webapp
python -m http.server 8000
# open:
# http://localhost:8000
```
- Main entry: [webapp/index.html](webapp/index.html)

### 4) Load the Chrome extension
- Chrome → chrome://extensions → enable Developer mode → Load unpacked → select the project folder.
- Ensure config.js exists (from step 1).

### Typical flow
1. Start the vision server.
2. Open the web app at http://localhost:8000, sign up/in, save phone numbers (Settings), click “Start Session”.
3. Open the extension popup, set your task, and start monitoring.

### Troubleshooting
- Vision server: check http://localhost:8080/status and the terminal running [vision/vision_server.py](vision/vision_server.py)
- Camera permissions: grant OS/browser access
- Calls not triggering: verify keys in config.js and .env; ensure phone numbers are saved in the web app
- Security tips and env keys: see [SECURITY.md](SECURITY.md)

---

## About the Project

### Inspiration
We’ve all been there—opening a laptop to work, only to end up on YouTube, Reddit, or social media. Traditional blockers are easy to bypass. BigBro brings external accountability into your workflow with AI and computer vision—so you stay focused.

### What it does
- Task Monitoring
  - The extension captures a tab screenshot plus URL and stated task, then asks Claude if you’re on-task. Strikes accumulate while off-task.
- Attention Monitoring
  - The Python server tracks facial landmarks (MediaPipe) and head pose (OpenCV solvePnP) to detect:
    - Sleeping: sustained eye closure triggers an AI-generated TTS wake-up (Fish Audio)
    - Looking Away: sustained yaw/pitch deviation → strike
    - Not Present: extended absence triggers a phone call via Vapi
- Strike System
  - Two strikes escalate to calling your accountability partner via Vapi.
- Analytics Dashboard
  - The web app shows productivity stats and session history (Supabase-backed).

### How we built it
- Chrome Extension (Frontend)
  - Manifest V3 service worker orchestrates monitoring and communicates with the vision server; popup for task and controls.
- Vision Analysis System (Python/Flask)
  - Server at port 8080: [vision/vision_server.py](vision/vision_server.py)
  - MediaPipe Face Mesh (468 landmarks) + OpenCV solvePnP/Rodrigues for yaw/pitch; EAR for eye closure
  - Four-state machine: focused, sleeping, looking_away, not_present
- AI Integration
  - Claude (Anthropic) for on-task classification and feedback
- Alert Systems
  - Fish Audio for local TTS; Vapi for real phone calls
- Data Layer
  - Supabase (PostgreSQL) for auth, settings, strikes, sessions, and events

### Challenges we ran into
- 3D head pose estimation: mapping 2D landmarks to stable 3D pose with solvePnP/Rodrigues
- Priority logic for overlapping states (sleeping > looking_away) and timer resets
- Real-time performance: CPU-only pipeline with sub-500ms end-to-end latency
- Prompt engineering for nuanced “on-task/off-task” calls
- MV3 service worker lifecycle constraints

### Accomplishments
- Reliable MediaPipe + OpenCV integration for real-time head pose
- Multi-tier accountability: notifications → strikes → phone calls
- Seamless cross-component integration via Supabase and a clean server API
- Real-world effectiveness in preventing procrastination

### What we learned
- Facial landmarks, solvePnP, Rodrigues, Euler angles, EAR
- Practical state machines for overlapping detection
- Accountability design changes behavior
- Multi-API integration with error handling and rate limits

### What’s next
- Adaptive thresholds per user (context memory) to reduce false positives
- Mobile companion app for away-from-desk tracking
- Team accountability and shared stats
- Sentiment/expression analysis for proactive assistance


## Detected evidence (automated analysis)

Indexed codebase: 32 recognized source files, 263 KB.
- Anthropic (technology) — detected in the code
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- Flask (technology) — detected in the code
- HTML (language) — detected in the code
- Hugging Face (technology) — detected in the code
- JavaScript (language) — detected in the code
- OpenAI (technology) — detected in the code
- Python (language) — detected in the code
- PyTorch (technology) — detected in the code
- SQL (language) — detected in the code
- Streamlit (technology) — detected in the code
- Supabase (technology) — detected in the code
- TensorFlow (technology) — detected in the code

## Codebase structure (from repository index)

### Files (41 of 41)

```
.env.example
.gitignore
assets/PROJECT_DESCRIPTION.md
background.js
config_template.js
content.js
events.jsonl
manifest.json
options.html
options.js
package.json
popup.css
popup.html
popup.js
popup.js.backup
README.md
requirements.txt
SECURITY.md
setup.sh
supabase-ext.js
vision/attention_monitor.py
vision/attention_monitor/__init__.py
vision/attention_monitor/analyzer.py
vision/attention_monitor/audio.py
vision/attention_monitor/configuration.py
vision/attention_monitor/logging_utils.py
vision/attention_monitor/notifications.py
vision/attention_monitor/pipeline.py
vision/events.jsonl
vision/main.py
vision/requirements.txt
vision/vision_server.py
webapp/app.js
webapp/config_template.js
webapp/index.html
webapp/styles.css
webapp/webapp/app.js
webapp/webapp/README.md
webapp/webapp/supabase_functions.sql
webcam-monitor.html
webcam-monitor.js
```

### Dependencies

- package.json: @letta-ai/letta-client@^0.0.68665, @supabase/supabase-js@^2.76.1
- requirements.txt: absl-py@==2.3.1, aiohappyeyeballs@==2.6.1, aiohttp@==3.12.15, aiosignal@==1.4.0, altair@==5.5.0, altex@==0.2.0, annotated-types@==0.7.0, ansicolors@==1.1.8, anthropic@==0.52.0, anyio@==4.10.0, asn1crypto@==1.5.1, astrapy@==1.4.2, astunparse@==1.6.3, attrs@==25.3.0, backoff@==2.2.1, bcrypt@==4.3.0, beautifulsoup4@==4.13.4, bidict@==0.23.1, blinker@==1.9.0, blis@==1.3.0, boto3@==1.40.11, botocore@==1.40.11, branca@==0.8.1, bson@==0.5.10, build@==1.3.0, cachetools@==5.5.2, cassandra-driver@==3.29.2, cassio@==0.1.10, catalogue@==2.0.10, causal-learn@==0.1.4.3, certifi@==2025.8.3, cffi@==1.15.0, charset-normalizer@==3.4.2, chromadb@==1.0.15, click@==8.3.0, cloudpathlib@==0.21.1, cloudpickle@==3.0.0, coloredlogs@==15.0.1, confection@==0.1.5, contourpy@==1.3.1, cryptography@==45.0.6, curl_cffi@==0.11.1, cycler@==0.12.1, cymem@==2.0.11, daily-python@==0.19.3, dataclasses-json@==0.5.14, deprecation@==2.1.0, distro@==1.9.0, dnspython@==2.7.0, durationpy@==0.10, entrypoints@==0.4, et_xmlfile@==2.0.0, eventlet@==0.33.3, Faker@==37.5.3, fastapi@==0.110.1, fastjsonschema@==2.21.1, favicon@==0.7.0, feedparser@==6.0.11, filelock@==3.18.0, Flask@==2.3.3, flask-cors@==5.0.1, Flask-SocketIO@==5.3.6, flatbuffers@==25.2.10, folium@==0.20.0, fonttools@==4.55.8, frozendict@==2.4.6, frozenlist@==1.7.0, fsspec@==2025.5.1, futures@==3.0.5, fuzzywuzzy@==0.18.0, gast@==0.4.0, geographiclib@==2.0, geomet@==0.2.1.post1, geopy@==2.4.1, gitdb@==4.0.12, GitPython@==3.1.44, google-auth@==2.40.3, google-auth-oauthlib@==1.0.0, google-pasta@==0.2.0, googleapis-common-protos@==1.70.0, graphviz@==0.21, greenlet@==3.2.4, grpcio@==1.74.0, h11@==0.16.0, h2@==4.2.0, h5py@==3.14.0, hf-xet@==1.1.5, hpack@==4.1.0, htbuilder@==0.9.0, httpcore@==1.0.9, httptools@==0.6.4, httpx@==0.28.1, httpx-sse@==0.4.1, huggingface-hub@==0.34.3, humanfriendly@==10.0, hyperframe@==6.1.0, idna@==3.10, importlib_metadata@==8.7.0, importlib_resources@==6.5.2, itsdangerous@==2.2.0, Jinja2@==3.1.6, jiter@==0.10.0, jmespath@==1.0.1, joblib@==1.4.2, jsonpatch@==1.33, jsonpointer@==3.0.0, jsonschema@==4.24.0, jsonschema-specifications@==2025.4.1, jupyter_client@==8.6.3, jupyter_core@==5.8.1, keras@==2.13.1, kiwisolver@==1.4.8, kubernetes@==33.1.0, langchain-text-splitters@==0.3.9, langcodes@==3.5.0, langsmith@==0.0.92, language_data@==1.3.0, Levenshtein@==0.27.1, libclang@==18.1.1, logging@==0.4.9.6, lxml@==6.0.0, marisa-trie@==1.3.0, Markdown@==3.8.2, markdown-it-py@==3.0.0, markdownlit@==0.0.7, MarkupSafe@==3.0.3, marshmallow@==3.26.1, matplotlib@==3.7.2, mdurl@==0.1.2, mediapipe@==0.10.7, ml_dtypes@==0.5.3, mmh3@==5.2.0, momentchi2@==0.1.8, mpmath@==1.3.0, multidict@==6.6.3, multitasking@==0.0.11, murmurhash@==1.0.13, mypy_extensions@==1.1.0, namex@==0.1.0, narwhals@==1.42.1, nbclient@==0.10.2, nbformat@==5.10.4, networkx@==3.5, nltk@==3.9.1, numexpr@==2.11.0, numpy@==1.24.3, oauthlib@==3.3.1, onnxruntime@==1.22.1, openai@==0.28.1, opencv-contrib-python@==4.11.0.86, opencv-python@==4.8.1.78, openpyxl@==3.1.5, opentelemetry-api@==1.36.0, opentelemetry-exporter-otlp-proto-common@==1.36.0, opentelemetry-exporter-otlp-proto-grpc@==1.36.0, opentelemetry-proto@==1.36.0, opentelemetry-sdk@==1.36.0, opentelemetry-semantic-conventions@==0.57b0, opt_einsum@==3.4.0, optree@==0.17.0, orjson@==3.11.1, outcome@==1.3.0.post0, overrides@==7.7.0, packaging@==23.2, pandas@==2.0.3, papermill@==2.6.0, patsy@==1.0.1, pdfminer.six@==20250506, pdfplumber@==0.11.7, peewee@==3.14.10, pillow@==11.1.0, pip-chill@==1.0.3, platformdirs@==4.3.8, plotly@==6.3.0, posthog@==5.4.0, preshed@==3.0.10, prometheus_client@==0.22.1, propcache@==0.3.2, protobuf@==3.20.3, pyarrow@==20.0.0, pyasn1@==0.6.1, pyasn1_modules@==0.4.2, PyAudio@==0.2.14, pybase64@==1.4.2, pycparser@==2.21, pydantic@==2.11.7, pydantic_core@==2.33.2, pydantic-settings@==2.10.1, pydeck@==0.9.1, pydot@==4.0.1, pygame@==2.6.1, Pygments@==2.19.2, PyJWT@==2.10.1, pymdown-extensions@==10.16.1, pymongo@==4.13.2, PyMySQL@==1.0.2, pyOpenSSL@==25.1.0, pyparsing@==3.0.9, PyPDF2@==3.0.1, pypdfium2@==4.30.0, PyPika@==0.48.9, pyproject_hooks@==1.2.0, PySocks@==1.7.1, python-dateutil@==2.9.0.post0, python-dotenv@==1.0.0, python-engineio@==4.12.3, python-Levenshtein@==0.27.1, python-socketio@==5.9.0, pytz@==2025.1, PyYAML@==6.0.2, pyzmq@==27.0.0, RapidFuzz@==3.12.2, rdflib@==7.1.4, referencing@==0.36.2, regex@==2024.11.6, requests@==2.31.0, requests-oauthlib@==2.0.0, requests-toolbelt@==1.0.0, rich@==14.1.0, rpds-py@==0.25.1, rsa@==4.9.1, s3transfer@==0.13.1, safetensors@==0.5.3, scikit-learn@==1.3.2, scipy@==1.11.3, seaborn@==0.13.2, selenium@==4.32.0, sentence-transformers@==5.0.0, sgmllib3k@==1.0.0, shellingham@==1.5.4, simple-websocket@==1.1.0, six@==1.17.0, smart_open@==7.3.0.post1, smmap@==5.0.2, sniffio@==1.3.1, snowflake-connector-python@==3.17.1, snowflake-snowpark-python@==1.36.0, sortedcontainers@==2.4.0, sounddevice@==0.5.3, soupsieve@==2.6, spacy@==3.8.7, spacy-legacy@==3.0.12, spacy-loggers@==1.0.5, SQLAlchemy@==2.0.42, srsly@==2.5.1, st-annotated-text@==4.0.2, st-theme@==1.2.3, starlette@==0.37.2, statsmodels@==0.14.4, streamlit@==1.45.1, streamlit_faker@==0.0.4, streamlit-agraph@==0.0.45, streamlit-avatar@==0.1.3, streamlit-camera-input-live@==0.2.0, streamlit-card@==1.0.2, streamlit-embedcode@==0.1.2, streamlit-extras@==0.7.6, streamlit-image-coordinates@==0.4.0, streamlit-keyup@==0.3.0, streamlit-notify@==0.3.1, streamlit-option-menu@==0.4.0, streamlit-toggle-switch@==1.0.2, streamlit-vertical-slider@==2.5.5, sympy@==1.14.0, tenacity@==8.5.0, tensorboard@==2.13.0, tensorboard-data-server@==0.7.2, tensorflow@==2.13.0, tensorflow-estimator@==2.13.0, tensorflow-macos@==2.13.0, termcolor@==3.1.0, textblob@==0.19.0, thinc@==8.3.6, threadpoolctl@==3.6.0, tigramite@==5.2.8.2, tiktoken@==0.10.0, tokenizers@==0.21.1, toml@==0.10.2, tomlkit@==0.13.3, torch@==2.7.0, tornado@==6.5.1, tqdm@==4.67.1, traitlets@==5.14.3, transformers@@ git+https://github.com/huggingface/transformers.git@6dfd561d9cd722dfc09f702355518c6d09b9b4e3, trio@==0.30.0, trio-websocket@==0.12.2, typer@==0.16.0, typing_extensions@==4.5.0, typing-inspect@==0.9.0, typing-inspection@==0.4.1, tzdata@==2025.1, tzlocal@==5.3.1, urllib3@==2.5.0, uuid6@==2024.1.12, uvicorn@==0.29.0, uvloop@==0.21.0, vaderSentiment@==3.3.2, validators@==0.35.0, vapi-python@==0.1.9, wasabi@==1.1.3, watchfiles@==1.1.0, weasel@==0.4.1, webdriver-manager@==4.0.2, websocket-client@==1.8.0, websockets@==15.0.1, Werkzeug@==3.1.3, wordcloud@==1.9.4, wrapt@==1.17.3, wsproto@==1.2.0, xgboost@==3.0.0, xyzservices@==2025.4.0, yarl@==1.20.1, yfinance@==0.2.61, zipp@==3.23.0, zstandard@==0.23.0
- vision/requirements.txt: fish-audio-sdk@==0.1.0, flask@==2.3.2, flask-cors@==3.0.10, mediapipe@==0.10.14, numpy@==1.26.4, opencv-python@==4.10.0.84, python-dotenv@==1.0.1, requests@==2.31.0

### Recent commits (newest first)

- Update README.md
- Add comprehensive BigBro project documentation and improve AI productivity monitoring
- update readme
- update readme
- Update strike countdown display from 30s to 10s to match task monitoring threshold
- Reduce task monitoring strike time from 30s to 10s and looking away strike from 5s to 10s
- Remove stat cards (Total Strikes, Total Calls, Quiz Accuracy, Questions Answered, Correct Answers) from dashboard
- Fix Supabase RPC parameter name from user_id to target_user_id
- Remove temporary file
- Fix Vapi call success detection to accept both 200 and 201 status codes
- Increase Vapi timeout to 30s for absence calls
- Add debug logging for absence call
- Add debug logging to increment_strikes_supabase
- Fix strike display and call timing: update local strikes from Supabase count and reset only after successful call
- Add extensive debug logging for strike increment and call logic
- Fix strike call logic: persist momCalled to storage and improve logging
- Fix strike tracking and call logic to use user_id from active sessions and check for 2 strikes
- Fix table name from active_sessions to user_sessions
- Fix get_user_phone_from_supabase to query by user_id from active_sessions
- Update vision server to load .env from root directory

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

### SECURITY.md

```markdown
# Security & Sensitive Information Management

## Important: Sensitive Information Protection

This project handles sensitive API keys and credentials. **Never commit these to the repository.**

## Files Protected by .gitignore

The following files contain sensitive information and are **automatically ignored by git**:

### Configuration Files
- `config.js` - Chrome extension configuration (API keys)
- `webapp/config.js` - Web app configuration (API keys)
- `.env` - Root environment variables
- `vision/.env` - Vision server environment variables

### All Sensitive Data Includes:
- Claude API keys
- Gemini API keys
- Letta AI credentials
- Vapi AI keys and assistant IDs
- Supabase credentials
- Fish Labs API keys
- ChromaDB configuration

## Setup Instructions

### For Extension:
1. Copy `config_template.js` to `config.js`
2. Fill in your API keys in `config.js`
3. **Never commit `config.js`**

### For Web App:
1. Copy `webapp/config_template.js` to `webapp/config.js`
2. Fill in your API keys
3. **Never commit `webapp/config.js`**

### For Vision Server:
1. Create a `.env` file in the root directory
2. Add your API keys (see environment variables below)

## Required Environment Variables

```bash
# Claude AI
CLAUDE_API_KEY=your-key-here

# Gemini AI
GEMINI_API_KEY=your-key-here

# Fish Labs
FISH_LABS_API_KEY=your-key-here
FISH_LABS_MODEL_ID=your-model-id-here

# Vapi
VAPI_API_KEY=your-key-here
VAPI_PHONE_NUMBER_ID=your-phone-number-id
VAPI_SLACK_OFF_ASSISTANT_ID=your-assistant-id

# Supabase
SUPABASE_URL=your-supabase-url
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key

# ChromaDB
CHROMADB_API_KEY=your-chromadb-key
CHROMADB_TENANT=your-tenant-id
CHROMADB_DATABASE=your-database-name
```

## Verification

To verify that sensitive files are protected, run:
```bash
git check-ignore config.js webapp/config.js .env
```

If all files are listed, they are properly ignored.

## Security Checklist

- [ ] `config.js` exists locally but is NOT in git
- [ ] `webapp/config.js` exists locally but is NOT in git
- [ ] `.env` files are NOT committed
- [ ] Template files (`*_template.js`) are committed (they're safe)
- [ ] No API keys in code comments
- [ ] No API keys in log messages
- [ ] `.gitignore` includes all sensitive files

## What to Do If You Accidentally Commit Sensitive Data

1. **Immediately** revoke the exposed API key
2. Remove the file from git history:
   ```bash
   git filter-branch --force --index-filter \
     "git rm --cached --ignore-unmatch config.js" \
     --prune-empty --tag-name-filter cat -- --all
   ```
3. Force push (WARNING: This rewrites history)
4. Notify team members to re-clone the repository

## Best Practices

1. ✅ Always use `config_template.js` as a base
2. ✅ Keep API keys in environment variables when possible
3. ✅ Use `.env` files for local development
4. ✅ Never share API keys in messages or commits
5. ✅ Rotate keys regularly
6. ❌ Never commit `config.js` or `.env` files
7. ❌ Never share screenshots with visible keys
8. ❌ N
[truncated — 248 more characters]
```

### assets/PROJECT_DESCRIPTION.md

```markdown
# BigBro

## Inspiration

We've all been there - opening our laptop with the best intentions to study or work, only to find ourselves two hours deep into YouTube, Reddit, or endlessly scrolling social media. Traditional productivity apps rely on self-reporting or simple website blockers that are easy to bypass. We wanted to create something different: a system that truly *knows* when you're slacking off and holds you accountable in ways you can't ignore.

The idea came from a simple truth: we're accountable to others in ways we aren't to ourselves. If someone else knows you're wasting time, you're far more likely to get back on track. BigBro brings that external accountability into the digital workspace, using cutting-edge AI and computer vision to monitor both what you're doing and whether you're actually paying attention.

## What it does

BigBro is a comprehensive productivity monitoring system that watches you work in two powerful ways:

**Task Monitoring**: BigBro continuously captures screenshots of your current browser tab and sends it to Claude AI along with the URL and your stated task. Claude analyzes whether you're actually working toward your goal or if you've drifted off-task. If you're caught slacking for more than a few seconds, you accumulate strikes.

**Attention Monitoring**: Using your webcam, BigBro tracks your face in real-time using MediaPipe and OpenCV. It detects three critical states:
- **Sleeping**: If your eyes close for more than a few seconds, an aggressive AI-generated wake-up message plays through Fish Audio's text-to-speech
- **Looking Away**: If you turn your head away from the screen for more than a few seconds, you get a strike
- **Not Present**: If you leave your desk for more than a few seconds, BigBro calls your phone via Vapi to bring you back

**The Strike System**: Accumulate 2 strikes, and BigBro escalates. It automatically calls your accountability partner (yes, your mom) via Vapi's phone API to report that you're off-task. This creates real consequences that can't be dismissed with a click.

**Analytics Dashboard**: A web interface shows your productivity stats, strike history, and session summaries, helping you understand your focus patterns over time.

## How we built it

BigBro combines multiple cutting-edge technologies into a cohesive monitoring pipeline:

**Chrome Extension (Frontend)**
- Built with Manifest V3 to access browser tabs and capture screenshots
- Background service orchestrates tab monitoring and coordinates with the vision server
- Popup interface for task setting and real-time status viewing

**Vision Analysis System (Python/Flask)**
- Flask server running on port 8080 handles all computer vision processing
- **MediaPipe Face Mesh** detects 468 3D facial landmarks in real-time
- **OpenCV** solves the Perspective-n-Point problem to calculate precise head orientation
  - Takes 6 key landmarks from MediaPipe (nose, chin, eyes, mouth)
  - Maps 2D pixel coordinates to a 3D face model
  -
[truncated — 6405 more characters]
```

### package.json

```
{
  "name": "cal-hacks-fall25",
  "version": "1.0.0",
  "description": "A Chrome browser extension that helps monitor your productivity by tracking if you're staying on task based on the websites you visit.",
  "main": "background.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/smritirangarajan/cal-hacks-fall25.git"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "type": "module",
  "bugs": {
    "url": "https://github.com/smritirangarajan/cal-hacks-fall25/issues"
  },
  "homepage": "https://github.com/smritirangarajan/cal-hacks-fall25#readme",
  "dependencies": {
    "@letta-ai/letta-client": "^0.0.68665",
    "@supabase/supabase-js": "^2.76.1"
  }
}

```

### requirements.txt

```
absl-py==2.3.1
aiohappyeyeballs==2.6.1
aiohttp==3.12.15
aiosignal==1.4.0
altair==5.5.0
altex==0.2.0
annotated-types==0.7.0
ansicolors==1.1.8
anthropic==0.52.0
anyio==4.10.0
asn1crypto==1.5.1
astrapy==1.4.2
astunparse==1.6.3
attrs==25.3.0
backoff==2.2.1
bcrypt==4.3.0
beautifulsoup4==4.13.4
bidict==0.23.1
blinker==1.9.0
blis==1.3.0
boto3==1.40.11
botocore==1.40.11
branca==0.8.1
bson==0.5.10
build==1.3.0
cachetools==5.5.2
cassandra-driver==3.29.2
cassio==0.1.10
catalogue==2.0.10
causal-learn==0.1.4.3
certifi==2025.8.3
cffi==1.15.0
charset-normalizer==3.4.2
chromadb==1.0.15
click==8.3.0
cloudpathlib==0.21.1
cloudpickle==3.0.0
coloredlogs==15.0.1
confection==0.1.5
contourpy==1.3.1
cryptography==45.0.6
curl_cffi==0.11.1
cycler==0.12.1
cymem==2.0.11
daily-python==0.19.3
dataclasses-json==0.5.14
deprecation==2.1.0
distro==1.9.0
dnspython==2.7.0
durationpy==0.10
entrypoints==0.4
et_xmlfile==2.0.0
eventlet==0.33.3
Faker==37.5.3
fastapi==0.110.1
fastjsonschema==2.21.1
favicon==0.7.0
feedparser==6.0.11
filelock==3.18.0
Flask==2.3.3
flask-cors==5.0.1
Flask-SocketIO==5.3.6
flatbuffers==25.2.10
folium==0.20.0
fonttools==4.55.8
frozendict==2.4.6
frozenlist==1.7.0
fsspec==2025.5.1
futures==3.0.5
fuzzywuzzy==0.18.0
gast==0.4.0
geographiclib==2.0
geomet==0.2.1.post1
geopy==2.4.1
gitdb==4.0.12
GitPython==3.1.44
google-auth==2.40.3
google-auth-oauthlib==1.0.0
google-pasta==0.2.0
googleapis-common-protos==1.70.0
graphviz==0.21
greenlet==3.2.4
grpcio==1.74.0
h11==0.16.0
h2==4.2.0
h5py==3.14.0
hf-xet==1.1.5
hpack==4.1.0
htbuilder==0.9.0
httpcore==1.0.9
httptools==0.6.4
httpx==0.28.1
httpx-sse==0.4.1
huggingface-hub==0.34.3
humanfriendly==10.0
hyperframe==6.1.0
idna==3.10
importlib_metadata==8.7.0
importlib_resources==6.5.2
itsdangerous==2.2.0
Jinja2==3.1.6
jiter==0.10.0
jmespath==1.0.1
joblib==1.4.2
jsonpatch==1.33
jsonpointer==3.0.0
jsonschema==4.24.0
jsonschema-specifications==2025.4.1
jupyter_client==8.6.3
jupyter_core==5.8.1
keras==2.13.1
kiwisolver==1.4.8
kubernetes==33.1.0
langchain-text-splitters==0.3.9
langcodes==3.5.0
langsmith==0.0.92
language_data==1.3.0
Levenshtein==0.27.1
libclang==18.1.1
logging==0.4.9.6
lxml==6.0.0
marisa-trie==1.3.0
Markdown==3.8.2
markdown-it-py==3.0.0
markdownlit==0.0.7
MarkupSafe==3.0.3
marshmallow==3.26.1
matplotlib==3.7.2
mdurl==0.1.2
mediapipe==0.10.7
ml_dtypes==0.5.3
mmh3==5.2.0
momentchi2==0.1.8
mpmath==1.3.0
multidict==6.6.3
multitasking==0.0.11
murmurhash==1.0.13
mypy_extensions==1.1.0
namex==0.1.0
narwhals==1.42.1
nbclient==0.10.2
nbformat==5.10.4
networkx==3.5
nltk==3.9.1
numexpr==2.11.0
numpy==1.24.3
oauthlib==3.3.1
onnxruntime==1.22.1
openai==0.28.1
opencv-contrib-python==4.11.0.86
opencv-python==4.8.1.78
openpyxl==3.1.5
opentelemetry-api==1.36.0
opentelemetry-exporter-otlp-proto-common==1.36.0
opentelemetry-exporter-otlp-proto-grpc==1.36.0
opentelemetry-proto==1.36.0
opentelemetry-sdk==1.36.0
opentelemetry-semantic-conventions==0.57b0
opt_einsum==3.4.0
optree==0.17.0
orjson==3.11.1
outcome==1.3.0.post0
overrides==7.7.0
packaging==23.2
pandas==2.0.3
papermill==2.6.0
patsy==1.0.1
pdfminer.six==20250506
pdfplumber==0.11.7
peewee==3.14.10
pillow==11.1.0
pip-chill==1.0.3
platformdirs==4.3.8
plotly==6.3.0
posthog==5.4.0
preshed==3.0.10
prometheus_client==0.22.1
propcache==0.3.2
protobuf==3.20.3
pyarrow==20.0.0
pyasn1==0.6.1
pyasn1_modules==0.4.2
PyAudio==0.2.14
pybase64==1.4.2
pycparser==2.21
pydantic==2.11.7
pydantic-settings==2.10.1
pydantic_core==2.33.2
pydeck==0.9.1
pydot==4.0.1
pygame==2.6.1
Pygments==2.19.2
PyJWT==2.10.1
pymdown-extensions==10.16.1
pymongo==4.13.2
PyMySQL==1.0.2
pyOpenSSL==25.1.0
pyparsing==3.0.9
PyPDF2==3.0.1
pypdfium2==4.30.0
PyPika==0.48.9
pyproject_hooks==1.2.0
PySocks==1.7.1
python-dateutil==2.9.0.post0
python-dotenv==1.0.0
python-engineio==4.12.3
python-Levenshtein==0.27.1
python-socketio==5.9.0
pytz==2025.1
PyYAML==6.0.2
pyzmq==27.0.0
RapidFuzz==3.12.2
rdflib==7.1.4
referencing==0.36.2
regex==2024.11.6
requests==2.31.0
requests-oauthlib==2.0.0
requests-toolbelt==1.0.0
rich==14.1.0
rpds-py==0.25.1
rsa==4.9.1
s3transfer==0.13.1
safetensors==0.5.3
scikit-learn==1.3.2
scipy==1.11.3
seaborn==0.13.2
selenium==4.32.0
sentence-transformers==5.0.0
sgmllib3k==1.0.0
shellingham==1.5.4
simple-websocket==1.1.0
six==1.17.0
smart_open==7.3.0.post1
smmap==5.0.2
sniffio==1.3.1
snowflake-connector-python==3.17.1
snowflake-snowpark-python==1.36.0
sortedcontainers==2.4.0
sounddevice==0.5.3
soupsieve==2.6
spacy==3.8.7
spacy-legacy==3.0.12
spacy-loggers==1.0.5
SQLAlchemy==2.0.42
srsly==2.5.1
st-annotated-text==4.0.2
st-theme==1.2.3
starlette==0.37.2
statsmodels==0.14.4
streamlit==1.45.1
streamlit-agraph==0.0.45
streamlit-avatar==0.1.3
streamlit-camera-input-live==0.2.0
streamlit-card==1.0.2
streamlit-embedcode==0.1.2
streamlit-extras==0.7.6
streamlit-image-coordinates==0.4.0
streamlit-keyup==0.3.0
streamlit-notify==0.3.1
streamlit-option-menu==0.4.0
streamlit-toggle-switch==1.0.2
streamlit-vertical-slider==2.5.5
streamlit_faker==0.0.4
sympy==1.14.0
tenacity==8.5.0
tensorboard==2.13.0
tensorboard-data-server==0.7.2
tensorflow==2.13.0
tensorflow-estimator==2.13.0
tensorflow-macos==2.13.0
termcolor==3.1.0
textblob==0.19.0
thinc==8.3.6
threadpoolctl==3.6.0
tigramite==5.2.8.2
tiktoken==0.10.0
tokenizers==0.21.1
toml==0.10.2
tomlkit==0.13.3
torch==2.7.0
tornado==6.5.1
tqdm==4.67.1
traitlets==5.14.3
transformers @ git+https://github.com/huggingface/transformers.git@6dfd561d9cd722dfc09f702355518c6d09b9b4e3
trio==0.30.0
trio-websocket==0.12.2
typer==0.16.0
typing-inspect==0.9.0
typing-inspection==0.4.1
typing_extensions==4.5.0
tzdata==2025.1
tzlocal==5.3.1
urllib3==2.5.0
uuid6==2024.1.12
uvicorn==0.29.0
uvloop==0.21.0
vaderSentiment==3.3.2
validators==0.35.0
vapi-python==0.1.9
wasabi==1.1.3
watchfiles==1.1.0
weasel==0.4.1
webdriver-manager==4.0.2
websocket-client==1.8.0
websockets==15.0.1
Werkzeug==3.1.3
wordcloud==1.9.4
wrapt==1.17.3
wsproto==1.2.0
xgboost==3.0.0
xyzservices==2025.4.0
yarl==1.20.1
yfinance==0.2.61
zipp==3.23.0
zstandard==0.23.0

```

### vision/requirements.txt

```
mediapipe==0.10.14
numpy==1.26.4
opencv-python==4.10.0.84
python-dotenv==1.0.1
# simpleaudio==1.0.4 # this is only in attention_monitor/audio.py

flask==2.3.2
flask-cors==3.0.10
requests==2.31.0
fish-audio-sdk==0.1.0

```

### vision/main.py

```python
"""First-time setup:
1. python3.12 -m venv venv
2. source venv/bin/activate
3. pip install -r requirements.txt
4. cp .env.example .env  # update with your secrets
5. python main.py
"""

from __future__ import annotations

import asyncio
import logging
import os
from pathlib import Path

from dotenv import load_dotenv

from attention_monitor import PipelineConfig, AttentionMonitorPipeline
from attention_monitor.audio import SoundManager
from attention_monitor.notifications import NotificationClient


def _get_int(name: str, default: int) -> int:
    value = os.getenv(name)
    if value is None:
        return default
    try:
        return int(value)
    except ValueError:
        return default


def _get_float(name: str, default: float) -> float:
    value = os.getenv(name)
    if value is None:
        return default
    try:
        return float(value)
    except ValueError:
        return default


def _get_bool(name: str, default: bool) -> bool:
    value = os.getenv(name)
    if value is None:
        return default
    return value.lower() in {"1", "true", "yes", "on"}


def build_config() -> PipelineConfig:
    load_dotenv()

    base = PipelineConfig()
    event_log_path = Path(os.getenv("EVENT_LOG_PATH", str(base.event_log_path)))

    return base.with_overrides(
        frame_width=_get_int("FRAME_WIDTH", base.frame_width),
        frame_height=_get_int("FRAME_HEIGHT", base.frame_height),
        frame_process_interval=_get_float("FRAME_PROCESS_INTERVAL", base.frame_process_interval),
        max_consecutive_closed=_get_int("MAX_CONSECUTIVE_CLOSED", base.max_consecutive_closed),
        ear_threshold=_get_float("EAR_THRESHOLD", base.ear_threshold),
        yaw_threshold=_get_float("YAW_THRESHOLD", base.yaw_threshold),
        pitch_threshold=_get_float("PITCH_THRESHOLD", base.pitch_threshold),
        history_window=_get_int("HISTORY_WINDOW", base.history_window),
        distraction_threshold=_get_int("DISTRACTION_THRESHOLD", base.distraction_threshold),
        event_log_path=event_log_path,
        notification_api_key=os.getenv("NOTIFICATION_API_KEY", base.notification_api_key),
        enable_sounds=_get_bool("ENABLE_SOUNDS", base.enable_sounds),
    )


async def main() -> None:
    config = build_config()
    logging.basicConfig(level=logging.INFO, format="%(message)s")
    sound_manager = SoundManager(enabled=config.enable_sounds)
    notification_client = NotificationClient(config.notification_api_key)

    pipeline = AttentionMonitorPipeline(
        config,
        sound_manager=sound_manager,
        notification_client=notification_client,
    )

    try:
        await pipeline.run()
    except asyncio.CancelledError:  # pragma: no cover - defensive
        raise


if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        print("Interrupted by user.")
```

### webapp/app.js

```javascript
// Web App - Dashboard
import { createClient } from 'https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2/+esm';
import { SUPABASE_URL, SUPABASE_ANON_KEY } from './config.js';

const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
let currentUser = null;
let webcamStream = null;
let isSessionActive = false;

// Initialize
checkSession();

// Authentication
async function checkSession() {
  const { data: { session } } = await supabase.auth.getSession();
  
  if (session) {
    currentUser = session.user;
    document.getElementById('user-email').textContent = session.user.email;
    await loadDashboard();
    showMainApp();
  } else {
    showLanding();
  }
}

function showLanding() {
  document.getElementById('landing-page').style.display = 'flex';
  document.getElementById('auth-section').style.display = 'none';
  document.getElementById('main-section').style.display = 'none';
}

function showAuth() {
  document.getElementById('landing-page').style.display = 'none';
  document.getElementById('auth-section').style.display = 'flex';
  document.getElementById('main-section').style.display = 'none';
}

function showMainApp() {
  document.getElementById('landing-page').style.display = 'none';
  document.getElementById('auth-section').style.display = 'none';
  document.getElementById('main-section').style.display = 'block';
}

// Landing page navigation
document.getElementById('get-started-btn')?.addEventListener('click', () => {
  showAuth();
  // Show signup view by default
  document.getElementById('login-view').style.display = 'none';
  document.getElementById('signup-view').style.display = 'block';
});

document.getElementById('get-started-btn-header')?.addEventListener('click', () => {
  showAuth();
  // Show signup view by default
  document.getElementById('login-view').style.display = 'none';
  document.getElementById('signup-view').style.display = 'block';
});

document.getElementById('login-link')?.addEventListener('click', (e) => {
  e.preventDefault();
  showAuth();
  // Show login view
  document.getElementById('login-view').style.display = 'block';
  document.getElementById('signup-view').style.display = 'none';
});

document.getElementById('login-link-header')?.addEventListener('click', (e) => {
  e.preventDefault();
  showAuth();
  // Show login view
  document.getElementById('login-view').style.display = 'block';
  document.getElementById('signup-view').style.display = 'none';
});

// Auth handlers
document.getElementById('switch-to-signup')?.addEventListener('click', () => {
  document.getElementById('login-view').style.display = 'none';
  document.getElementById('signup-view').style.display = 'block';
});

document.getElementById('switch-to-login')?.addEventListener('click', () => {
  document.getElementById('signup-view').style.display = 'none';
  document.getElementById('login-view').style.display = 'block';
});

document.getElementById('signup-btn')?.addEventListener('click', async () => {
  const email = document.getElementById('signup-email').value;
  const password = document.getElementById('signup-password').value;
  
  if (!email || !password) {
    showMessage('Please fill in all fields', 'error');
    return;
  }
  
  const { data, error } = await supabase.auth.signUp({ email, password });
  
  if (error) {
    console.error('Signup error:', error);
    showMessage(error.message, 'error');
  } else {
    console.log('Signup successful, user:', data.user);
    
    // Create initial user_settings record
    if (data.user) {
      console.log('Creating user_settings for user:', data.user.id);
      const { error: settingsError, data: settingsData } = await supabase
        .from('user_settings')
        .insert({
          user_id: data.user.id,
          mom_phone: null,
          your_phone: null,
          total_strikes: 0,
          total_calls: 0
        })
        .select();
      
      if (settingsError) {
        console.error('Error creating user_settings:', settingsError);
      } else {
        console.log('User settings created successfully:', settingsData);
      }
    }
    
    showMessage('Account created! Please log in.', 'success');
    setTimeout(() => {
      document.getElementById('signup-view').style.display = 'none';
      document.getElementById('login-view').style.display = 'block';
    }, 1500);
  }
});

document.getElementById('login-btn')?.addEventListener('click', async () => {
  const email = document.getElementById('login-email').value;
  const password = document.getElementById('login-password').value;
  
  if (!email || !password) {
    showMessage('Please fill in all fields', 'error');
    return;
  }
  
  const { data, error } = await supabase.auth.signInWithPassword({ email, password });
  
  if (error) {
    showMessage(error.message, 'error');
  } else {
    currentUser = data.user;
    document.getElementById('user-email').textContent = data.user.email;
    await loadDashboard();
    showMainApp();
  }
});

function showMessage(text, type) {
  const msg = document.getElementById('auth-message');
  msg.textContent = text;
  msg.className = `message active ${type}`;
  setTimeout(() => msg.className = 'message', 5000);
}

// Load dashboard stats
async function loadDashboard() {
  if (!currentUser) return;
  
  const { data } = await supabase
    .from('user_settings')
    .select('mom_phone, your_phone')
    .eq('user_id', currentUser.id)
    .single();
  
  if (data) {
    // Load phone numbers into settings
    document.getElementById('mom-phone').value = data.mom_phone || '';
    document.getElementById('your-phone').value = data.your_phone || '';
  }
}

// Settings modal
document.getElementById('settings-btn')?.addEventListener('click', () => {
  document.getElementById('settings-modal').style.display = 'block';
});

document.querySelector('.close-modal')?.addEventListener('click', () => {
  document.getElementById('settings-modal').style.display = 'none';
});

document.getElementById('save-settings-btn')?.addEvent
[truncated — 10432 more characters]
```

### webapp/webapp/app.js

```javascript
// Main Application Script
import { createClient } from 'https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2/+esm';
import { SUPABASE_URL, SUPABASE_ANON_KEY, CLAUDE_API_KEY, VAPI_PRIVATE_KEY, VAPI_ASSISTANT_ID, VAPI_AWAY_ASSISTANT_ID, VAPI_PHONE_NUMBER_ID } from './config.js';

// Initialize Supabase
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);

// State
let currentUser = null;
let isMonitoring = false;
let webcamStream = null;
let monitoringInterval = null;
let webcamCheckInterval = null;
let awayCallTimeout = null;
let currentTask = '';
let strikeCount = 0;
let momCalled = false;

// DOM Elements
const authSection = document.getElementById('auth-section');
const mainSection = document.getElementById('main-section');
const overlay = document.getElementById('overlay');

// Initialize
checkSession();

// Authentication
async function checkSession() {
  const { data: { session } } = await supabase.auth.getSession();
  
  if (session) {
    currentUser = session.user;
    await loadUserSettings();
    showMainApp();
  } else {
    showAuth();
  }
}

function showAuth() {
  authSection.style.display = 'flex';
  mainSection.style.display = 'none';
}

function showMainApp() {
  authSection.style.display = 'none';
  mainSection.style.display = 'grid';
}

// Auth handlers
document.getElementById('switch-to-signup')?.addEventListener('click', () => {
  document.getElementById('login-view').style.display = 'none';
  document.getElementById('signup-view').style.display = 'block';
});

document.getElementById('switch-to-login')?.addEventListener('click', () => {
  document.getElementById('signup-view').style.display = 'none';
  document.getElementById('login-view').style.display = 'block';
});

document.getElementById('signup-btn')?.addEventListener('click', async () => {
  const email = document.getElementById('signup-email').value;
  const password = document.getElementById('signup-password').value;
  
  if (!email || !password) {
    showMessage('Please fill in all fields', 'error');
    return;
  }
  
  const { data, error } = await supabase.auth.signUp({ email, password });
  
  if (error) {
    showMessage(error.message, 'error');
  } else {
    showMessage('Account created! Please log in.', 'success');
  }
});

document.getElementById('login-btn')?.addEventListener('click', async () => {
  const email = document.getElementById('login-email').value;
  const password = document.getElementById('login-password').value;
  
  if (!email || !password) {
    showMessage('Please fill in all fields', 'error');
    return;
  }
  
  const { data, error } = await supabase.auth.signInWithPassword({ email, password });
  
  if (error) {
    showMessage(error.message, 'error');
  } else {
    currentUser = data.user;
    await loadUserSettings();
    showMainApp();
  }
});

document.getElementById('logout-btn')?.addEventListener('click', async () => {
  await supabase.auth.signOut();
  currentUser = null;
  if (isMonitoring) stopMonitoring();
  showAuth();
});

function showMessage(text, type) {
  const msg = document.getElementById('auth-message');
  msg.textContent = text;
  msg.className = `message active ${type}`;
  setTimeout(() => msg.className = 'message', 5000);
}

// Settings
async function loadUserSettings() {
  if (!currentUser) return;
  
  const { data } = await supabase
    .from('user_settings')
    .select('mom_phone, your_phone')
    .eq('user_id', currentUser.id)
    .single();
  
  if (data) {
    document.getElementById('mom-phone').value = data.mom_phone || '';
    document.getElementById('your-phone').value = data.your_phone || '';
  }
}

document.getElementById('save-settings-btn')?.addEventListener('click', async () => {
  const momPhone = document.getElementById('mom-phone').value;
  const yourPhone = document.getElementById('your-phone').value;
  
  const { error } = await supabase
    .from('user_settings')
    .upsert({
      user_id: currentUser.id,
      mom_phone: momPhone || null,
      your_phone: yourPhone || null
    });
  
  alert(error ? 'Error: ' + error.message : 'Settings saved!');
});

// Monitoring
document.getElementById('start-monitoring-btn')?.addEventListener('click', async () => {
  const task = document.getElementById('task-input').value.trim();
  if (!task) {
    alert('Please enter a task');
    return;
  }
  
  currentTask = task;
  await startMonitoring();
});

document.getElementById('stop-monitoring-btn')?.addEventListener('click', () => {
  stopMonitoring();
});

async function startMonitoring() {
  isMonitoring = true;
  
  // Show overlay
  overlay.style.display = 'block';
  document.getElementById('overlay-task').textContent = currentTask;
  document.getElementById('start-monitoring-btn').style.display = 'none';
  document.getElementById('stop-monitoring-btn').style.display = 'block';
  
  // Start webcam
  await initWebcam();
  
  // Start monitoring
  monitoringInterval = setInterval(checkProductivity, 5000);
  webcamCheckInterval = setInterval(checkWebcam, 60000);
  
  checkProductivity();
}

function stopMonitoring() {
  isMonitoring = false;
  overlay.style.display = 'none';
  document.getElementById('start-monitoring-btn').style.display = 'block';
  document.getElementById('stop-monitoring-btn').style.display = 'none';
  
  if (webcamStream) {
    webcamStream.getTracks().forEach(track => track.stop());
    webcamStream = null;
  }
  
  if (monitoringInterval) clearInterval(monitoringInterval);
  if (webcamCheckInterval) clearInterval(webcamCheckInterval);
  if (awayCallTimeout) clearTimeout(awayCallTimeout);
}

async function initWebcam() {
  try {
    webcamStream = await navigator.mediaDevices.getUserMedia({ video: true });
    document.getElementById('webcam-preview').srcObject = webcamStream;
  } catch (error) {
    console.error('Webcam error:', error);
  }
}

async function checkProductivity() {
  if (!isMonitoring) return;
  
  // Get current tab info (would need extension API)
  const status = Math.random() > 0.5 ? '
[truncated — 2974 more characters]
```

### webcam-monitor.html

```html
<!DOCTYPE html>
<html>
<head>
  <title>Webcam Monitor</title>
  <style>
    body {
      margin: 0;
      padding: 20px;
      background: #1a1a1a;
      color: white;
      font-family: Arial, sans-serif;
    }
    video {
      width: 320px;
      height: 240px;
      border: 2px solid #0f0;
      border-radius: 8px;
    }
    .status {
      margin-top: 20px;
      font-size: 14px;
    }
  </style>
</head>
<body>
  <h3>Webcam Monitor</h3>
  <video id="video" autoplay playsinline></video>
  <div class="status" id="status">Initializing...</div>
  
  <script src="webcam-monitor.js"></script>
</body>
</html>

```

### config_template.js

```javascript
// Configuration Template for Chrome Extension
// Copy this file to config.js and fill in your API keys

// Claude AI API
const CLAUDE_API_KEY = 'your-claude-api-key-here';

// Google Gemini API
const GEMINI_API_KEY = 'your-gemini-api-key-here';

// Letta AI
const LETTA_API_KEY = 'your-letta-api-key-here';
const LETTA_PROJECT = 'your-letta-project-id';
const LETTA_AGENT_ID = 'your-letta-agent-id';

// Vapi (Voice AI)
const VAPI_PRIVATE_KEY = 'your-vapi-private-key';
const VAPI_PHONE_NUMBER_ID = 'your-vapi-phone-number-id';
const VAPI_ASSISTANT_ID = 'your-vapi-assistant-id';
const VAPI_AWAY_ASSISTANT_ID = 'your-vapi-away-assistant-id';

// Supabase
const SUPABASE_URL = 'https://your-project.supabase.co';
const SUPABASE_ANON_KEY = 'your-supabase-anon-key';
```

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