# Project export: Motherly.ai

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: TreeHacks 2025
- Tagline: Your mom knows you better than you do.
- Devpost: https://devpost.com/software/ai-mama
- GitHub: https://github.com/MingkuanY/ai-mommy
- Video: https://www.youtube.com/embed/eehiiErNsOM?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Result: winner (Delve: Best Agentic Workflow (AirPods + Delve Founder Numbers + Onsite); Scrapybara: Capy Prize (Japan offsite + $1k credits + Scrapybara Pro 6 months [1st] & $1k credits + Scrapybara Pro 3 months [2nd] & $100 credits + Scrapybara Pro 1 month [3rd]))
- Team: 4 GitHub contributor(s) — Pranav Tadepalli (25 commits), sophiasharif (23 commits), Sonav Agarwal (21 commits), Mingkuan Yan (12 commits)

## Devpost submission (written by the team)

### Inspiration

In the past year, 74% of people have felt overwhelmed by stress, and 30% of young adults in the U.S. experience loneliness multiple times a week. We wanted to create a large-scale solution and turned to what helps us most with both stress and loneliness: our moms. While AI companions like Character AI provide emotional support, they can’t truly take care of you. Our goal was to build an AI agent that deeply understands your emotions and takes action to help you.

### What it does

Stress management is deeply personal - what works for one person might not work for another. That’s why we built Motherly.ai, an AI-powered agent that understands how you experience stress and takes real-world actions to support you. Using biometric data like EMG, heart rate, and blood pressure, it detects your mental state in real-time and responds with personalized interventions, whether it’s scheduling a break, ordering comfort food, or adjusting your environment to help you relax. Unlike traditional AI companions, Motherly.ai doesn’t just listen – it takes care of you. Some things Motherly.ai can do for you If reading the news makes you stressed out She can play calming music and surface good news in the world She can play calming music and surface good news in the world If you mention you’re stressed out and constantly doom-scrolling Motherly.ai can interrupt your scrolling, order food for you, give you a massage, and contact your friends so they can reach out to you Motherly.ai can interrupt your scrolling, order food for you, give you a massage, and contact your friends so they can reach out to you To help your child stay motivated while studying Motherly.ai can automatically give them a YouTube break between problems on Khan Academy when they’re stressed, or alternatively nudge them back to studying when they spend too much time distracted. Motherly.ai can automatically give them a YouTube break between problems on Khan Academy when they’re stressed, or alternatively nudge them back to studying when they spend too much time distracted. If you want her to help you stay productive and avoid distractions She can recognize when you’re on Instagram and bring you back to your work She can recognize when you’re on Instagram and bring you back to your work

### How we built it

Accomplishments that we're proud of Hardware Hacking: We hacked a TENS unit to give an AI agent the capability of controlling the human body. Cutting-edge research: We used EMGs to create a stress monitoring system based on several papers that correlate EMG activity in the trapezius to stress with p<0.0001 Experimentally Induced Stress Validated by EMG Activity, A Comprehensive Analysis of Trapezius Muscle EMG Activity in Relation to Stress and Meditation The impact of adverse childhood experiences on EMG reactivity: A proof of concept study Experimentally Induced Stress Validated by EMG Activity, A Comprehensive Analysis of Trapezius Muscle EMG Activity in Relation to Stress and Meditation The impact of adverse childhood experiences on EMG reactivity: A proof of concept study Trained an LSTM classifier to predict your emotions based on your stress levels and biometric data Live Biometric Processing: Manage 4 separate real time data streams. Distinct Agent Actions: Created several distinct actions for our agent to take: Close the tab you’re on Switch the music to match your mood Order a surprise Domino’s order Text a friend Order matcha Prescribe a massage (using TENS) Book a yoga class Find fun events in your area Close the tab you’re on Switch the music to match your mood Order a surprise Domino’s order Text a friend Order matcha Prescribe a massage (using TENS) Book a yoga class Find fun events in your area Interactive Avatar: AI mother makes eye contact with your cursor as she helps you

### Challenges we ran into

To get the EMGs to have consistent readings, it took SOO many different sensor placements on the trapezius muscle in order to get any sort of reading. After learning a lot about anatomy of shoulder muscles and bones, we finally got the electrodes in the right place to measure stress from these muscles, and got really amazing readings from our sensor! We initially tried to use the same signal processing methods mentioned in the papers (bandpass and butter filters, extracting only low-frequency data, etc.) but these were too slow for real time feedback, so we had to create more optimized methods: We accidentally ordered $20 worth of Dominos while testing our Scrapybara automation Making the AI agent do the right thing based on its prompts was very difficult.

### What we learned

How to take readings using EMG sensors and how to give massages using TENS stimulation How to stream data between different services in real time How to create agents with LangChain

### What's next

Shrink the EMG sensor to be a 24/7 wearable to enable long-term stress / mental health tracking Allow the user to give Motherly.ai custom personalities, making help more familiar and comforting

## README (from the GitHub repository)

No README available.

## Detected evidence (automated analysis)

Indexed codebase: 22 recognized source files, 226 KB.
- CSS (language) — detected in the code
- Next.js (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (42 of 42)

```
agent.py
applescripts/sendMessage.applescript
book_yoga.py
bright.py
classifer/emotion_labels.npy
classifer/generate_data.py
classifer/lstm_emotion_model.pth
classifer/synthetic_emotion_data.npz
classifer/train_lstm.py
client/.gitignore
client/eslint.config.mjs
client/next.config.ts
client/package.json
client/postcss.config.mjs
client/README.md
client/src/app/globals.css
client/src/app/layout.tsx
client/src/app/mommy/page.tsx
client/src/app/page.tsx
client/src/lib/useClientRect.ts
client/src/lib/useMousePosition.ts
client/src/lib/useWindowDimensions.ts
client/tailwind.config.ts
client/tsconfig.json
emg/1_raw_data_13-12_22.03.16.txt
emg/read_data.py
fun_events.py
order_food.py
server/.gitignore
server/blood_pressure.txt
server/body_temperature.txt
server/heart_rate.txt
server/history.txt
server/lstm_emotion_model.pth
server/predict_emotion.py
server/rules.json
server/sample_history.txt
server/samples.txt
server/server.py
server/stress.py
shock.txt
stress.txt
```

### Dependencies

- client/package.json: @eslint/eslintrc@^3, @types/node@^20, @types/react@^19, @types/react-dom@^19, eslint@^9, eslint-config-next@15.1.7, next@15.1.7, postcss@^8, react@^19.0.0, react-dom@^19.0.0, react-markdown@^9.0.3, recharts@^2.15.1, tailwindcss@^3.4.1, typescript@^5

### Recent commits (newest first)

- Merge branch 'main' of https://github.com/MingkuanY/ai-mommy
- Create final_arch.png
- classifier!
- add labels to sections
- Merge branch 'main' of https://github.com/MingkuanY/ai-mommy
- book yoga & fun events
- Merge branch 'main' of https://github.com/MingkuanY/ai-mommy
- Enhanced agent
- rebrand
- edited actions error
- lol
- Merge branch 'main' of https://github.com/MingkuanY/ai-mommy
- stuf
- Update order_food.py
- Merge branch 'main' of https://github.com/MingkuanY/ai-mommy
- make dependent on stress
- Merge branch 'main' of https://github.com/MingkuanY/ai-mommy
- Updated server to work with new prompt commands
- fake data for terra
- Merge branch 'main' of https://github.com/MingkuanY/ai-mommy

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

### client/package.json

```
{
  "name": "ai-mommy",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "next": "15.1.7",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "react-markdown": "^9.0.3",
    "recharts": "^2.15.1"
  },
  "devDependencies": {
    "@eslint/eslintrc": "^3",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "eslint": "^9",
    "eslint-config-next": "15.1.7",
    "postcss": "^8",
    "tailwindcss": "^3.4.1",
    "typescript": "^5"
  }
}

```

### server/server.py

```python
import numpy as np
from collections import defaultdict
import random
import json
import flask
from threading import Thread
from time import sleep
# from stress import compute_stress_data_from_file
from typing import Dict, List

from dotenv import load_dotenv
from flask import Flask, Response, jsonify, request
from flask_cors import CORS
from pydantic import BaseModel, Field

from langchain_openai import ChatOpenAI
from langchain_community.tools import BaseTool
from langchain.prompts import PromptTemplate
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
from langgraph.prebuilt import create_react_agent
from terra.base_client import Terra
import requests
import threading
import time
import logging
from predict_emotion import predict_emotion

logging.basicConfig(level=logging.INFO)
_LOGGER = logging.getLogger("app")

terra = Terra(api_key="t0PMr4YpxCVtYc0M7bYGSpBuRwujEPvp", dev_id="4actk-aimommy-testing-ntJJIlrzqJ",
              secret="1a1e999f0665aeda4cf5a92335bce2cf4450f3a34fbb7273")


# --- File Paths ---
HISTORY_FILE = "history.txt"  # Change this to the actual file path
RULES_FILE = "rules.json"      # Change this to the actual file path
HEART_RATE_FILE = "heart_rate.txt"
BLOOD_PRESSURE_FILE = "blood_pressure.txt"
BODY_TEMPERATURE_FILE = "body_temperature.txt"

app = Flask(__name__)
CORS(app, resources={r"/*": {"origins": "*"}}, supports_credentials=True)
app.config['CORS_HEADERS'] = 'Content-Type'

load_dotenv()

# --- Monitoring Rule Definitions ---


class MonitoringRule(BaseModel):
    """Structure for a custom monitoring rule"""
    condition: str = Field(
        "Conditions based on the current time, stress level, and screen content.")
    actions: List[str] = Field(
        "Actions to take based on the condition. Available Action Types: CONTROL, MUSIC, NOTIFICATION, BRIGHTNESS, COLOR")
    priority: int = Field(
        "Priority level of the rule (1 (highest) to 5 (lowest))")
    condition_cute: str = Field(
        "Conditions field but briefly summarized in southern mothernly language")
    actions_cute: List[str] = Field(
        "Actions field but briefly summarized in southern mothernly language")
    priority_cute: str = Field(
        "A short priority level for the rule but in southern mothernly language")


def create_monitoring_rule(rule: MonitoringRule):
    """Adds a new monitoring rule to the rules.json file."""
    rules = read_monitoring_rules()

    print("------------------------------------------")
    print("Creating monitoring rule:")
    print(rule)
    print(rule.model_dump())
    print("------------------------------------------")

    rules.append(rule.model_dump())
    try:
        with open(RULES_FILE, "w") as file:
            json.dump(rules, file, indent=4)
            return "\n**Monitoring rule created successfully.**\n\n"
    except Exception as e:
        return f"\n**Error creating monitoring rule: {e}**\n\n"


def read_monitoring_rules():

    # make sure the file exists
    try:
        with open(RULES_FILE, "r") as file:
            data = json.load(file)
            print("Data: ", data)
            return data
    except:
        with open(RULES_FILE, "w") as file:
            json.dump([], file)
        return []


def read_history():
    print("rh")
    with open(HISTORY_FILE, "r") as file:
        data = [[float(x) for x in line.strip().split()[:2]] for line in file]

    recent_data = data[-10:]

    # # print("Data: ", data)

    # # Take the last 5000 points (or fewer if there aren't that many)
    # recent_data = data[-5000:]

    # # Downsample by selecting 10 evenly spaced points
    # if len(recent_data) > 10:
    #     indices = np.linspace(0, len(recent_data) - 1, 10, dtype=int)
    #     recent_data = [recent_data[i] for i in indices]

    # # print("Recent data:", recent_data)

    return recent_data


def gen_prediction():
    try:
        # Load the last 10 data points for each of the 4 features
        # stress, heart_rate, blood_pressure, temperature
        stressData = read_history()
        biometrics = get_bio_data()
        stress = [x[1] for x in stressData]
        heart_rate = [x["rate"] for x in biometrics["heart_rate"]][-10:]
        blood_pressure = [biometrics["blood_pressure_high"]] * 10
        temperature = [biometrics["body_temperature"]] * 10

        # Make a prediction
        prediction = predict_emotion(
            stress, heart_rate, blood_pressure, temperature)
        return prediction
    except Exception as e:
        print("Error generating prediction:", e)
        return str(e)


# def read_history():
#     # File formatted as "time stress" per line
#     with open(HISTORY_FILE, "r") as file:
#         data = []
#         for line in file:
#             parts = line.strip().split()
#             if parts:
#                 data.append([int(x) for x in parts])
#     return data

# --- Legacy endpoints remain unchanged ---


@app.route('/')
def hello_world():
    return 'Hello, World!'


@app.route("/history", methods=["GET"])
def get_history():
    print("Getting history rahhh")
    try:
        data = read_history()
        biometrics = get_bio_data()

        data = [{"time": time, "stress": stress} for time, stress in data]

        complete_data = {
            "history": data,
            "biometrics": biometrics,
            "prediction": gen_prediction()
        }
        return jsonify(complete_data)
    except FileNotFoundError:
        return jsonify({"error": "History file not found"}), 404
    except Exception as e:
        return jsonify({"error": str(e)}), 500


@app.route("/rules", methods=["GET"])
def get_rules():
    try:
        rules = read_monitoring_rules()
        return jsonify(rules)
    except FileNotFoundError:
        return jsonify({"error": "Rules file not found"}), 404
    except Exception as e:
        return jsonify({"error": str(e)}), 500

# --- Prompt Template Setup ---


system_prompt_template = PromptTemplate(
    input_variables
[truncated — 9704 more characters]
```

### client/src/app/layout.tsx

```typescript
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        {children}
      </body>
    </html>
  );
}

```

### client/src/app/page.tsx

```typescript
import Image from "next/image";

export default function Home() {
  return (
    <div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
      <main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
        <Image
          className="dark:invert"
          src="/next.svg"
          alt="Next.js logo"
          width={180}
          height={38}
          priority
        />
        <ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
          <li className="mb-2">
            Get started by editing{" "}
            <code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
              src/app/page.tsx
            </code>
            .
          </li>
          <li>Save and see your changes instantly.</li>
        </ol>

        <div className="flex gap-4 items-center flex-col sm:flex-row">
          <a
            className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
            href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
            target="_blank"
            rel="noopener noreferrer"
          >
            <Image
              className="dark:invert"
              src="/vercel.svg"
              alt="Vercel logomark"
              width={20}
              height={20}
            />
            Deploy now
          </a>
          <a
            className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
            href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
            target="_blank"
            rel="noopener noreferrer"
          >
            Read our docs
          </a>
        </div>
      </main>
      <footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
        <a
          className="flex items-center gap-2 hover:underline hover:underline-offset-4"
          href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
          target="_blank"
          rel="noopener noreferrer"
        >
          <Image
            aria-hidden
            src="/file.svg"
            alt="File icon"
            width={16}
            height={16}
          />
          Learn
        </a>
        <a
          className="flex items-center gap-2 hover:underline hover:underline-offset-4"
          href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
          target="_blank"
          rel="noopener noreferrer"
        >
          <Image
            aria-hidden
            src="/window.svg"
            alt="Window icon"
            width={16}
            height={16}
          />
          Examples
        </a>
        <a
          className="flex items-center gap-2 hover:underline hover:underline-offset-4"
          href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
          target="_blank"
          rel="noopener noreferrer"
        >
          <Image
            aria-hidden
            src="/globe.svg"
            alt="Globe icon"
            width={16}
            height={16}
          />
          Go to nextjs.org →
        </a>
      </footer>
    </div>
  );
}

```

### client/src/app/mommy/page.tsx

```typescript
"use client";

import { useClientRect } from "@/lib/useClientRect";
import useMousePosition from "@/lib/useMousePosition";
import useWindowDimensions from "@/lib/useWindowDimensions";
import { useEffect, useRef, useState } from "react";
import Markdown from "react-markdown";
import {
	CartesianGrid,
	Line,
	LineChart,
	ResponsiveContainer,
	Tooltip,
	XAxis,
	YAxis,
} from "recharts";

interface Props {}
interface ChatObject {
	sender: "user" | "mommy";
	message: string;
}

const EMOTION_TO_MESSAGE = {
	fear: "Are you feeling scared? I'm here for you.",
	anger: "I'm sorry you're feeling angry. Let's talk about it.",
	sadness: "I'm here for you. I know you're feeling sad.",
	normal: "Seems like you're having a good day!",
};

const page = (props: Props) => {
	const [chatInput, setChatInput] = useState<string>("");
	const [chatHistory, setChatHistory] = useState<ChatObject[]>([]);

	async function sendChat() {
		// fetch data from localhost 5000

		// add the user's chat to the chat history

		let newChatHistory: ChatObject[] = [
			...chatHistory,
			{ sender: "user", message: chatInput },
		];

		setChatHistory(newChatHistory);

		const response = await fetch("http://127.0.0.1:5000/ask", {
			body: JSON.stringify({ history: newChatHistory }),
			headers: {
				"Content-Type": "application/json",
			},
			method: "POST",
		});
		const reader = response.body?.getReader();

		if (!reader) return;

		let done = false;
		let data = "";
		while (!done) {
			const { value, done: done_ } = await reader.read();
			done = done_;
			data += new TextDecoder().decode(value);

			// update the last chat
			// if the last chat object is from the user
			// then add a new chat object with the response from the server
			// else update the last chat object with the response from the server
			setChatHistory((oldChatHistory) => {
				if (oldChatHistory.length > 0) {
					if (oldChatHistory[oldChatHistory.length - 1].sender === "user") {
						return [
							...oldChatHistory.slice(0, -1),
							{ sender: "user", message: chatInput },
							{ sender: "mommy", message: data },
						];
					} else {
						const newChatHistory = oldChatHistory.slice(0, -1);
						return [...newChatHistory, { sender: "mommy", message: data }];
					}
				} else {
					return [{ sender: "mommy", message: data }];
				}
			});
		}
		fetchActions();
	}

	const [data, setData] = useState<any[]>();
	const [biometrics, setBiometrics] = useState<any>();
	const [prediction, setPrediction] = useState<
		"normal" | "fear" | "anger" | "sadness"
	>();
	useEffect(() => {
		function fetchData() {
			// fetch data from localhost 5000
			fetch("http://127.0.0.1:5000/history")
				.then((res) => res.json())
				.then((data) => {
					console.log(data);
					setData(data.history);
					setBiometrics(data.biometrics);
					setPrediction(data.prediction);
				});
		}

		// interval to fetch data every 5 seconds
		try {
			fetchData();
		} catch (e) {
			console.log("Error fetching data");
		}

		const interval = setInterval(() => {
			console.log("fetching data...");
			try {
				fetchData();
			} catch (e) {
				console.log("Error fetching data");
			}
		}, 500);

		return () => clearInterval(interval);
	}, [setData]);

	const [actions, setActions] = useState<any[]>();
	function fetchActions() {
		fetch("http://127.0.0.1:5000/rules")
			.then((res) => res.json())
			.then((data) => {
				console.log(data);
				setActions(data);
			});
	}
	useEffect(() => {
		// interval to fetch data every 5 seconds
		try {
			fetchActions();
		} catch (e) {
			console.log("Error fetching actions");
		}
	}, [setActions]);

	return (
		<div className="w-screen min-h-screen flex flex-col items-center bg-slate-50 gap-4">
			<div className="flex flex-col gap-4 items-center justify-center py-36">
				{/* <img
					src="/aimom_trans.webp"
					alt="Mommy"
					className="w-48 rounded-lg mb-2  "
				/> */}
				<div className="mb-2">
					<MotherAvatar />
				</div>
				<h1 className="text-4xl font-bold text-slate-700 text-center">
					🤍 motherly.ai 🤍
				</h1>
				<p className="text-gray-400 text-center italic">
					Don't worry, mommy's here.
				</p>

				{chatHistory.length > 0 && (
					<div className="w-[40rem] bg-slate-100 rounded-lg p-4 flex flex-col gap-2 max-h-[80vh] overflow-scroll">
						{chatHistory.map((chat, index) => (
							<div
								key={index}
								className={`flex w-full ${
									chat.sender === "user" ? "justify-end" : "justify-start"
								}`}
							>
								<div
									className={`p-2 overflow-hidden ${
										chat.sender === "user" ? "bg-slate-200" : "bg-slate-200"
									} rounded-lg max-w-[75%]`}
								>
									<div className="text-black">
										<Markdown>{chat.message}</Markdown>
									</div>
								</div>
							</div>
						))}
					</div>
				)}

				<div className="w-[40rem] bg-slate-100 rounded-lg p-4">
					<input
						type="text"
						value={chatInput}
						onChange={(e) => setChatInput(e.target.value)}
						onKeyDown={(e) => {
							if (e.key === "Enter") {
								sendChat();
								setChatInput("");

								// setChatHistory([
								// 	...chatHistory,
								// 	{ sender: "user", message: chatInput },
								// ]);
							}
						}}
						className="w-full bg-transparent outline-none text-black"
						placeholder="What's up sweetheart?"
					></input>
				</div>
			</div>

			<p className="text-gray-400 text-center italic">
				I know how you're feeling.
			</p>

			<div className="w-[50rem] bg-slate-100 rounded-lg p-4 flex flex-col gap-4">
				<p className="text-slate-700 text-center italic font-semibold">
					{/* @ts-ignore */}
					{EMOTION_TO_MESSAGE[prediction]}
				</p>
				<div className="flex flex-row gap-4">
					<p className=" text-slate-400 flex-1 text-center italic ">
						Your stress levels 🥺
					</p>
					<p className=" text-slate-400 flex-1 text-center italic ">
						Your heart rate 💓
					</p>
				</div>
				<div className="w-full flex flex-row gap-4">
					<di
[truncated — 5680 more characters]
```

### bright.py

```python
import screen_brightness_control as sbc

def set_brightness(value):
    """
    Set the display brightness using screen_brightness_control.
    
    Args:
        value (float): Brightness value between 0.0 and 1.0
    """
    if not 0.0 <= value <= 1.0:
        raise ValueError("Brightness must be between 0.0 and 1.0")
    
    # Convert to percentage (0-100)
    brightness_percent = int(value * 100)
    
    try:
        # Set brightness for all displays
        sbc.set_brightness(brightness_percent)
    except Exception as e:
        print(f"Error setting brightness: {e}")

def get_brightness():
    """Get current brightness level."""
    try:
        # Get brightness of primary display
        return sbc.get_brightness()[0] / 100.0
    except Exception as e:
        print(f"Error getting brightness: {e}")
        return None

# Example usage
if __name__ == "__main__":
    # Print current brightness
    print(f"Current brightness: {get_brightness()}")
    
    # Set brightness to 80%
    set_brightness(0.8)
```

### book_yoga.py

```python
from scrapybara.tools import BashTool, ComputerTool, EditTool
from scrapybara.anthropic import Anthropic
from scrapybara.prompts import UBUNTU_SYSTEM_PROMPT
from scrapybara import Scrapybara
from playwright.sync_api import sync_playwright

client = Scrapybara(api_key="YOUR_SCRAPYBARA_API_KEY")
instance = client.start_ubuntu(timeout_hours=1)
cdp_url = instance.browser.start().cdp_url
playwright = sync_playwright().start()
browser = playwright.chromium.connect_over_cdp(cdp_url)

def print_step(step):
    print(step.text)

prompt_text = (
    "Go to Google and search for 'yoga classes near me this week'. "
    "Find a beginner-friendly class at a reputable studio. "
    "Check available slots and select the earliest one that fits my schedule. "
    "Proceed to book the class using my account. "
    "Confirm the booking and return the session details."
)

def attempt_booking():
    """Run the yoga booking task."""
    return client.act(
        model=Anthropic(),
        tools=[
            BashTool(instance),
            ComputerTool(instance),
            EditTool(instance),
        ],
        system=UBUNTU_SYSTEM_PROMPT,
        prompt=prompt_text,
        on_step=print_step,
    )

try:
    response = attempt_booking()
except Exception as e:
    print("Encountered an error:", e)

print("Final response:")
print(response.text)

```

### fun_events.py

```python
from scrapybara.tools import BashTool, ComputerTool, EditTool
from scrapybara.anthropic import Anthropic
from scrapybara.prompts import UBUNTU_SYSTEM_PROMPT
from scrapybara import Scrapybara
from playwright.sync_api import sync_playwright

# Initialize Scrapybara client
client = Scrapybara(api_key="YOUR_SCRAPYBARA_API_KEY")

# Start an Ubuntu instance
instance = client.start_ubuntu(timeout_hours=1)

# Start the browser
cdp_url = instance.browser.start().cdp_url
playwright = sync_playwright().start()
browser = playwright.chromium.connect_over_cdp(cdp_url)

def print_step(step):
    print(step.text)

# Event Search Prompt
prompt_text = (
    "Go to Eventbrite and search for fun local events happening this weekend. "
    "Filter events by 'highly rated' and 'outdoor' categories. "
    "Select the top three events based on popularity and return: "
    "- Event name "
    "- Location "
    "- Date & time "
    "- Ticket price "
    "- Booking link."
)

def attempt_event_search():
    """Run the local event search task."""
    return client.act(
        model=Anthropic(),
        tools=[
            BashTool(instance),
            ComputerTool(instance),
            EditTool(instance),
        ],
        system=UBUNTU_SYSTEM_PROMPT,
        prompt=prompt_text,
        on_step=print_step,
    )

try:
    response = attempt_event_search()
except Exception as e:
    print("Encountered an error:", e)

print("Final response:")
print(response.text)

```

### order_food.py

```python
from scrapybara.tools import BashTool, ComputerTool, EditTool
from scrapybara.anthropic import Anthropic
from scrapybara.prompts import UBUNTU_SYSTEM_PROMPT
from scrapybara import Scrapybara
from playwright.sync_api import sync_playwright
from mistralai import Mistral

api_key = "X1ZHa8fUIXvz9wy9nJOXhbKSRbEwexJx"
model = "mistral-large-latest"

client = Mistral(api_key=api_key)

chat_response = client.chat.complete(
    model= model,
    messages = [
        {
            "role": "user",
            "content": "I'm stressed and i want to get some food from dominos. choose 3 items that will help me feel better. just say the name of the items, no other text, separated by commas",
        },
    ]
)
items = chat_response.choices[0].message.content
print("Mistral AI chose the following items: ", items)
print("Ordering these items via Scrapybara...")

# Initialize the Scrapybara client
client = Scrapybara(api_key="scrapy-a90ef477-a167-4d50-a0ae-465ba3ab0a29")

# Start an Ubuntu instance
instance = client.start_ubuntu(timeout_hours=1)

# Start the browser and connect via CDP
cdp_url = instance.browser.start().cdp_url
playwright = sync_playwright().start()
browser = playwright.chromium.connect_over_cdp(cdp_url)
stream_url = instance.get_stream_url().stream_url

def print_step(step):
    print(step.text)

# Updated prompt: use Domino's website (which ideally avoids captchas) and order a pizza.
prompt_text = (
    "Go to Domino's Pizza website (https://www.dominos.com), log into my account "
    "Click on DELIVERY button"
    "Fill out the address with 475 Via Ortega, Stanford, CA 94305"
    "Select today's date for the delivery and the earliest time available"
    f"order the following items: {items}"
    "Click on continue checkout"
    # "Fill in with my information: Sophia Sharif, sophiasharif@ucla.edu, 650-555-1234"
    # "select I'll sign up later"
    # "select leave it at the door"
    # "click pay with cash upon delivery"
)

def attempt_order(model):
    """Run the ordering task using the given AI model."""
    return client.act(
        model=model,
        tools=[
            BashTool(instance),
            ComputerTool(instance),
            EditTool(instance),
        ],
        system=UBUNTU_SYSTEM_PROMPT,
        prompt=prompt_text,
        on_step=print_step,
    )

# Try using Anthropic as the primary model.
try:
    response = attempt_order(Anthropic())
except Exception as e:
    print("Encountered an error with Anthropic:", e)
    print("Falling back to Mistral model...")

# Check for any indications of bot protection (like "captcha" or "security check")
if "captcha" in response.text.lower() or "security check" in response.text.lower():
    print("Detected a captcha or security check in the response. Switching to Mistral model...")
    response = attempt_order(Anthropic())

print("Final response:")
print(response.text)

```

### client/next.config.ts

```typescript
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  /* config options here */
};

export default nextConfig;

```

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