opencode-telegram-session-control
v1.0.13
Published
Native OpenCode plugin to securely control sessions from Telegram.
Maintainers
Readme
OpenCode Telegram Session Control
Control a running OpenCode session from Telegram with your own bot. Messages are forwarded to OpenCode; answers, permission requests, and questions from the agent appear in Telegram. The plugin supports OpenCode on Windows, WSL, Linux, and macOS.
Telegram stays off when OpenCode starts. The connection only begins when you ask your agent to connect and it calls telegram_connect. There is no Telegram command to turn it on; /start shows a brief connection notice and a /help button. Only /help opens the full control menu. Neither command can start an inactive bridge.
Features
- Agent-gated connection.
telegram_connectvalidates the token, acquires a single-instance poll lock, and starts long polling.telegram_disconnectstops it;telegram_statusreports the state. - Allowlist security. Only configured Telegram user IDs or chat IDs can control the session; everything else is rejected. One-time pairing codes are supported when no allowlist is configured.
- Full session control. List, switch, create, and rename sessions; list and switch models; abort running work.
- Permissions and questions in chat. Approve, always-allow, or deny tool permissions and answer agent questions directly from Telegram, including inline buttons.
- Optional live trace. See OpenCode-visible streamed response text, visible reasoning parts, tool calls and tool results in Telegram while the agent works.
- Local credentials only. The bot token and your user ID live in a local env file; the token is sent only to Telegram for authentication. Prompts and responses pass through Telegram and the configured OpenCode model provider.
System requirements
Runtime requirements
- OpenCode 1.18.0 or newer with npm plugin support (tested with OpenCode 1.18.29).
- A working model configuration in OpenCode.
- Node.js 20 or newer (the runtime is provided by OpenCode; also needed for the helper command below).
- Telegram on your phone or computer and internet access on the OpenCode machine.
OpenCode must be running while you use Telegram. The plugin does not start a separate OpenCode server and opens no inbound port.
Development/build requirements
These are required only when building or testing this repository from source:
- Node.js 20 or newer.
- npm.
Platform support
The plugin uses only Node.js built-ins (node:fs, node:os, node:path, node:crypto) and the OpenCode plugin/SDK API. There is no platform-specific code:
- The Telegram transport is plain HTTPS long polling, identical on Windows, WSL, Linux, and macOS.
- The config file is resolved through
os.homedir()(~/.config/opencode/telegram.env), so it is correct on every platform. - The bot poll lock lives in the operating system temporary directory (
os.tmpdir()) and works on all supported platforms.
Install in native Windows OpenCode
Open PowerShell:
opencode --version
opencode plugin -g [email protected]
opencodeTo replace an already configured version:
opencode plugin -g -f [email protected]OpenCode writes the plugin to the global config and installs npm plugin dependencies automatically. No manual JSON editing or separate global npm installation is needed. Restart running OpenCode instances after installation. The CLI installation was verified with OpenCode 1.18.29.
Installation does not start Telegram polling. Complete the token/user-ID setup below, then ask your agent to call telegram_connect.
Install in OpenCode running in WSL or Linux
Open the WSL or Linux terminal:
opencode --version
opencode plugin -g [email protected]
opencodeIf the package is already configured:
opencode plugin -g -f [email protected]The same commands apply on macOS. Run them inside the environment where OpenCode is installed. Write the package spec with a plain @; no backslash is needed.
Uninstall
OpenCode has no plugin removal command, so uninstalling is a manual three-step process:
- Remove the
"opencode-telegram-session-control"entry from thepluginarray of your global config (~/.config/opencode/opencode.jsoncor~/.config/opencode/opencode.json). - Delete the plugin cache:
- Windows PowerShell:
Remove-Item -Recurse -Force "$env:USERPROFILE\.cache\opencode\packages\opencode-telegram-session-control*" - WSL/Linux/macOS:
rm -rf ~/.cache/opencode/packages/opencode-telegram-session-control*
- Windows PowerShell:
- Restart OpenCode.
Optionally also delete your credentials file (~/.config/opencode/telegram.env).
1. Get a bot and token
- Open the official @BotFather in Telegram.
- Send it
/newbot. - Choose a display name and an available username for your bot. The username must end in
bot. - BotFather gives you an API token. Treat it like a password.
- Open the chat with your new bot and send it a normal message such as
Hallo. No answer is expected at this point.
The token belongs to the bot. Your personal user ID is determined separately in the next step. Telegram's own guide: create a bot.
2. Store token and user ID
Create this file:
- Windows:
C:\Users\<YOUR-NAME>\.config\opencode\telegram.env - Linux/macOS/WSL:
~/.config/opencode/telegram.env
On Windows you can open it with:
New-Item -ItemType Directory -Force "$env:USERPROFILE\.config\opencode" | Out-Null
notepad "$env:USERPROFILE\.config\opencode\telegram.env"Start with your real bot token:
TELEGRAM_TOKEN=DEIN_BOT_TOKENSave the file exactly as telegram.env, not telegram.env.txt.
Find your user ID
Keep the Telegram connection off in OpenCode. After you have privately sent your bot Hallo, run:
npx --yes [email protected] opencode-telegram-user-idThe helper reads the token from your file and prints the user IDs of pending messages, for example DeinName: TELEGRAM_USER=123456789. It sends no message and never prints the token. If several people are listed, take your own ID. If nothing is listed, send Hallo again and repeat. No other application may poll this bot while you do this.
Then complete the file:
TELEGRAM_TOKEN=DEIN_BOT_TOKEN
TELEGRAM_USER=123456789
TELEGRAM_PAIRING=falseThe user ID is a number, not an @username and not the bot's ID. Multiple allowed users are separated by commas. With this configuration only the listed users can control the bot.
You need no npm token for installation or use. Your model API key belongs in the OpenCode model configuration, not in this file. Never copy the token into prompts, screenshots, or the repository.
3. Connect and first test
Start OpenCode in your project folder:
cd path/to/your/project
opencodeTell your OpenCode agent:
Connect this session to Telegram. Use telegram_connect.
Only this tool call validates the token and starts Telegram polling. The bot then sends a brief connection notice with a /help button. For that you must have written to it privately once and must not have blocked it. With static allowlist disabled, the welcome notice appears only after successful pairing. Delivery errors are reported at connect time; the connection stays usable.
Then send your bot:
/currentThe output shows the selected session. Then send:
Reply with HALLO only.Normal replies contain only the model answer. A single status message identifies the selected session after you switch or create one, and the selected model after you change it. The model works with the tools and permissions of your running OpenCode instance.
4. Using it in Telegram
| Message/Command | Function |
| --- | --- |
| Plain text | Send as a prompt to the selected session |
| /start | Show a brief welcome with a /help button |
| /help | Open the English control menu with executable command buttons |
| /current | Show current session and model |
| /session | Show the current session name as text (no selection menu) |
| /model | Show the current model as text (no selection menu) |
| /sessions | Choose a session using buttons and pagination |
| /session 2 | Select a session from the list |
| /session <id> | Select a session by its listed ID |
| /new My test | Create and select a new session |
| /rename New title | Rename the current session |
| /models | Choose a model using buttons and pagination |
| /model 2 | Select a model from the list |
| /model provider/model | Select a model by its full name |
| /stop or /abort | Immediately abort the running turn in the connected session (like pressing Escape twice in OpenCode) |
| /tracing | Open buttons to turn the live trace on or off for this Telegram chat |
| /permissions | Show open permission requests |
| /approve <id> | Allow once |
| /always <id> | Permanently allow the matching permission |
| /deny <id> | Deny the permission |
| /cancel | Cancel custom-answer text input |
| /questions | Show open questions |
| /answer <id> answer | Answer a question |
| /rejectquestion <id> | Reject a question |
| /disconnect | Remove this chat's binding; static user allowlists remain valid |
/tracing is off by default and is stored per Telegram chat for the current OpenCode process. Each response or OpenCode-visible reasoning block is buffered until complete. Each tool call is sent once after completion as a short activity summary. Token fragments and repeated headings are not sent. Use the buttons from /tracing to enable or disable it. The final answer is not repeated when its block was already delivered.
User prompts are excluded from tracing. Tool output is never forwarded. For example, read displays Reading path/to/file, while command tools display the command being run and then its completion state. This keeps tool messages compact and prevents a tool result from becoming an opencode-block.txt download. Trace and normal output contain no repeated session or model headers. Session and model changes are announced once when they happen.
Trace messages can contain command names, paths and error messages. Enable them only in an authorized private chat. Internal model reasoning that OpenCode does not expose through its plugin events cannot be sent.
Messages sent quickly one after another in the same Telegram chat are processed in order. If a temporary Telegram delivery failure occurs while sending a trace block, delivery is retried and blocks already delivered to a chat are not sent again. If Telegram reports that another bot instance is polling with the same token, the bridge stops polling and reports the conflict instead of competing indefinitely. Close the other instance, then ask OpenCode to reconnect.
If the connected OpenCode session no longer exists, the bridge creates and selects a new Telegram session, reports that one change, and retries the original prompt once. A context-overflow error is reported once as Session context is full. Use /new to start a new session. Its follow-up MessageAbortedError is suppressed.
/session and /model without arguments only display status. /sessions and /models open the selection menus. With arguments, /session <id or number> and /model <provider/model or number> still switch the selection. /current displays both session name and model. The model query uses the Telegram model override, session model, recent session messages, or configured default in that order. If no model is known yet, it says so explicitly.
For multiple questions: /answer <id> answer one || answer two. For multi-select: Option A; Option B. Questions and permissions include inline buttons. Multiple questions are shown one at a time. For multi-select questions, tap options to toggle them, then tap Confirm selection. Menus show eight options per page. Selectable names appear only on buttons, without a duplicate text list. The message shows the heading and page number; questions also show their question text and numbered descriptions when provided. Navigation includes previous/next, jumps of ten pages when useful, and direct jumps to the first/last page. Multi-select choices remain selected across page jumps.
Send /stop (or /abort) once to interrupt the connected session, including streaming output and running tools. The command calls OpenCode's POST /session/{sessionID}/abort endpoint directly and is processed while the prompt is still running. The Telegram connection remains active, so you can send a new task afterwards. Already completed actions are not undone. With no session connected, the command reports this without creating a session. /cancel only cancels custom-answer input; it does not stop the agent.
For a custom answer, tap Write an answer, then send your next message as plain text; /cancel cancels this input. /questions and /permissions display pending requests with fresh buttons. Old or already answered buttons cannot submit a second response. Menu buttons expire after one hour or when OpenCode restarts; reopen the menu using its command.
All bot messages use UTF-8, including German umlauts and emoji. Text is sent without Telegram Markdown parsing, so code and special characters cannot break delivery. Long messages are split without losing characters; the keyboard appears only on the final part.
/commands lists registered OpenCode commands with executable inline buttons and pagination. These slash commands execute through the actual OpenCode session command API, including arguments and the selected model. Unknown commands report an error and are not sent as model prompts. Commands that only operate the local OpenCode interface are unavailable in Telegram. /start and /help are handled locally and never sent to the model.
Turning Telegram off completely
Tell your OpenCode agent:
Disconnect Telegram. Use telegram_disconnect.
This stops polling and releases the bot lock. /disconnect in Telegram only removes the chat binding. Abort a running job with /abort first; a full disconnect waits for in-flight message processing to finish.
After restarting OpenCode you must connect again. Only one OpenCode instance can be connected to the same bot at a time. Other instances do not take over the bot automatically. Disconnect the previous instance, then ask the desired agent to connect.
Welcome and help menu
After telegram_connect, the bridge sends a short welcome with a /help button to configured users/chats and existing chat bindings. Users must have messaged the bot first and must not have blocked it. Delivery failures are reported by the connect tool; the bridge stays usable. Without static allowlists, the welcome is sent after successful /pair <code>. /start repeats only this welcome. /help opens the full control menu.
All built-in interface text is English: help, buttons, navigation, status messages, errors and trace labels. Model responses, model-generated questions, tool output and user-provided names retain their original language and content. Each command listed in help has an inline button. Clicking runs the command immediately; commands requiring arguments, such as /rename, ask for the required text first. Use /cancel to leave this input mode.
Configuration
Existing environment variables take precedence. Accepted names:
| Variable | Meaning |
| --- | --- |
| TELEGRAM_BOT_TOKEN | Environment variable for the bot token; files also accept TELEGRAM_TOKEN |
| TELEGRAM_ALLOWED_USER_IDS | Allowed users; files also accept TELEGRAM_USER |
| TELEGRAM_ALLOWED_CHAT_IDS | Allowed chats, comma-separated |
| TELEGRAM_PAIRING | true or false |
| TELEGRAM_ENV_FILE | Explicit path to a configuration file |
File lookup order: explicit path, global telegram.env, global telegram.json, then .env.opencode-telegram and .env in the project or parent directories. Prefer the global file. Restart OpenCode after changes so previously loaded values are replaced.
Alternative JSON format: {"token":"DEIN_BOT_TOKEN","userId":"123456789"}.
A chat allowlist lets every user in that chat control the session. User and chat allowlists are combined with OR. For private use, your user ID is enough.
Without a user/chat allowlist, one-time pairing is possible: telegram_connect returns a code you send to your bot as /pair <code>. The code expires after use or when the connection ends. With TELEGRAM_PAIRING=false, a static allowlist must be configured.
Troubleshooting
- Bot does not answer: OpenCode must be running and
telegram_connectmust have succeeded. Check via the agent withtelegram_status. - Unauthorized: Check your numeric user ID and restart OpenCode after correcting it.
- Token error: Copy the token from BotFather into the file again. Check file name and path.
- Another instance connected / Telegram Conflict: Disconnect other OpenCode instances or programs polling the same bot.
- Webhook conflict: This plugin uses long polling. Use a dedicated bot without an existing webhook.
- No model answer: Test the same task directly in OpenCode first. Check model access, open permissions, and pending questions.
- Tools missing: Check the plugin entry, remove old local references, and restart OpenCode.
Smoke test from OpenCode
After connecting, send your bot:
/currentThe reply shows the selected session. Then send a plain message such as Reply with HALLO only. and confirm you receive the session ID followed by the model answer.
Build and verification from source
From the repository directory:
npm ci
npm test
npm run check
npm packnpm test verifies authorization, pairing, session and model selection, permissions, questions, the real SDK request format with a simulated HTTP transport, and the connection lifecycle without network access.
Publishing
The npm package name is opencode-telegram-session-control.
npm login
npm publish --access publicThe package declares win32, linux, and darwin as installable npm platforms because the plugin is pure Node.js and runs on every platform OpenCode supports.
Maintainer
Joel Buchholz
License
MIT © 2026 Joel Buchholz
