Modular bridge that connects chat platforms to AI agents. Each layer is independent — swap platforms or agents without touching the others.
Currently supports: Slack + Claude Code
┌──────────────┐ ┌──────────┐ ┌──────────────┐
│ Platform │────▶│ Bridge │────▶│ Agent │
│ (Slack) │◀────│ (Router) │◀────│ (Claude Code)│
└──────────────┘ └──────────┘ └──────────────┘
Session owner Pure routing Purely invoked
Locking & render Key → ID map Yields events
UI logic Concurrency No UI knowledge
- Python 3.12+
- uv
- Claude Code CLI installed and authenticated
git clone https://github.com/htkuan/ai-agent-bridge.git
cd agent-bridge
uv synccp .env.example .envEdit .env with your tokens:
# Required for Slack
AGENT_BRIDGE_SLACK_BOT_TOKEN=xoxb-your-bot-token
AGENT_BRIDGE_SLACK_APP_TOKEN=xapp-your-app-level-token
# Claude Code working directory
AGENT_BRIDGE_CLAUDE_WORK_DIR=/path/to/your/projectSee Environment Variables for the full list.
- Create a Slack App at api.slack.com/apps
- Enable Socket Mode → generate an App-Level Token (
xapp-...) - Add Bot Token Scopes (OAuth & Permissions):
app_mentions:read,chat:write,files:write,im:history,im:read
- Subscribe to Events:
app_mention,message.im
- Install to workspace → copy Bot User OAuth Token (
xoxb-...)
uv run agent-bridge| Action | How |
|---|---|
| Channel | @AgentBridge help me refactor this function |
| DM | Send a direct message to the bot |
| Continue conversation | Reply in the same Slack thread |
| Attach files | Upload files in the message — the agent receives download URLs |
Each Slack thread is one agent session. The agent remembers context within a thread.
The system has three independent layers:
| Layer | Role | Docs |
|---|---|---|
| Platform Adapter | Owns session semantics, per-session locking, UI rendering | Slack Adapter |
| Bridge | Routes messages, maps session keys → IDs, enforces concurrency | Core — see below |
| Agent Controller | Executes prompts, yields generic events | Claude Agent |
All agent output flows through generic events — the shared language between agents and platforms:
| Event | Description |
|---|---|
Processing |
Slot acquired, agent is starting |
TextDelta |
Incremental text from agent |
StatusUpdate |
Agent performing an action (tool use, etc.) |
UserQuestion |
Agent asking user for input |
Completion |
Agent finished (with cost, duration, error status) |
- User sends message → Platform constructs session key (e.g.
slack:{channel}:{thread_ts}) - Bridge resolves key → UUID session ID (creates new if first message)
- Agent runs with session ID (new session or resume existing)
- Sessions expire after configurable TTL (default 72h)
| Variable | Required | Default | Description |
|---|---|---|---|
ANTHROPIC_API_KEY |
No | — | API key for the Claude Code CLI (skip if already authenticated via claude login) |
AGENT_BRIDGE_SLACK_BOT_TOKEN |
Yes (if using Slack) | — | Slack Bot User OAuth Token (xoxb-...) |
AGENT_BRIDGE_SLACK_APP_TOKEN |
Yes (if using Slack) | — | Slack App-Level Token for Socket Mode (xapp-...) |
AGENT_BRIDGE_CLAUDE_WORK_DIR |
No | . |
Working directory for Claude Code |
AGENT_BRIDGE_CLAUDE_PERMISSION_MODE |
No | acceptEdits |
Claude permission mode |
AGENT_BRIDGE_CLAUDE_TIMEOUT_SECONDS |
No | 600 |
Per-invocation timeout (seconds) |
AGENT_BRIDGE_CLAUDE_WORKTREE_ENABLED |
No | false |
Run each session in an isolated git worktree (requires origin/HEAD) |
AGENT_BRIDGE_SESSION_STORE_PATH |
No | ./sessions.json |
Session mapping file path |
AGENT_BRIDGE_SESSION_TTL_HOURS |
No | 72 |
Session TTL (hours) |
AGENT_BRIDGE_MAX_CONCURRENT_SESSIONS |
No | 5 |
Max concurrent agent processes |
AGENT_BRIDGE_HEARTBEAT_ENABLED |
No | false |
Enable the heartbeat platform — fires a fixed prompt on a fixed interval |
AGENT_BRIDGE_HEARTBEAT_INTERVAL_MINUTES |
Yes (if heartbeat enabled) | — | Interval between heartbeat ticks (minutes) |
AGENT_BRIDGE_HEARTBEAT_PROMPT |
Yes (if heartbeat enabled) | — | Prompt sent on every heartbeat tick |
AGENT_BRIDGE_HEARTBEAT_STATE_PATH |
No | ./heartbeat.json |
Last-run timestamp path (used for restart catch-up) |
AGENT_BRIDGE_LOG_LEVEL |
No | INFO |
DEBUG / INFO / WARNING / ERROR |
Create platforms/{name}/ with config.py and adapter.py. Implement the PlatformAdapter protocol. Define your session key format. See Slack Adapter docs for reference.
Create agents/{name}/ with config.py, controller.py, and events.py. Implement the AgentController protocol — your run() yields BridgeEvents. See Claude Agent docs for reference.
Neither change requires modifying the bridge, the other agent, or the other platform.
# Run tests
uv run pytest tests/ -v
# Run with debug logging
AGENT_BRIDGE_LOG_LEVEL=DEBUG uv run agent-bridgeCommits follow Conventional Commits (lowercase
types: feat:, fix:, ...) and are enforced on PRs. Merging to main cuts a release
automatically — see docs/releasing.md.
MIT