🗝 KeyzHub
19Keys · community archive

chairman-agent

Autonomous chief-of-staff agent: heartbeat scheduling, cost guards, revenue dashboard. Stdlib-only Python.

◆ 2 commits history main
CLONE THIS PROJECT
git clone https://hub.19keys.com/chairman-agent.git

Public read access — no account needed. Build your own from it.

main
config/
README

Chairman Agent

Requirements: Python 3.9+ (stdlib only, no pip install), one Anthropic API key — everything else (Supabase, SendBlue, Telegram, macOS LaunchAgents) is optional — full walkthrough in SETUP.md.

An autonomous "chief of staff" agent: a conversational + heartbeat-driven Claude loop with server-side tool use (bash, file editing, web search), a cost guard that hard-stops spend at a monthly cap, a 32-persona agent roster it can delegate to, and a local dashboard to watch it work.

This is not a chatbot wrapper. It has three ways in — one-shot CLI chat, a scheduled heartbeat via macOS LaunchAgent, and two optional messaging bridges (iMessage via SendBlue, Telegram) — and one execution loop underneath all of them (agent.py:agent_loop). Whatever channel a message comes in on, it hits the same system prompt, the same tools, and the same cost guard.

Why it matters

Most "AI assistant" setups are a single prompt-response call. This one is a standing process: it runs on a timer whether or not you're looking at it, tracks its own spend and refuses to keep calling the API once a cap trips, and keeps a persona roster it can route sub-tasks to instead of trying to be one undifferentiated generalist. If you want an agent that has actual authority to act (run bash, edit files, search the web) on a schedule instead of only when you type to it, this is the shape of that.

What's in here

  • agent.py — the core loop. --chat, --chat-stdin, and --heartbeat modes; server-side tool use (bash, text editor, web search) against the Anthropic Messages API; retries until the model stops calling tools.
  • prompt.py — the agent's persona and standing operating rules (voice, execution standard, memory policy, mandated output structure). This is the actual "who is this agent" file — read it before you run anything, and rewrite it to be yours before you point it at your own business.
  • config/agents.py — a 32-persona roster (Executive / Oversight / Operations / Intelligence / Revenue / Community / Product tiers) the Chairman can delegate named tasks to. Each entry is a system prompt + provider + model.
  • cost_guard.py — monthly spend cap with an auto-cutoff threshold; wraps the API call so a runaway tool loop can't blow past budget mid-run.
  • credit_guard.py, musa_cadence.py — smaller guard/cadence utilities used by the heartbeat and Telegram bridge.
  • agent_bus.py — writes task/decision ledger rows to a Supabase project (optional — the agent runs fine with this unset, it just won't log).
  • webhook.py + sendblue.py — inbound/outbound iMessage bridge via SendBlue. No Mac Full Disk Access or AppleScript required. See SENDBLUE-SETUP.md for the full wiring walkthrough.
  • telegram_bridge.py — a stdlib long-poll Telegram bridge with one-tap commands (/pulse, /costs, /agent <ID> <task>, etc.) routed through the same agent_loop.
  • dashboard/ — a local ops dashboard (server.py + index.html + ops.html) for watching heartbeat status, chat history, token/cost usage, and connection health. Run it separately from the agent itself.
  • com.chairman.heartbeat.plist, com.chairman.webhook.plist — macOS LaunchAgent definitions for the scheduled heartbeat and the always-on webhook server.
  • CUTOVER.md — a worked example of migrating a Telegram bot + agent roster from one system into this one. Useful as a reference for how the pieces fit together, not something you need to run.

Not included: the data/, inbox/, done/, and dashboard/conversations/ directories, and dashboard/usage.jsonl — these are runtime state (task queue, processed tasks, real chat history, real usage logs) from the original deployment. They're gitignored here on purpose; your own instance will grow its own versions of these directories the first time you run it.

Quickstart

1. Install dependencies

The agent itself is stdlib-only (urllib, no pip install needed for agent.py, webhook.py, sendblue.py, telegram_bridge.py, dashboard/server.py). You need Python 3.9+ and a shell. That's it.

2. Set your environment

Copy env.example to .env and fill in real values (it's named without the leading dot in this repo so it survives KeyzHub's secret-filename scanner — rename it however you like once it's on your machine):

cp env.example .env

At minimum you need ANTHROPIC_API_KEY. Everything else (Supabase, SendBlue, Telegram) is optional and gates its own feature — leave a block unset and that bridge simply won't come up.

agent.py looks for its .env at <repo-root>/content-os/config/.env by default (ENV_FILE near the top of the file — it's a relative path one level up from this directory, named after the original project's layout). Either recreate that path, or change ENV_FILE in agent.py to point at wherever you put your .env.

3. Make the persona yours

Open prompt.py and config/agents.py. Both are written for 19Keys / Sovereign Mind Media by name. Replace the identity, voice rules, and roster with your own before running this against your own business — the agent will follow whatever persona and standing mandate you give it, verbatim.

prompt.py also references a memory file path (sovereign-wiki/memory/CHAIRMAN-MEMORY.md in the original deployment) that isn't part of this repo. Point it at wherever you want the agent's memory to live, or remove the reference if you're not wiring up persistent memory.

4. Run it once, by hand

python3 agent.py --chat "what should I focus on this week"

or pipe a message in:

echo "summarize today's priorities" | python3 agent.py --chat-stdin

Run a single heartbeat cycle manually (this is what the LaunchAgent will run on a schedule):

python3 agent.py --heartbeat

Drop a task file for the next heartbeat to pick up:

mkdir -p inbox
cat > inbox/$(date +%Y%m%d-%H%M)-task.md <<'EOF'
## Task
Whatever you want the agent to do next cycle.
EOF

5. Load the LaunchAgent (macOS scheduled heartbeat)

Edit the two .plist files first — they hardcode absolute paths (/Users/19keys/sovereign-empire-sweep/chairman-agent/...) that need to point at wherever you cloned this repo, and the heartbeat's StartCalendarInterval (currently: Mondays at 9:00am) is a placeholder schedule, not a recommendation.

cp com.chairman.heartbeat.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/com.chairman.heartbeat.plist

Unload:

launchctl unload ~/Library/LaunchAgents/com.chairman.heartbeat.plist

The webhook plist (com.chairman.webhook.plist) is only needed if you're wiring up the SendBlue iMessage bridge — see SENDBLUE-SETUP.md.

6. Run the dashboard (optional)

cd dashboard
python3 server.py

Defaults to http://localhost:8919. It reads the same logs/, data/, and dashboard/conversations/ directories the agent writes to at runtime — those will be empty until you've actually run the agent a few times.

Make it your own

  • Persona: prompt.py is the whole personality. Nothing here is magic — it's plain-language standing instructions the model follows. Rewrite it in your own voice.
  • Roster: config/agents.py defines every persona the Chairman can delegate to. Trim it to the roles you actually have, or expand it.
  • Cost cap: cost_guard.py holds the monthly spend cap and cutoff threshold — set your own number before you turn on the heartbeat.
  • Channels: iMessage (SENDBLUE-SETUP.md) and Telegram (telegram_bridge.py) are both optional and independent — wire up whichever channels you actually want to talk to the agent through.
  • Live grounding: agent.py:live_crm_context() pulls business numbers from a Supabase project into the system prompt if SUPABASE_URL / SUPABASE_ANON_KEY are set. If you don't have a Supabase project, leave those unset — the agent runs without live grounding.

A note on scope

This was built as one operator's personal chief-of-staff agent, wired to their specific business. The architecture (heartbeat + tool-using loop + cost guard + multi-channel bridge + persona roster) is the reusable part. The specific persona, roster, and business-numbers wiring are not — treat them as a fully worked example to fork and replace, not as configuration to leave as-is.