Type a high-level goal in plain English — "research my top 3 competitors and draft an outreach email" — and the system:
- Decomposes it into a dependency graph of subtasks (a DAG, not a list)
- Assigns each subtask to a specialized AI agent (Research / Calendar / Email)
- Executes them with real tools (Tavily web search, Google Calendar, Gmail) through a dependency-aware job queue
- Streams progress live — you watch the graph's nodes flip
queued → running → done - Pauses for approval before anything leaves the sandbox (creating a calendar event, creating a Gmail draft — email is never auto-sent)
- Replans dynamically — after each subtask completes, the orchestrator re-evaluates the remaining plan and can add or cancel tasks
- Synthesizes a final report when everything settles
┌──────────────────────┐ HTTP + SSE ┌──────────────────────────────┐
│ apps/web (Next.js) │◄─────────────────────────►│ apps/server (Fastify) │
│ - goal input │ │ - auth (cookie sessions) │
│ - live task graph │ │ - REST: goals / artifacts │
│ - activity feed │ │ - SSE relay (Redis pub/sub) │
│ - approval modal │ └──────────┬───────────────────┘
└──────────────────────┘ │
BullMQ jobs│ + pub/sub
▼
┌─────────────────────────────────────────────────────┐
│ Workers (same process, split-ready) │
│ │
│ plan queue ──► Orchestrator (Claude Opus 4.8) │
│ goal → task DAG → Postgres │
│ │
│ task queue ──► dispatch by agent type: │
│ Research Agent ── web_search (Tavily) │
│ Calendar Agent ── list events, PROPOSE events │
│ Email Agent ───── draft emails (never sends) │
│ │
│ Execution engine: │
│ - enqueue tasks whose deps are all done │
│ - after each completion: replan check (≤3/goal) │
│ - external effects pause in awaiting_approval │
│ - all settled → Writer Agent → final report │
└───────────┬───────────────────────┬─────────────────┘
│ │
┌──────▼──────┐ ┌──────▼──────┐
│ Postgres │ │ Redis │
│ (Neon) │ │ (Upstash) │
│ users/goals │ │ BullMQ jobs │
│ tasks/tool_ │ │ + pub/sub │
│ calls/artif.│ │ for SSE │
└─────────────┘ └─────────────┘
Key design points
- Custom DAG state machine, not a framework. Tasks live in Postgres with a
depends_onarray. The engine enqueues anypendingtask whose dependencies are alldone, using atomic status transitions (UPDATE … WHERE status='pending') so concurrent workers never double-execute. - Real replanning. After every completed task the orchestrator (an LLM call with structured output) reviews the remaining graph and may add or cancel tasks — capped at 3 replans per goal, and cancellations cascade to dependents.
- Human-in-the-loop as a hard boundary. Agents can only propose external effects. Proposals persist as
artifactsinpending_approval; the owning task parks inawaiting_approval; the Google API call happens only inside the approval endpoint, after an explicit user decision. Gmail integration can only create drafts — there is no code path that sends email. - Every tool call is logged to the
tool_callstable (input, output, error flag, mock flag). - Live UX via SSE. Workers publish events to Redis pub/sub; the API relays them per-goal over Server-Sent Events; the frontend reduces them into the task graph, activity feed, and approval modal.
- Mock fallbacks. Tavily and Google integrations fall back to clearly-labeled mock data when their credentials are absent, so the whole pipeline is demoable with just an Anthropic key.
apps/server Fastify API + BullMQ workers + orchestrator + agents
apps/web Next.js App Router frontend
packages/shared Types shared between the two (DTOs, SSE event union)
Prerequisites: Node 20+, and free accounts for Neon (Postgres) and Upstash (Redis).
npm install
# 1. Configure the server
cp apps/server/.env.example apps/server/.env
# - DATABASE_URL: Neon connection string
# - REDIS_URL: Upstash *TCP* URL (rediss://default:...@xxx.upstash.io:6379)
# - ANTHROPIC_API_KEY
# - optional: TAVILY_API_KEY, GOOGLE_CLIENT_ID/SECRET/REFRESH_TOKEN
# 2. Configure the web app
cp apps/web/.env.local.example apps/web/.env.local
# 3. Create the database tables
npm run db:push
# 4. Run both apps (web on :3000, API on :4000)
npm run devRegister an account at http://localhost:3000/register, submit a goal, and watch the graph.
Create an OAuth client in Google Cloud Console, enable the Calendar and Gmail APIs, and obtain a refresh token authorized for https://www.googleapis.com/auth/calendar and https://www.googleapis.com/auth/gmail.compose (the OAuth Playground is the quickest way). Put the client ID, secret, and refresh token in apps/server/.env. Without them, calendar/email actions are simulated and labeled as mock.
v1 uses a single server-configured Google identity. The upgrade path to per-user OAuth is a
google_accountstable keyed by user + a standard OAuth redirect flow; every call site already goes throughsrc/tools/google.ts.
| Table | Purpose |
|---|---|
users |
email/password auth |
sessions |
cookie session tokens |
goals |
the original prompt, status, final summary, replan counter |
tasks |
subtasks: agent, status, depends_on[], result |
tool_calls |
log of every tool invocation (input/output, error + mock flags) |
artifacts |
outputs: email drafts, proposed events, final summaries + approval state |
Task lifecycle: pending → queued → running → (awaiting_approval →) done | failed | cancelled
- Frontend: Vercel (
apps/web, setNEXT_PUBLIC_API_URL) - Backend: Render or Railway (
apps/server,npm start); pointDATABASE_URL/REDIS_URLat the same Neon/Upstash instances and setWEB_ORIGINto the Vercel URL - The workers run inside the API process; to scale, move
startWorkers()(one call site insrc/index.ts) into a dedicated worker service.