Command Layer · Always-On Daemon

Your Mobile
JARVIS.

Dispatcher is the command layer of Cortex. Send a Telegram message from your phone, and it classifies, routes, and executes tasks through CLI-based AI agents — Claude Code, Aider, or any agent that speaks stdin/stdout.

Get Started View on GitHub
dispatcher — daemon
# You're on the subway. You have an idea.
telegram> refactor the auth module, add proper error handling

[dispatcher] Classified: task | Project: myapp
[dispatcher] Phase 1: quick response in 1 turn...
[telegram] "On it (myapp). Spawning session."

[dispatcher] Phase 2: full execution (50 turns max)...
[runner] Agent session started (pid: 42891)
[progress] typing indicator... 4s interval
[progress] status update @ 3min

[runner] Agent session complete (14 turns, 4m 22s)
[telegram] "Done. Refactored into 3 modules, 24 tests passing."

# 30 seconds later, you reply from your phone...
telegram> also add rate limiting
[session] Smart follow-up detected (24 chars, within 5min window)
[session] Linking to session #42891, same project context
[dispatcher] Phase 1: quick response...
0
External Deps (Telegram)
3
Concurrent Sessions
2
Phase Algorithm
24/7
Always-On Daemon
Architecture

Phone to codebase. One message.

Dispatcher bridges the gap between your phone and your AI coding agents. Messages flow down, results flow back — with intelligent routing at every step.

RESULT
YOU
Phone / Anywhere
Telegram Bot API
TELEGRAM
Transport Layer
Long polling · Zero deps (urllib) · Auto-reconnect
Classify → Route → Execute
DISPATCHER
Command Layer
Message classification · Project routing · Fire-and-Refine · Session management
core.py config.py telegram.py runner.py session.py memory.py cli.py
Subprocess spawn · stdin/stdout
AI AGENT CLI
Execution Layer
Claude Code · Aider · Any CLI Agent
max_turns: 50 timeout: 1800s --dangerously-skip-permissions
Read · Write · Execute · Commit
YOUR CODEBASE
Target
Project-aware routing via keyword matching
Python >=3.10
Deps click>=8.0, pyyaml>=6.0
Telegram Zero deps (stdlib urllib)
Async asyncio event loop
Signals SIGTERM / SIGINT graceful shutdown
Features

Everything you need. Nothing you don't.

Dispatcher is designed to be invisible — it just works. Send a message, get a result. The intelligence is in the details.

?!

Smart Message Classification

Every incoming message is classified as task, status, or cancel. Status keywords like "status" check running sessions. Cancel keywords like "stop" or "cancel" terminate them. Everything else is a task.

Project-Aware Routing

Configure keyword-to-project mappings in YAML. Say "fix the webapp login" and Dispatcher detects "webapp", sets the working directory to ~/projects/webapp, and spawns the agent there. No manual context switching.

Fire-and-Refine Algorithm

Phase 1: instant 1-turn response ("On it. Detected project X."). Phase 2: full multi-turn execution with up to 50 turns. You get immediate feedback while the real work happens in the background.

Smart Follow-Up Detection

Short replies under 30 characters sent within 5 minutes are automatically linked to the most recent session. "also add tests" becomes a continuation, not a new task. Configurable threshold and time window.

x3

Concurrent Sessions

Run up to 3 parallel agent sessions simultaneously. "Refactor the API" and "update the docs" can execute in parallel on different projects. Each session is tracked independently with its own process ID.

Reply Threading

Bot responses are threaded back to your original message in Telegram. When you have 3 concurrent tasks, you always know which result belongs to which request. Clean, organized conversation history.

...

Progress Updates

Typing indicators every 4 seconds so you know the agent is alive. Status updates every 3 minutes for long-running tasks. Never left wondering "is it still working?" All intervals are configurable.

macOS LaunchAgent

One command to install as an always-on daemon: dispatcher install. Starts on boot, auto-restarts on crash, runs in the background. dispatcher uninstall to remove. Also supports foreground mode for debugging.

Algorithm

Fire-and-Refine

The core execution algorithm. Designed to give you instant feedback while the real work happens asynchronously.

1

Fire

~2 seconds

Classify message — task, status, or cancel

Detect project — keyword matching against configured projects

Quick response — 1-turn agent call for instant acknowledgment

Send to Telegram — "On it (myapp). Spawning full session."

2

Refine

Up to 30 min

Spawn agent process — full CLI session with project context

Multi-turn execution — up to 50 turns of autonomous work

Progress updates — typing indicators (4s) + status (3min)

Deliver result — threaded reply with full summary to Telegram

Why two phases? Because waiting 5 minutes for "I heard you" is painful. Phase 1 confirms receipt in seconds. Phase 2 does the real work. You can keep walking, keep meeting, keep living — the result arrives when it's ready.

Configuration

YAML config. Environment overrides.

Everything is configured through a single YAML file. Every value can be overridden with environment variables for deployment flexibility.

~/.config/dispatcher/config.yaml YAML
# Telegram connection
telegram:
  bot_token: "YOUR_BOT_TOKEN"
  chat_id: 123456789

# Agent configuration
agent:
  command: "claude"
  args: ["-p", "--dangerously-skip-permissions"]
  max_concurrent: 3         # parallel sessions
  timeout: 1800            # 30 min max per session
  max_turns: 50           # max agent turns

# Behavior tuning
behavior:
  poll_timeout: 30         # Telegram long-poll seconds
  progress_interval: 180    # status update every 3min
  recent_window: 300        # 5min follow-up window
  cancel_keywords: [cancel, stop]
  status_keywords: [status]

# Project routing (keyword → directory)
projects:
  webapp:
    path: ~/projects/webapp
    keywords: [web, frontend, webapp]
  api:
    path: ~/projects/api-server
    keywords: [api, backend, server]
  ml:
    path: ~/research/ml-pipeline
    keywords: [ml, model, training, data]
Environment Variable Overrides ENV
# Every config value can be overridden via env vars

DISPATCHER_BOT_TOKEN="your-token"     # telegram.bot_token
DISPATCHER_CHAT_ID=123456789        # telegram.chat_id
DISPATCHER_AGENT_COMMAND="claude"    # agent.command
DISPATCHER_MAX_CONCURRENT=3        # agent.max_concurrent
DISPATCHER_TIMEOUT=1800            # agent.timeout
DISPATCHER_DATA_DIR="~/.dispatcher" # data directory

# Env vars take precedence over YAML config
# Useful for Docker, CI/CD, or secrets management
CLI Reference

Six commands. That's it.

Dispatcher ships with a minimal, opinionated CLI built on Click. Each command does one thing well.

$ dispatcher init
Interactive setup wizard. Walks you through Telegram bot token, chat ID, project directories, and agent configuration. Generates config.yaml. Run once, forget forever.
$ dispatcher start
Start the daemon in foreground mode. Begins polling Telegram for messages immediately. Useful for development and debugging. Ctrl+C for graceful shutdown.
$ dispatcher install
Install as a macOS LaunchAgent. Starts on login, auto-restarts on crash, runs silently in the background. The "set and forget" option for daily use.
$ dispatcher uninstall
Remove the LaunchAgent cleanly. Stops the running daemon, removes the plist file, and unloads from launchd. Clean, reversible, no leftover artifacts.
$ dispatcher status
Check if the daemon is running, how many active sessions exist, uptime, and last message processed. Quick health check from the terminal.
$ dispatcher logs [-n 50] [-f]
View daemon logs. -n N for last N lines, -f for live follow mode (like tail -f). Streams structured logs with timestamps, levels, and context.
Module Reference

Seven modules. Clean boundaries.

Each module has a single responsibility. The dependency graph is acyclic. Easy to understand, easy to extend, easy to test.

dispatcher/core.py

Core

The brain. Main asyncio event loop, message classification (task/status/cancel), project routing via keyword matching, and the Fire-and-Refine orchestration. Entry point for all message processing.

event loop message classifier project router fire-and-refine
dispatcher/config.py

Config

YAML configuration loader with environment variable overrides. Handles default values, path expansion, and validation. All configuration flows through this module.

YAML parser env overrides defaults validation
dispatcher/telegram.py

Telegram

Telegram Bot API client built on stdlib urllib only. Zero external dependencies. Long polling, message sending, reply threading, typing indicators, and auto-reconnect on network failures.

urllib only long polling reply threading typing indicators auto-reconnect
dispatcher/runner.py

Runner

Agent subprocess manager. Spawns CLI processes (Claude Code, Aider, etc.), captures stdout/stderr, enforces timeouts, and manages concurrent session limits. Graceful termination on cancel.

subprocess spawn stdout capture timeout enforcement concurrent limits
dispatcher/session.py

Session

Session tracking and smart follow-up detection. Tracks active sessions by ID, links short replies (<30 chars) within a 5-minute window to the most recent session. Manages session lifecycle: create, update, complete.

session tracking follow-up detection 30-char threshold 5-min window
dispatcher/memory.py

Memory

Persistent user preferences stored as markdown. Remembers project preferences, common patterns, and user-specific context across sessions. Loaded into agent prompts for personalized execution.

markdown storage user preferences cross-session prompt injection
dispatcher/cli.py

CLI

Click-based command-line interface. Six commands: init, start, install, uninstall, status, logs. Handles LaunchAgent plist generation, log streaming, and interactive setup wizard with validation.

click framework setup wizard LaunchAgent log streaming
Quick Start

Four steps. Five minutes.

From zero to "controlling Claude from your phone" in under 5 minutes. All you need is a Telegram bot token.

1 — Install Python >=3.10
# Install from PyPI
pip install dispatcher-agent

# Or from source
git clone https://github.com/zzhiyuann/dispatcher.git
cd dispatcher && pip install -e .
2 — Initialize Interactive wizard
dispatcher init

> Telegram bot token: 7740709485:AAF35Lk...
> Your chat ID: 7542082932
> Agent command: claude
> Max concurrent sessions: 3
> Add project? (name): webapp
>  Path: ~/projects/webapp
>  Keywords: web, frontend, webapp
> Add another project? n

✓ Config saved to ~/.config/dispatcher/config.yaml
✓ Ready to start.
3 — Start Choose your mode
# Foreground (for testing)
dispatcher start
✓ Dispatcher running — listening for Telegram messages
✓ Ctrl+C to stop

# Or install as always-on daemon (recommended)
dispatcher install
✓ LaunchAgent installed
✓ Starts on login, auto-restarts on crash
✓ Dispatcher is now always on
4 — Use From your phone
# Open Telegram on your phone and message your bot

You: refactor the auth module, add error handling
Bot: On it (webapp). Spawning full session.

# ...minutes later...
Bot: Done. Refactored auth into 3 modules.
     Added try/catch blocks, custom error classes,
     and 24 unit tests. All passing. Pushed to git.

# Quick follow-up (auto-linked to same session)
You: also add rate limiting
Bot: Got it. Continuing session...

# Check on running tasks
You: status
Bot: 2 active sessions: webapp (4m), api (1m)

# Cancel a task
You: cancel
Bot: Cancelled most recent session (webapp).