Personal Chief of Staff (MVP)
A multi-agent Chief of Staff: a LangGraph orchestrator coordinates Email, Calendar, Task, Research, and Memory agents to produce a daily Morning Briefing β top priorities, why they matter, risks to watch, and recommended next actions β with human approval required before any consequential action (sending an email, modifying your calendar) actually happens.
See docs/Multi_Agent_Personal_Chief_of_Staff_MVP_Requirements.pdf
and docs/Personal_Chief_of_Staff_Architectural_Considerations_v2.pdf
for the full requirements and architecture this implementation follows.
Architecture at a glance
- Backend: FastAPI + LangGraph (Python), SQLite for structured data + a brute-force cosine-similarity embedding store for semantic memory recall.
- Model gateway: all agents call a single interface (
app/models/gateway.py) backed by LiteLLM.MODEL_PROVIDER(.env) switches between OpenAI (default,gpt-5-nano), Claude (Anthropic API), and a local Ollama model (qwen2.5:7b-instruct). No agent code calls the provider directly β swapping providers means changing.env/the gateway, not the agents. - Frontend: React + Vite (TypeScript) β Morning Briefing, Approvals queue, Ask (chat), and Settings (memory + connection status).
- Integrations: real Gmail, Google Calendar, and Google Tasks via OAuth; DuckDuckGo web search (no API key) for the Research Agent.
- Policy:
app/policy/guardrails.pyis deterministic, non-LLM code that decides which action kinds require human approval. Read actions run automatically; sending email, modifying calendar events, or writing tasks always create a pending approval first. - Observability: LangSmith tracing (optional) to monitor agent performance, debug issues,
and track token usage across all API calls. See
LANGSMITH_SETUP.md.
This MVP intentionally skips Docker/CI, Postgres/pgvector, and formal observability tooling (OpenTelemetry/Phoenix) called out in the architecture doc as later upgrades β SQLite and basic logging are sufficient to prove the MVP locally.
Prerequisites
- Python 3.14 and Node.js 24+ (already verified on this machine).
- A model provider β set
MODEL_PROVIDERinbackend/.env:openai(default): setOPENAI_API_KEYinbackend/.env(get a key at platform.openai.com/api-keys). Usesgpt-5-nanoby default β OpenAI's cheapest current tier, well suited to this app's short structured-output calls.claude: setANTHROPIC_API_KEYinbackend/.env(get a key at console.anthropic.com).ollama: run Ollama locally with a model pulled:
Verify it's reachable:ollama pull qwen2.5:7b-instructcurl http://localhost:11434/api/tags
- Google Cloud OAuth credentials (see below) β required for real Gmail/Calendar/Tasks access. Without this, the app still runs and the briefing gracefully degrades (shows a "source unavailable" notice instead of crashing).
Google Cloud OAuth setup (one-time)
- Go to console.cloud.google.com and create a new project (or select an existing one).
- Go to APIs & Services β Library and enable these three APIs:
- Gmail API
- Google Calendar API
- Tasks API
- Go to APIs & Services β OAuth consent screen. Choose External (unless you have a Workspace org), fill in the required fields (app name, your email), and add yourself as a test user. You don't need to submit for verification for personal/local use.
- Go to APIs & Services β Credentials β Create Credentials β OAuth client ID.
- Application type: Desktop app
- Name it anything (e.g. "Chief of Staff MVP")
- Download the resulting JSON and save it as:
backend/data/credentials.json - The first time the backend needs Google access, it opens a local browser window for you
to sign in and grant access (or trigger it manually via Settings β Connect Google
account in the app, or
POST http://localhost:8000/status/google/authorize). A token is then cached atbackend/data/token.jsonand refreshed automatically.
Scopes requested: gmail.readonly, gmail.send, calendar.readonly, calendar.events,
tasks. Reads never require approval; the send/events/tasks write scopes are only
exercised after you approve a specific proposed action in the Approvals page.
Running it
Backend
cd backend
python -m venv venv
./venv/Scripts/pip install -r requirements.txt # Windows; use venv/bin/pip on macOS/Linux
cp .env.example .env # adjust if needed
./venv/Scripts/uvicorn app.main:app --reload --port 8000
Frontend
cd frontend
npm install
npm run dev
Open the printed local URL (typically http://localhost:5173).
Tests
cd backend
./venv/Scripts/pytest tests/ -v
The suite covers all 12 MVP acceptance requirements with fixture data β no live Ollama, Google, or network access needed to run it.
Using the app
- Morning Briefing (
/) β today's top 3 priorities with explanations, risks to watch, and a plain-language summary. Refresh any time. - Approvals (
/approvals) β anything consequential the Chief of Staff wants to do (e.g. reply to an email) waits here. You see exactly what will happen before approving or denying; nothing is sent/changed until you click Approve. - Ask (
/chat) β ask an ad hoc question. Check "needs current external information" to route the request through the Research Agent (web search) instead of your inbox/calendar. - Settings (
/settings) β connect your Google account, check whether the configured model provider is reachable, and inspect/add/delete stored goals & preferences. A new explicit preference supersedes the previous one of the same kind.
Known MVP-scope trims
- No Docker/CI pipeline yet β run both servers locally as shown above.
- SQLite instead of Postgres+pgvector β fine at personal-use data volumes; swappable later.
- No OpenTelemetry/Phoenix tracing β structured logging only.
- No formal WCAG audit β semantic HTML, keyboard navigation, and visible focus states are followed as baseline practice in the React UI.