Documentation

Getting started

Binary install from GitHub Releases, setup, pair, first topic.

No Bun or source checkout required for normal use. Download a release binary, run setup, pair once.

Requirements

  • A Telegram bot with topic mode enabled in private chats (@BotFather)
  • Shell access on the host (to approve pairing — you do not need a Telegram user id first)
  • At least one agent CLI on PATH and logged in where required:
    • Grok Buildgrok (grok agent stdio)
    • Claudeclaude + npx (ACP adapter)
    • Codexcodex + npx (ACP adapter)
    • OpenCodeopencode (opencode acp)

1. Provision the bot

@BotFather is not scriptable. Once per bot:

  1. Create a bot → copy the token
  2. Enable topics in private chats for that bot

Pair as operator after start: DM the bot for a code, then run acpbot pair approve <code> on the host. See Pairing.

2. Download the binary

From GitHub Releases download one binary for your platform:

PlatformArtifact
Linux x64acpbot-v*-linux-x64.tar.gz
Linux arm64acpbot-v*-linux-arm64.tar.gz
macOS Apple Siliconacpbot-v*-darwin-arm64.tar.gz (signed)
macOS Intelacpbot-v*-darwin-x64.tar.gz (signed)

One unified binary — host and worker are subcommands of acpbot.

# example: v0.1.0 on Apple Silicon — use the latest tag from Releases
curl -sL -o acpbot.tar.gz \
  "https://github.com/pmdroid/acpbot/releases/download/v0.1.0/acpbot-v0.1.0-darwin-arm64.tar.gz"
tar -xzf acpbot.tar.gz
chmod +x acpbot-v0.1.0-darwin-arm64
sudo mv acpbot-v0.1.0-darwin-arm64 /usr/local/bin/acpbot
acpbot help    # host, worker, setup, services, …

Config is created automatically on first start under ~/.config/acpbot/ (no manual mkdir / cp).
Full reference: Configuration.

3. Setup + start host and worker

You need both processes: host (agents) and worker (Telegram). The worker fails at boot if the host socket is missing.

acpbot setup
# Guided TUI: bot token, agent, workspace, speech keys, optional OAuth callback.
# OAuth step can suggest Tailscale DNS / Tailscale IP / LAN IP, or a custom URL.
# macOS: offers Full Disk Access so agents can read real folders.
# Daemon step installs BOTH (same binary, two processes):
#   • acpbot host    (LaunchAgent / systemd)
#   • acpbot worker  (LaunchAgent / systemd)
ServiceCommandRole
Hostacpbot hostAgents, schedules, OAuth
Workeracpbot workerTelegram
  • macOS: app.acpbot.host + app.acpbot.worker LaunchAgents (KeepAlive)
  • Linux: acpbot-host.service + acpbot.service (systemd user)

Same ~/.config/acpbot/config.toml for both (mode 600). Logs: ~/.local/share/acpbot/logs/ on macOS, or journalctl --user -u acpbot-host -u acpbot on Linux.

Day-to-day service control (default = both host + worker):

acpbot install    # write + enable LaunchAgents / systemd units
acpbot start
acpbot stop
acpbot restart
acpbot status
# one side only: acpbot start --host   ·   acpbot stop --worker

If you skip the daemon step in setup:

acpbot host      # terminal 1 — agent stdio, schedules, OAuth
acpbot worker    # terminal 2 — Telegram

Workspace roots (folder browser) — see Repos:

acpbot repo
acpbot repo add demo ~/code/demo
# host/worker hot-reload [repos]; restart worker only if needed

Optional — install telegram / schedules skills into global agent dirs so Grok/Claude/… see them outside Telegram:

acpbot skills install

Background service paths: Configuration.

4. Pair as operator

The bot starts unpaired. Approve on the host CLI:

  1. Open a private chat with the bot and send any message (e.g. /ping).
  2. The bot replies with a pairing code (e.g. AB3K-9Q2M).
  3. On the machine running acpbot:
acpbot pair list
acpbot pair approve AB3K-9Q2M
acpbot pair status

Details: Pairing.

5. Use Telegram

/ping
/new demo hello
# open the new forum topic → type a prompt

/status   /model   /effort   /agent   /mode   /skills   /mcp   /cancel
# while busy: free-text is queued; /steer <text> interrupts

On startup acpbot wipes stale setMyCommands scopes and registers the slash menu from the command registry. Slash commands never go to the agent.

CommandEffect
type textStart an ACP turn (or queue if a turn is already running)
/steer <text>Interrupt the current turn and inject guidance now
/queue / /unqueueList / remove waiting prompts
/statusAgent, model, mode, effort, cwd, MCP
/modelLLM picker (or /model <id>)
/effortReasoning effort (when the agent advertises it)
/permissionsTool policy: ask (default) or bypass
/agentSwitch agent process for this session
/plan / /build / /modeSession plan/agent mode
/skillsSkill picker then prompt
/mcpPer-repo remote MCP registry + OAuth
/cancelStop current turn and clear the queue (session kept)

While a turn runs you will see a single (or ) status bubble in the topic. It updates in place as tools run. The final reply appears after the bubble is removed. Forum topic titles stay fixed (⏸ repo/name).

Busy-turn UX: free-text is queued (non-interrupt) with a Remove button on the ack; /steer … interrupts. Telegram does not notify message deletes — use Remove or /unqueue.

Details: Commands, Agents, Architecture.

6. Media & speech (optional)

DirectionBehavior
Photo / document → agentSaved under .acpbot-inbox/ (or ACP content blocks if features.acp_media_attachments = true)
Voice → agentSTT via configured provider (auto / openai / elevenlabs)
Agent → voiceMCP speak → TTS (same provider selection) → sendVoice
Agent → photo / fileMCP telegram_send_photo / telegram_send_file (path under session repo)

OpenAI-only example:

[speech]
tts_provider = "openai"
stt_provider = "openai"

[speech.openai]
api_key = "sk-…"
tts_voice = "alloy"

Full provider options: Configuration. Outbound path: Worker API.

Common failures

SymptomLikely cause
Boot fails: topics disabledEnable private-chat topics in @BotFather
No reply from non-youNot paired, or a different Telegram account
Missing bot tokenRun acpbot setup or edit ~/.config/acpbot/config.toml
Agent picker emptyNo agent CLIs on PATH (grok, claude, …)
Spawn dies immediatelyCheck agent login / adapter; stderr is logged
OAuth / host divergeWorker and host must share the same state_dir / config file
acpbot host exits on bootOAuth listen port in use, or missing shared state dir
Speech silent / no STTSet [speech.openai] or [speech.elevenlabs] keys; check tts_provider / stt_provider

From source (developers only)

Binary installs do not need Bun. For contributors, build or run via Bun, but all operator commands stay acpbot:

git clone https://github.com/pmdroid/acpbot.git
cd acpbot
bun install
bun run build:compile    # → dist/acpbot
# or during dev: bun run src/main.ts <command>

acpbot skills install    # global agent skills
acpbot host              # terminal 1
acpbot worker            # terminal 2
bun test ./test
bun run typecheck

Next

  • Architecture
  • MCP — per-repo tools & host acpbot tools
  • Skills — bundled telegram + schedules, global install
  • OAuth — remote gateways