🎬 Interactive Demo — No installation required

2BAgent in action

Walk through every major feature — from capturing a thought in Discord to how the Think-Act-Observe cycle synthesizes your context into a morning briefing. Then explore the full system architecture and service map.

See every layer, step by step

Each tab shows a realistic trace of what happens when you use that feature — from the command you type to what the system does internally.



You type a thought in Discord. Here is exactly what happens in the next 200ms.

💬
Discord
/capture command
🌐
FastAPI
POST / · port 8900
🧬
Extractor
type · people · topics
🤖
Ollama
1024-dim embedding
🗄️
PostgreSQL
thoughts + pgvector

Discord → API

#second-brain
R
ram today at 9:14 AM
/capture DECISION: use asyncpg over SQLAlchemy — raw SQL gives us full control over query planning and index hints
2B
2BAgent APPtoday at 9:14 AM
✅ Captured
Type: decision
Topics: #database #architecture
People: —
Stored · embedding 1024d · 143ms

What gets stored in PostgreSQL

thoughts table — live row
id: "a3f8c2d1-..."
raw_text: "DECISION: use asyncpg..."
source: "discord"
metadata: {
"type": "decision",
"topics": ["database","architecture"],
"people": []
}
embedding: vector(1024) [0.021, -0.094, ...]
fts_vector: tsvector (GIN indexed)
created_at: "2026-06-16T09:14:22Z"
Supported prefixes (case-insensitive):
DECISION: insight: ToDo: todo- task: meeting: PERSON: — or plain text → auto-classified as note

Every morning at 7:00 AM the agent synthesizes your last 24 hours of captures, open tasks, calendar events, and recent emails into a single briefing — then posts it to your Discord agent channel.

Sample output — Discord

🌅
Good morning, Ram — here's your Monday
2BAgent · morning briefing · Mon Jun 16, 7:00 AM · via Gemini
Yesterday's captures
9 thoughts captured — 2 decisions, 3 insights, 3 tasks, 1 note
Most active topics: #database (4×), #architecture (3×), #product (2×)
Build habit: marked complete at 4:12 PM
Open tasks 3 open
"Review PR #47 for asyncpg pool changes" — 4 days old stale
"Follow up with Alice re: Q2 planning" — 7 days old stale
"Migrate legacy cron jobs to agent runner" — added yesterday
Today's calendar
10:00 AM — Engineering standup (30 min) · Alice, Ben, Carol
2:00 PM — Q2 planning · Alice confirmed · agenda not set
6 unread emails since Friday · 2 from Alice (re: Q2)
Patterns & connections
"database" has appeared in 11 of your last 14 thoughts — this is becoming a major focus area. Consider a decision log.
"Alice" appears in 3 open tasks and 2 calendar events today. High-leverage day for that relationship.
💡 Your decision on asyncpg 3 weeks ago is now showing up as a follow-on insight. The pattern holds.

How the briefing is generated

🧠
Think
Gather context from all sources
Agent fetches last 24h thoughts from DB, reads Gmail inbox (OAuth2), pulls today's calendar events, checks HABITS.md state, loads MEMORY.md for context. All data is assembled into the synthesis prompt.
list_recent(24h) gmail.list_inbox() calendar.today() habits.state()
Act
Synthesize via AI (Claude CLI → Gemini)
The assembled context is passed to the synthesis tier: tries Claude CLI first (your subscription), falls back to Gemini API if unavailable, falls back to a raw text summary as last resort. Never blocks.
synthesis_tier=gemini model=gemini-2.0-flash
tokens_in=2841 tokens_out=612 latency_ms=1840
👁
Observe
Deliver to Discord · write to memory
Result written to agent_runs table, appended to USER.md (episodic memory), and posted to your Discord agent channel. The next synthesis automatically includes this run as context.
insert_agent_run() append USER.md post_discord()
Scheduled jobs that deliver to Discord:
morning_briefing7:00 AM daily · synthesis + email + calendar + tasks
evening_review9:00 PM daily · synthesis + habit check
task_staleness8:00 AM daily · nudge for tasks idle 48h+
relationship_decayMon 9:00 AM · surface fading relationships
pattern_recognitionSun 6:00 AM · weekly deep analysis
weekly_reviewSun 6:00 PM · topic clusters + forgotten items
idea_rouletteMon 10:00 AM · surface a forgotten old thought
memory_consolidation2:00 AM daily · promote facts to MEMORY.md

Type any question or request in your interactive Discord channel. The ReAct loop (Reason + Act) plans which tools to call, executes them, observes the results, and replies — all in one response.

Discord conversation

#agent-interactive
R
ram today at 3:45 PM
research my asyncpg pool task and find me the best approach with links
🤖
2BAgent APPtoday at 3:45 PM
🤖 Researching — cycle 1/3
Found task: "Migrate legacy asyncpg connection to pool" (3 days old)

Searching for best practices…
✅ Research complete
Recommended approach: asyncpg.create_pool(min_size=2, max_size=10)

Key finding: Your existing shared _conn pattern causes "another operation in progress" errors under concurrent reconcile + job execution — confirmed in your own log from Jun 5.

Resources:
• asyncpg Pool docs — connection lifecycle, acquire/release
• pgbouncer vs asyncpg pool — when to use each
• Your own insight from 2 weeks ago: "asyncpg bottleneck on 50+ writes" aligns exactly with this

Next step: Replace _conn in services/agent/database.py with create_pool() and update all callers to use async with pool.acquire() as conn.

ReAct loop — internal trace

🧠
Reason · cycle 1
Parse request, plan tools
"User wants me to find an asyncpg pool task and research it. I'll search tasks first to get the exact text, then call web_search for implementation guidance."
list_tasks("asyncpg")
Act · cycle 1
Call list_tasks tool
list_tasks → {
"found": 1,
"task": "Migrate legacy asyncpg
connection to pool",
"age_hours": 72,
"status": "open"
}
🧠
Reason · cycle 2
Task found — now research
"I have the exact task. Now search for asyncpg connection pool best practices and cross-reference with the user's existing insights about the bottleneck issue."
web_search("asyncpg create_pool best practices") semantic_search("asyncpg bottleneck")
👁
Observe · synthesize
Merge results → final reply
Web results + user's own insight from 2 weeks ago merged. Cross-reference with agent log (Jun 5 error confirms the concurrency issue). Synthesize into actionable reply with specific file + code change.
Available tools in the interactive ReAct loop:
list_tasks · complete_task · reopen_task · research_task · add_task_note · semantic_search · list_recent · get_stats · web_search · get_calendar_event · list_calendar_events · list_emails

Full task lifecycle via Discord slash commands. Capture in any format, list open tasks, mark done, reopen, or ask the agent to research what to do next.

Open task list — /todo list

Open Tasks 4 open
Migrate asyncpg connection to pool in database.py
⚠ 3d
Review PR #47 for asyncpg pool changes
⚠ 4d
Follow up with Alice re: Q2 planning meeting agenda
⚠ 7d
Set up Ollama watchdog cron job on this machine
1d

After /todo done asyncpg pool

Recently completed done
Migrate asyncpg connection to pool in database.py
just now
Discord slash commands
/todo add Add a task. Accepts any format — "ToDo:", "todo -", "TASK:", or plain text.
/todo list [status] List tasks: open (default), done, or all.
/todo done Fuzzy-match a task by partial text and mark it complete. The agent confirms the match before acting.
/todo reopen Reopen a completed task — sets status back to open with a note.
/todo research Triggers the ReAct loop to research the task and return concrete next steps + links.
/todo note Append a context note to a task without changing its status.
/tasks Quick shortcut — same as /todo list open.

Case-insensitive capture — all map to type=task

extractor.py — supported prefixes
# All of these → metadata.type = "task"
"TODO: migrate the cron jobs"
"ToDo: migrate the cron jobs"
"todo - migrate the cron jobs"
"task: migrate the cron jobs"
"TASK: migrate the cron jobs"
"Action: migrate the cron jobs"
"Fix: the reconcile loop error"
"Review: PR #47 before Thursday"

# Also works via /todo add (no prefix needed):
"migrate the cron jobs" → task

Every service, mapped

2BAgent is built from five cooperating processes and a shared PostgreSQL data layer. All local. No cloud required.

User entry points
💬
Discord
discord.py 2.x
🌐
REST API
HTTP + Web UI
🧩
Claude Desktop
MCP client
Core services — Docker Compose + host
🤖
Discord Bot
Docker container
services/discord/bot.py
FastAPI
:8900
services/api/main.py
🧠
Agent Runner
:8766
services/agent/runner.py
🔌
MCP Server
stdio transport
services/mcp/server.py
Data layer — always local
🗄️
PostgreSQL 16 + pgvector
127.0.0.1:5432
thoughts · tasks · agent_runs · scheduled_jobs
🤖
Ollama (host)
localhost:11434
mxbai-embed-large · 1024-dim
External integrations — opt-in
📧
Gmail
OAuth2 token
📅
Google Calendar
OAuth2 token
📓
Obsidian
Local REST API
🌐
Brave Search
API key · optional
🧠
Claude CLI
your subscription
Gemini API
fallback only
FastAPI / HTTP service
Agent / scheduler
MCP / stdio
Data / local
External / opt-in

Key data flows

Source Path What happens Tech
Discord /capture Bot → :8900/ Thought extracted, embedded by Ollama, stored in PostgreSQL with vector + FTS index PythonasyncpgOllama
MCP capture tool stdio → FastAPI Claude Desktop calls create_task or semantic_search MCP tool → HTTP to :8900 MCP SDKDocker
APScheduler 7am Runner → Ollama → Claude CLI → Discord Morning briefing: fetch recent thoughts + Gmail + Calendar → synthesize via Claude/Gemini → post to agent channel APSchedulerClaude CLI
Discord /ask Bot → :8766/ask → ReAct loop Interactive agent: multi-step Reason + Act cycle across up to 12 tools, reply to Discord channel ReActDiscord.py
Gmail fetch Runner → gmail.py → Google API OAuth2 token refreshed, inbox fetched, body wrapped in guardrails XML before synthesis prompt OAuth2guardrails
Search query Client → :8900/search → DB Query embedded by Ollama, hybrid SQL merges cosine (0.7×) + FTS ts_rank (0.3×), returns ranked results pgvectorOllama
Memory consolidation 2am Runner → consolidator.py → USER.md / MEMORY.md Nightly: promote durable facts from episodic USER.md to curated MEMORY.md via AI synthesis PythonClaude/Gemini

Five services, one stack

Each service has a single responsibility. They communicate over HTTP and a shared PostgreSQL database — no message queues, no pub/sub.

FastAPI (Capture API)
:8900 · Docker · services/api/
The primary data ingestion endpoint. Accepts thought captures via POST, auto-classifies type/topics/people, generates 1024-dim embeddings via Ollama, stores to PostgreSQL with pgvector. Also serves the web UI, retrieval endpoints, and agent monitoring pages.
FastAPIasyncpgOllamaDocker
🧠
Agent Runner
:8766 · Docker --profile agent · services/agent/
Runs APScheduler with 8 default cron jobs synced from the DB every 5 minutes. Executes Think-Act-Observe cycles, manages episodic and curated memory files (USER.md, MEMORY.md), handles Gmail/Calendar context, and exposes the /ask endpoint for the Discord interactive agent.
APSchedulerClaude CLIGeminiDocker
💬
Discord Bot
Docker · services/discord/
Provides the user-facing slash command interface: /capture, /search, /recent, /todo, /tasks, /stats, /agent, /health. Routes to FastAPI for data ops and to the Agent Runner for interactive ReAct sessions. Separate interactive channel for open-ended conversation.
discord.py 2.xslash commandsDocker
🔌
MCP Server
stdio transport · host Python · services/mcp/
Spawned by Claude Desktop as a subprocess. Exposes 32 tools over the MCP stdio protocol — semantic search, task CRUD, Gmail/Calendar read, Obsidian read+write, habit logging, agent memory read/write, web search, Discord messaging. Never opens a network port.
MCP SDK32 toolshost Python
🗄️
PostgreSQL 16 + pgvector
127.0.0.1:5432 · Docker · schema/init.sql
The single source of truth. Tables: thoughts (with 1024-dim vector + GIN FTS index), agent_runs (with compound TAO cycle fields), scheduled_jobs (APScheduler-compatible cron rows), and settings. Migrations tracked in migrate_r2 through migrate_r8.
PostgreSQL 16pgvectorDocker
🤖
Ollama (host process)
localhost:11434 · host · not containerized
Runs the mxbai-embed-large model locally for 1024-dimensional text embeddings. Called by both FastAPI (capture) and the search endpoint. Intentionally kept on the host (not in Docker) so GPU acceleration works natively. Zero cloud calls — all embeddings stay on your machine.
Ollamamxbai-embed-largehttpx