Bridge for OMP on Telegram, Discord, and the local Web UI.
One Bun process per transport, one AgentSession per chat, every interactive surface mapped to the native UI of each.
Quick start Β· Commands Β· Configuration Β· Production
Bridges a Telegram chat to OMP's AgentSession SDK in-process β no omp --mode rpc subprocess, no JSON-RPC marshalling, full type access to the SDK.
- Native UI mapping ποΈ β agent
confirm/select/input/editorarrive as inline keyboards or text-reply prompts, not auto-rejected RPC frames - Streaming replies βοΈ β throttled inline message edits as tokens arrive; per-tool status line (
π read foo.ts,π» bash: β¦,β bash failed: β¦) - Per-chat cwd binding π β pin any chat (or forum topic) to a project directory; persisted to
~/.omptg/chats.json - Voice input ποΈ β voice / audio messages β ffmpeg β local whisper β confirm-before-send keyboard
- Auth that fits group chats π β allow-list by user id OR chat id; one user id covers DMs + every group you're in
- Auto session titles π·οΈ β OMP's
smolrole names sessions for/sessions
Installing with an LLM agent? Point it at install.md β that file is written for autonomous execution (Mintlify install.md format) and front-loads everything that doesn't need a human, leaving only the Telegram-bot creation step for you.
Prerequisites: Bun β₯ 1.3. For voice input also ffmpeg and uv (only consulted the first time a voice message arrives β skip if you won't speak to the bot).
1. Create a bot. Talk to @BotFather, send /newbot, copy the token.
2. Find your user id. Talk to @userinfobot once.
3. Boot it.
bun install
cp .env.example .env
# fill TELEGRAM_BOT_TOKEN + TELEGRAM_ALLOWED_CHATS
bun startDM the bot. /start shows the command list; /whoami prints the ids the bot sees you as.
4. Pin it to a project.
/bind ~/Workspaces/my-repo
Every agent turn in this chat now runs in that cwd. In a forum topic the binding is scoped to that topic only.
/newβ start a fresh session in the current chat/sessions [n]β list recent sessions (default 8)/resume <n>β resume session #n from/sessions/cancelβ cancel the in-flight agent turn/statusβ active session id, cwd, model/whoamiβ Telegram ids the bot sees you as/bind <path>β pin this chat/topic to a cwd/unbindβ remove the binding/bindingβ show current binding
Registered across default / all_private_chats / all_group_chats / all_chat_administrators scopes so they appear in Telegram's / menu everywhere.
All variables live in .env (auto-loaded by Bun). See .env.example for the full list with inline docs.
Required: TELEGRAM_BOT_TOKEN, TELEGRAM_ALLOWED_CHATS.
Optional: OMP_DEFAULT_CWD Β· OMPTG_STT_MODEL Β· OMPTG_STT_LANG Β· OMPTG_LOG_RETAIN_DAYS Β· OMPTG_LOG_COMPRESS_AFTER_DAYS.
Three transports, three independent processes, one shared ~/.omptg/chats.json for cwd bindings:
- Telegram (
bun start, envTELEGRAM_*) β DM + group + forum-topic routing, voice input, MarkdownV2 with the in-house escaper. The original surface; everything else is modeled after it. - Discord (
bun run start:discord, envDISCORD_*) β one text channel β one cwd, top-level messages auto-spawn a thread, each thread is an independent session. Slash commands register globally + per-dev-guild. See Discord setup below. - Web (
bun run start:web) β local Svelte UI for hands-on use without a chat client; useful when you want copy-paste and code rendering without Telegram/Discord caps.
Each bridge namespaces its ChatStore keys (tg: / dc: / web:) so the three processes don't collide on the shared JSON file. Writes are reload-then-merge under atomic tmp-rename, so concurrent /bind calls from different bridges interleave safely: each process preserves entries written by the others between its load and its save.
1. Create the application. Discord developer portal β New Application β Bot β Reset Token. Paste into DISCORD_BOT_TOKEN.
2. Enable intents. Same page, "Privileged Gateway Intents":
- MESSAGE CONTENT INTENT β required, the bot can't read message text without it.
Non-privileged intents (Guilds, GuildMessages, GuildMessageReactions) are requested automatically at gateway login.
3. Invite URL. OAuth2 β URL Generator. Scopes: bot, applications.commands. Bot permissions: View Channels, Send Messages, Send Messages in Threads, Create Public Threads, Read Message History, Add Reactions, Use Slash Commands. The portal builds the URL β open it, pick a guild, authorize.
Equivalent permission bitfield for a hand-rolled URL: 311385197632 (VIEW_CHANNEL + SEND_MESSAGES + SEND_MESSAGES_IN_THREADS + CREATE_PUBLIC_THREADS + READ_MESSAGE_HISTORY + ADD_REACTIONS + USE_APPLICATION_COMMANDS β no MANAGE_* bits). Template:
https://discord.com/api/oauth2/authorize?client_id=<APP_ID>&permissions=311385197632&scope=bot%20applications.commands
4. Configure & boot.
# fill DISCORD_BOT_TOKEN at minimum; DISCORD_ALLOWED_GUILDS recommended.
# DISCORD_DEV_GUILDS = your test-server id β instant slash-command updates
# while iterating (global registration takes up to 1h to propagate).
bun run start:discordPost any message in an allowed text channel β the bot auto-creates a thread, runs your prompt inside it, and every subsequent message in that thread routes to the same session. /bind /abs/path on the parent channel pins the cwd for every thread spawned underneath; /bind inside a specific thread overrides for that thread only.
bun start dies when your terminal closes. For real use, supervise with PM2 β one config covers macOS launchd, Linux systemd, and Windows services. The shipped ecosystem.config.cjs declares both omptg (Telegram) and omptg-discord; start whichever apply.
bun install -g pm2 # or: brew install pm2 / npm i -g pm2
bun run pm2:start # pm2 start ecosystem.config.cjs (starts both apps)
pm2 save # remember the running set across reboots
pm2 startup # generate the OS autostart hook β run the printed sudo line once
pm2 logs # live tail of all apps
pm2 restart omptg # apply code changes to Telegram only
pm2 restart omptg-discord # ...or Discord only
pm2 stop omptg-discord # don't want Discord supervised? stop + delete itUpgrading from a pre-Phase-6 deployment? PM2 log filenames now include the app name:
logs/pm2-out.logβlogs/pm2-omptg-out.log(and a newpm2-omptg-discord-*.logpair). The old files are orphaned, not deleted βrm logs/pm2-{out,err}.logonce you've checked you don't need them.
After save + startup, the bots survive logouts, reboots, and their own crashes (auto-restart, capped at 10 restarts within 10s windows). Telegram and Discord run as separate processes so a gateway flake on one doesn't impact the other.
Logs. Structured JSONL in logs/<date>.log (one event per line, jq-friendly), rotated on each boot: gzipped after OMPTG_LOG_COMPRESS_AFTER_DAYS (default 7), deleted after OMPTG_LOG_RETAIN_DAYS (default 30). PM2's own pm2-<app>-out.log / pm2-<app>-err.log aren't managed by us β install pm2-logrotate if you care.