TelegramCodexBot controls live Codex sessions over Telegram. The CLI/package name is
telegram-codex-bot.
Control Codex sessions remotely through Telegram while keeping tmux as the source of truth. This lets you monitor, answer, interrupt, resume, and clean up real terminal sessions from your phone without switching to a separate SDK session.
TelegramCodexBot is a Telegram controller for live Codex sessions:
codexis the default command for new tmux windows- transcript parsing and monitoring target modern Codex JSONL output under
~/.codex - Telegram delivery and topic isolation are hardened for long-running Codex sessions
- tmux stays the source of truth, so you can return to the same terminal session on desktop
- the default backend is local tmux, with an optional plugin interface for center-bot / remote agent-node deployments
- GitHub bridge support can inject structured tasks from issues into Codex tmux sessions
- Topic-based sessions — each Telegram topic maps 1:1 to a tmux window and Codex session
- Real-time notifications — assistant replies, thinking, tool calls, tool results, and local command output can be forwarded to Telegram
- Interactive UI support — navigate AskUserQuestion, ExitPlanMode, and permission prompts from inline keyboards
- Voice message transcription — voice messages can be transcribed with OpenAI and forwarded as text
- Resume existing sessions — choose an existing Codex session in a directory and continue from there
- Closed-session hiding — sessions closed through topic deletion or cleanup are hidden from the resume picker by default, without deleting their transcripts
- Topic cleanup — stale topics, stale tmux windows, and dead bindings are cleaned up more safely
- Usage/auth recovery — usage-limit and login failures are reported in Telegram; optional account failover can be enabled when you have saved backup accounts
- Persistent state — thread bindings, display names, offsets, and monitor state survive restarts
- Pluggable agent backend — local tmux is the default, while advanced users can load a backend plugin for distributed center-bot / agent-node setups
- GitHub bridge — optional
telegram-codex-bridgeCLI can poll GitHub issues and inject structured tasks into Codex tmux sessions
- tmux installed and available in PATH
- Codex CLI installed and working locally
- A Telegram bot with threaded/forum mode enabled
# with uv
uv tool install git+https://github.com/Pigbibi/TelegramCodexBot.git
# or with pipx
pipx install git+https://github.com/Pigbibi/TelegramCodexBot.gitgit clone https://github.com/Pigbibi/TelegramCodexBot.git
cd TelegramCodexBot
uv syncFor a new Mac or a fresh local setup:
git clone https://github.com/Pigbibi/TelegramCodexBot.git
cd TelegramCodexBot
chmod +x scripts/bootstrap-macos.sh
./scripts/bootstrap-macos.shThe script does the following:
- run
uv sync - create
~/.telegram-codex-bot/.envfrom.env.exampleif missing - install
telegram-codex-bot hook --installinto the active Codex home - generate a reusable
~/.telegram-codex-bot/bin/telegram-codex-bot-launch - generate a LaunchAgent plist for macOS
Required local setup after the script runs:
TELEGRAM_BOT_TOKENALLOWED_USERS- optional
OPENAI_API_KEYif you want voice transcription - run
codex login
This project does not use a separate GPT_SUBSCRIPTION= env var.
It reuses the local Codex login state:
codex loginIf you use multiple Codex accounts or need to refresh login while away from the server, use Telegram commands:
/codexlogin # refresh the service user's default Codex login
/codexlogin backup # login and save a named backup account
/codexaccount list
/codexaccount use backup
If ~/.telegram-codex-bot/.env still contains placeholder values, the script will write the
launchd files but will not start the service. After editing .env, start it
manually:
launchctl bootstrap "gui/$(id -u)" ~/Library/LaunchAgents/io.github.telegramcodexbot.plist
launchctl kickstart -k "gui/$(id -u)/io.github.telegramcodexbot"Check status:
launchctl print "gui/$(id -u)/io.github.telegramcodexbot" | sed -n '1,40p'
tail -n 50 ~/.telegram-codex-bot/logs/telegram-codex-bot.err.logFor a Linux workstation or a VPS with systemd:
mkdir -p ~/.telegram-codex-bot/app
git clone https://github.com/Pigbibi/TelegramCodexBot.git ~/.telegram-codex-bot/app/TelegramCodexBot
cd ~/.telegram-codex-bot/app/TelegramCodexBot
chmod +x scripts/bootstrap-linux.sh
./scripts/bootstrap-linux.shKeep the bot checkout outside the project roots that Codex sessions can browse
or clean, such as ~/Projects. The Linux bootstrap refuses an unsafe checkout
inside TELEGRAM_CODEX_BOT_DEFAULT_PROJECTS_PATH or
TELEGRAM_CODEX_BOT_PROJECT_ROOTS, because the systemd launcher points back to
that checkout and a project cleanup would break the next restart.
The Linux helper:
- runs
uv sync - creates
~/.telegram-codex-bot/.envfrom.env.exampleif needed - installs
telegram-codex-bot hook --install - writes
~/.telegram-codex-bot/bin/telegram-codex-bot-launch - writes a user service at
~/.config/systemd/user/io.github.telegramcodexbot.service
After that:
- edit
~/.telegram-codex-bot/.env - run
codex login - start the service if it was not auto-started:
systemctl --user daemon-reload
systemctl --user enable --now io.github.telegramcodexbot.serviceOn a VPS, if you want the service to keep running after reboot without an interactive login session:
sudo loginctl enable-linger "$USER"Check status:
systemctl --user status io.github.telegramcodexbot.service --no-pager
tail -n 50 ~/.telegram-codex-bot/logs/telegram-codex-bot.err.log- Talk to @BotFather
- Create a bot and get the token
- Open the bot settings mini app
- Enable Threaded Mode
TELEGRAM_BOT_TOKEN=your_bot_token_here
ALLOWED_USERS=your_telegram_user_id
TELEGRAM_CODEX_BOT_CODEX_COMMAND=codex
TELEGRAM_CODEX_BOT_AUTO_UPDATE=true
TELEGRAM_CODEX_BOT_SHOW_COMMENTARY_MESSAGES=trueFor most setups, this is the only file you need to edit.
If you want GitHub issues to be handed off to Codex sessions, keep the bridge configuration local and use the template docs in:
docs/github_codex_bridge.mddocs/github_codex_bridge.sample.json
The bridge supports two local modes:
targets: poll one or more repositories directly and hand each issue to its configured tmux windoworchestrator: consume the monthly issue from a control-plane repository and relay it to a single runner window
The real config belongs at ~/.telegram-codex-bot/github_codex_bridge.json and should not
be committed.
| Variable | Description |
|---|---|
TELEGRAM_BOT_TOKEN |
Bot token from @BotFather |
ALLOWED_USERS |
Comma-separated Telegram user IDs allowed to control the bot |
| Variable | Default | Description |
|---|---|---|
TELEGRAM_CODEX_BOT_DIR |
~/.telegram-codex-bot |
Config and state directory |
TELEGRAM_CODEX_BOT_BACKEND |
local |
Agent backend ID. local keeps the single-machine tmux behavior |
TELEGRAM_CODEX_BOT_BACKEND_PLUGINS |
(none) | Comma-separated Python modules that register optional agent backends |
TELEGRAM_CODEX_BOT_TMUX_SESSION_NAME |
telegram-codex-bot |
tmux session name used by the bot |
TELEGRAM_CODEX_BOT_CODEX_COMMAND |
codex |
Command used when creating a new window |
TELEGRAM_CODEX_BOT_CODEX_BYPASS_HOOK_TRUST |
false |
Append Codex --dangerously-bypass-hook-trust for unattended hosts after you have vetted the configured hooks |
TELEGRAM_CODEX_BOT_CODEX_PROJECTS_PATH |
~/.codex |
Transcript root to scan |
TELEGRAM_CODEX_BOT_DEFAULT_PROJECTS_PATH |
~/Projects |
Default directory shown when creating a new session |
TELEGRAM_CODEX_BOT_PROJECT_ROOTS |
(none) | Optional named roots shown before directory browsing |
TELEGRAM_CODEX_BOT_MONITOR_POLL_INTERVAL |
2.0 |
Poll interval in seconds |
TELEGRAM_CODEX_BOT_ENABLE_ACCOUNT_ROTATION |
false |
Automatically rotate to the next saved account after usage_limit_exceeded |
TELEGRAM_CODEX_BOT_STATUS_POLL_INTERVAL |
1.0 |
Terminal status polling interval in seconds; active Working (...) status edits keep Telegram refreshed |
TELEGRAM_CODEX_BOT_STATUS_REPOST_INTERVAL |
60.0 |
Re-send long-running Thinking status after this many seconds so Telegram topics visibly stay active; set 0 to only edit in place |
TELEGRAM_CODEX_BOT_AGENT_INPUT_QUEUE_MAX_SIZE |
20 |
Maximum bot-held inputs per session while Codex shows an interactive prompt; regular busy-state inputs are sent to Codex directly |
TELEGRAM_CODEX_BOT_AGENT_INPUT_QUEUE_MAX_WAIT_SECONDS |
1800 |
Drop bot-held inputs after this many seconds if Codex never becomes ready; set 0 to disable expiry |
TELEGRAM_CODEX_BOT_AUTO_UPDATE |
false |
On startup, check and fast-forward git source installs |
TELEGRAM_CODEX_BOT_UPDATE_INTERVAL_SECONDS |
86400 |
Minimum seconds between automatic update checks |
TELEGRAM_CODEX_BOT_UPDATE_REQUIRE_IDLE |
true |
Apply automatic updates only when no Codex pane is active |
TELEGRAM_CODEX_BOT_UPDATE_BUSY_RETRY_SECONDS |
300 |
Retry delay when automatic update is deferred by active work |
TELEGRAM_CODEX_BOT_UPDATE_REMOTE |
git remote | Optional git remote override for updates |
TELEGRAM_CODEX_BOT_UPDATE_BRANCH |
git branch | Optional git branch override for updates |
TELEGRAM_CODEX_BOT_UPDATE_RUN_UV_SYNC |
true |
Run uv sync after a successful git update |
TELEGRAM_CODEX_BOT_CODEX_UPDATE_CHECK |
false |
Check npm for Codex CLI updates during the idle update loop |
TELEGRAM_CODEX_BOT_CODEX_UPDATE_NPM |
npm |
npm command used for Codex CLI checks/updates; can be sudo -n npm if explicitly allowed |
TELEGRAM_CODEX_BOT_CODEX_AUTO_UPDATE |
false |
Run npm install -g @openai/codex@latest when an idle Codex update exists |
TELEGRAM_CODEX_BOT_SHOW_COMMENTARY_MESSAGES |
false |
Forward Codex commentary/thinking messages |
TELEGRAM_CODEX_BOT_SHOW_TOOL_CALLS |
true |
Forward tool call notifications and outputs |
TELEGRAM_CODEX_BOT_SHOW_BASH_TOOL_CALLS |
true |
Forward Bash command and output notifications; set false to hide Bash only |
TELEGRAM_CODEX_BOT_SHOW_HIDDEN_DIRS |
false |
Show dot-directories in the directory picker |
OPENAI_API_KEY |
(none) | Used for voice transcription |
OPENAI_BASE_URL |
https://api.openai.com/v1 |
Custom OpenAI-compatible endpoint |
Telegram formatting uses MarkdownV2 with plain-text fallback when needed.
To choose a computer, VPS, or mounted workspace before browsing directories, configure named roots:
TELEGRAM_CODEX_BOT_PROJECT_ROOTS=Local=~/Projects,Remote=/mnt/remote-projectsWhen TELEGRAM_CODEX_BOT_PROJECT_ROOTS is set, a new Telegram topic first shows a
computer/VPS picker, even if only one root is configured. After selecting one,
the normal directory browser starts at that root and does not navigate above it.
Other computers or VPSes must be reachable as local paths from the machine
running telegram-codex-bot, for example through SSHFS or NFS mounts.
TelegramCodexBot starts with the local backend by default. This is the
existing single-machine mode: Telegram talks to a local tmux session, and the
bot monitors local Codex transcript files.
Single-machine mode is the supported default. The backend interface exists so center-bot / remote agent-node deployments can be added without changing the normal local workflow.
If you are not running remote agent nodes, leave TELEGRAM_CODEX_BOT_BACKEND
unset or set it to local. You do not need to install plugins/socket_backend,
run telegram-codex-agent-node, or configure socket node addresses.
Optional backends can be loaded as plugins. A plugin can expose a backend
through the telegram_codex_bot.backends entry point group, or through a module
listed in TELEGRAM_CODEX_BOT_BACKEND_PLUGINS.
The repository includes an optional socket backend package under
plugins/socket_backend/. It provides a socket-cluster center backend and a
telegram-codex-agent-node CLI for remote machines. Design notes and operating
details live in docs/agent_backend_plugins.md.
TELEGRAM_CODEX_BOT_BACKEND=localPlugin module example:
TELEGRAM_CODEX_BOT_BACKEND=socket-cluster
TELEGRAM_CODEX_BOT_BACKEND_PLUGINS=telegram_codex_bot_socket_backend
TELEGRAM_CODEX_BOT_SOCKET_NODES=macbook=127.0.0.1:8765The core bot loads the configured backend through a backend interface covering
lifecycle and agent operations: prepare(), start(message_callback),
stop(), create_session(), send_message(), send_control(), and
capture(). Backends can also implement the optional browser capability for
remote root selection, directory browsing, and resume-session lookup. The local
backend implements those interfaces by preparing tmux, starting the existing
transcript monitor, and forwarding operations to the current local managers.
Thread bindings are stored in both the legacy local window form and the newer
backend target form when the target is local. This keeps existing state files
rollback-safe while allowing non-local backends to address an agent by
backend_id, node_id, and session_id instead of a tmux window ID.
At the moment this repository does not ship demo screenshots or videos. The README intentionally avoids embedding old project media.
For source-checkout installs created by the bootstrap scripts, set:
TELEGRAM_CODEX_BOT_AUTO_UPDATE=trueWhile the bot keeps running, it checks the configured git remote at most once
per TELEGRAM_CODEX_BOT_UPDATE_INTERVAL_SECONDS. With the default
TELEGRAM_CODEX_BOT_UPDATE_REQUIRE_IDLE=true, it first verifies that Telegram delivery queues
are empty and that no Codex tmux pane is working or waiting for interactive
input. If work is active, it waits TELEGRAM_CODEX_BOT_UPDATE_BUSY_RETRY_SECONDS and tries
again.
If the checkout is clean and the update can be applied as a fast-forward, telegram-codex-bot
runs git pull --ff-only, runs uv sync, and restarts itself so the new code is
loaded. Existing Codex tmux windows and conversations are not killed.
Manual commands:
telegram-codex-bot update --check
telegram-codex-bot update
telegram-codex-bot codex-update --check
telegram-codex-bot codex-update
telegram-codex-bot --versionSelf-update intentionally skips non-git installs such as pipx install or
uv tool install, and it also skips a checkout with local modifications.
Codex CLI checks are separate from telegram-codex-bot self-update. The example .env enables
TELEGRAM_CODEX_BOT_CODEX_UPDATE_CHECK=true, which only reports when a newer npm package is
available. When an update is available, telegram-codex-bot sends allowed users a Telegram
prompt with an upgrade button. Keep TELEGRAM_CODEX_BOT_CODEX_AUTO_UPDATE=false unless the
service user should apply Codex CLI updates without confirmation. If the global npm package is
root-owned, set TELEGRAM_CODEX_BOT_CODEX_UPDATE_NPM=sudo -n npm only after granting that
non-interactive sudo path deliberately.
If Codex runs on a server where you do not want approval prompts in the terminal UI:
TELEGRAM_CODEX_BOT_CODEX_COMMAND=IS_SANDBOX=1 codex --dangerously-bypass-approvals-and-sandboxBy default, new sessions use the service user's normal ~/.codex login and automatic account rotation is disabled. This keeps single-account installs predictable.
Telegram commands:
/codexlogin # start Codex device login for the default CODEX_HOME
/codexlogin backup # login into an isolated account home and save it as backup
/codexaccount list
/codexaccount use backup
/codexaccount clear # go back to the service user's default CODEX_HOME
Named accounts are stored under ~/.telegram-codex-bot/accounts/. Switching affects newly created topics only; existing topics keep their current tmux window. Use /unbind when you want the current topic to start a fresh session with the selected account.
If you want usage-limit failover, set TELEGRAM_CODEX_BOT_ENABLE_ACCOUNT_ROTATION=true. When a live session emits usage_limit_exceeded, TelegramCodexBot marks that window as exhausted; on the next message it can create a fresh tmux window on the next saved account and forward the message there. This is session rotation, not seamless continuation of the exact same Codex session.
By default, this project scans Codex transcript files under ~/.codex.
If you want automatic session-to-window tracking via the CLI hook, install it with:
telegram-codex-bot hook --installThis command enables Codex hooks in the active Codex home:
$CODEX_HOME/config.tomland$CODEX_HOME/hooks.jsonwhenCODEX_HOMEis set- otherwise
~/.codex/config.tomland~/.codex/hooks.json
Manual equivalent:
~/.codex/config.toml
[features]
hooks = true~/.codex/hooks.json
{
"hooks": {
"SessionStart": [
{
"matcher": "startup|resume",
"hooks": [
{
"type": "command",
"command": "telegram-codex-bot hook",
"statusMessage": "Registering Codex session",
"timeout": 5
}
]
}
]
}
}The hook writes window/session mappings into $TELEGRAM_CODEX_BOT_DIR/session_map.json, which helps the bot keep tmux windows associated with Codex sessions even after clears or restarts.
On unattended hosts, newer Codex versions may require hook trust before the
first session starts. After you have verified that $CODEX_HOME/hooks.json
contains only hooks you expect, set:
TELEGRAM_CODEX_BOT_CODEX_BYPASS_HOOK_TRUST=trueThis makes new bot-managed Codex windows start with
--dangerously-bypass-hook-trust, avoiding a hidden terminal prompt that would
otherwise block the first Telegram message from reaching the transcript.
# installed tool
telegram-codex-bot
# from source
uv run telegram-codex-bot| Command | Description |
|---|---|
/start |
Show the welcome message |
/history |
Show message history for the current topic |
/screenshot |
Capture the current terminal pane |
/esc, /interrupt |
Send Escape to Codex |
/kill |
Kill the bound tmux window and clean up the topic binding |
/unbind |
Unbind the topic without killing the running tmux window |
/usage |
Open Codex usage info in the TUI and send the parsed result |
/codexlogin [name] |
Start Codex device login from Telegram |
/codexaccount |
List, save, select, or clear saved Codex accounts |
| Command | Description |
|---|---|
/clear |
Clear conversation history |
/compact |
Compact context |
/cost |
Show token/cost usage |
/goal |
Set or update the session goal |
/help |
Show Codex help |
/memory |
Edit AGENTS.md |
/model |
Switch the model |
Other unknown slash commands are forwarded to Codex as-is.
1 topic = 1 tmux window = 1 active session.
- Create a new Telegram topic
- Send any message
- Pick a directory from the browser
- Resume an existing session or create a new one
- TelegramCodexBot creates a tmux window and forwards your pending message
If the bot finds an existing tracked tmux window for that directory, it can offer that window for binding. Untracked windows are ignored on purpose so a topic does not attach to a terminal that has no reliable session mapping yet.
After a topic is bound, just keep sending text or voice messages in that topic.
- close/delete the Telegram topic, or
- use
/kill, or - use
/unbindif you want to keep the tmux window alive but detach the topic
If you close/delete a topic (or the bot cleans up a dead topic/window), the
associated Codex session is hidden from the resume picker by default. The
underlying transcript file is kept on disk under ~/.codex.
The monitor polls transcript files and can forward:
- assistant replies
- commentary / thinking output
- tool use and tool results
- local command output
- public progress updates visible in tmux, such as
Explored,Ran,Searched, andSearching the web - usage-limit exhaustion events
Public progress updates come from text already visible in the terminal UI. They are not hidden model reasoning dumps.
tmux attach -t telegram-codex-bot
tmux new-window -n myproject -c ~/Code/myproject
codexThe window must live inside the configured telegram-codex-bot tmux session.
| Path | Description |
|---|---|
$TELEGRAM_CODEX_BOT_DIR/state.json |
Thread bindings/targets, window state, display names, offsets, and hidden closed-session IDs |
$TELEGRAM_CODEX_BOT_DIR/session_map.json |
Hook-generated tmux window ↔ session mappings |
$TELEGRAM_CODEX_BOT_DIR/monitor_state.json |
Monitor byte offsets per session |
$TELEGRAM_CODEX_BOT_DIR/pending_topic_deletions.json |
Deferred topic deletions after local cleanup |
~/.codex/ |
Codex transcript root (read-only) |
~/.telegram-codex-bot/accounts/ |
Optional saved account homes and snapshots |
src/telegram_codex_bot/
├── __init__.py
├── account_manager.py
├── agent_io.py
├── backends/
├── bot.py
├── bridge.py
├── config.py
├── hook.py
├── main.py
├── markdown_v2.py
├── monitor_state.py
├── screenshot.py
├── session.py
├── session_monitor.py
├── terminal_parser.py
├── tmux_manager.py
├── transcribe.py
├── transcript_parser.py
├── utils.py
└── handlers/
This project is distributed under the MIT License.
Copyright and license notices are kept in LICENSE.
Bundled fonts keep their own license files under src/telegram_codex_bot/fonts/.