๐Ÿค– dsh-telegram

August 28, 2026 ยท View on GitHub

Telegram remote control for DeepSeek Harness โ€” your agent in your pocket.

Drive your dsh sessions from Telegram: list, bind, prompt, watch live streaming replies, answer agent questions with one tap, and stop or re-point sessions โ€” all through the exact same channel the Web UI uses, from any chat you whitelist.

License: MIT Node Chinese


โœจ Features

  • Full session console โ€” /attach bind a session, /status inspect it, plain text becomes a prompt, live replies edit in place on one message.
  • Real-time streaming replies โ€” assistant output is streamed into a single message and edited in place across the whole turn (๐Ÿ’ญ Think / ๐Ÿ”ง tool action lines per step), then finalized as Telegram HTML with a token-usage footer.
  • Interactive questions as buttons โ€” when the agent calls ask_user_question, the chat gets an answerable inline keyboard instead of a wall of text: tap to answer, tap to cancel; the outcome races the Web UI first-claimant-wins.
  • One-tap keyboards โ€” reply keyboards carry /create /archive /attach and /stop /close action rows; session lists and workspace pickers render as inline buttons. Almost everything is a tap, everything still works typed.
  • Safe by default โ€” only allowChatIds can talk to the bot; everyone else gets โ›” ๆ— ๆƒ่ฎฟ้—ฎใ€‚ and zero session facts. The plugin opens no inbound port: all traffic is outbound Telegram long polling (optionally through a CONNECT proxy).
  • Secrets never land on disk โ€” botToken is referenced as !!js process.env.TELEGRAM_BOT_TOKEN; the token lives in the environment (or $DSH_HOME/.env, 0600).
  • Cold-session resume โ€” binding and prompting go through the host apiProxy, the exact path the Web UI uses, including cold-session resume and queued-message semantics.

๐Ÿ“ธ Demo

A real session, driven from Telegram:

Real-world usage

Attach & live streaming replyOne-tap keyboardsAnswer a question by tapping
attach + streamkeyboardsask-question

๐Ÿš€ Quick Start

Prerequisites

  • A dsh source checkout (the plugin is installed into it) โ€” or an already-running dsh deployment for the package route.
  • Node.js โ‰ฅ 22.19, pnpm, git.
  • A bot token from @BotFather, and outbound access to api.telegram.org (or a CONNECT proxy).
git clone https://github.com/Kevin66Z0/dsh-telegram.git dsh-telegram
cd dsh-host-telegram
./install.sh /path/to/deepseek-harness     # your existing dsh checkout

install.sh syncs the plugin under src/packages/host/telegram, registers it in the workspace, and (when DSH_HOME is set) writes the mount row into $DSH_HOME/profiles/web/cordis.patch.yml. It is idempotent โ€” re-run after git pull to update.

Then three steps, once:

# 1) Inject the token (no plaintext anywhere)
echo 'TELEGRAM_BOT_TOKEN=<token-from-@BotFather>' >> "$DSH_HOME/.env"; chmod 600 "$DSH_HOME/.env"

# 2) Whitelist your chat(s) โ€” hot-reloaded, per-field override of the plugin row
#    in $DSH_HOME/settings.yaml:
#    telegram:
#      allowChatIds: [123456789]            # find yours below
#      # proxy: 'http://127.0.0.1:7890'     # only if api.telegram.org is blocked

# 3) Restart your dsh service (your usual start command / deployment script)

Find your chat id: with an empty allowlist, the first run logs every rejected chat id โ€” copy it into allowChatIds and it activates without a restart.

Verify: the log shows telegram: bot @<username> listening; message the bot /start.

Option 2: already-running dsh deployment

If your dsh is not run from a source checkout, install the plugin package into your profile:

dsh plugin --profile web add git+https://github.com/Kevin66Z0/dsh-telegram.git

โ€ฆthen the same token / allowlist / restart steps. This route installs the plugin as a separate package (all @deepseek-ai/* imports resolve from the running host). Don't combine both routes: one plugin row id: telegram per deployment.

Update / remove

cd dsh-host-telegram && git pull && ./install.sh /path/to/deepseek-harness   # update
# or, package route:  dsh plugin --profile web update/remove @deepseek-ai/dsh-host-telegram

โš™๏ธ Configuration

FieldTypeDefaultMeaning
botTokenstringrequiredBot token; referenced as !!js process.env.TELEGRAM_BOT_TOKEN, injected via env or $DSH_HOME/.env
allowChatIdsnumber[]requiredChats allowed to use the console; empty denies everything (first run logs rejected ids)
proxystringALL_PROXY, then HTTPS_PROXYHTTP CONNECT proxy for Telegram API traffic

The plugin registers the telegram settings namespace: fields in $DSH_HOME/settings.yaml (or the Web settings page, Plugins โ†’ Telegram) override the composition row and rebuild the bot session without a restart.

๐Ÿ•น๏ธ Commands

CommandWhat it does
/attach [scope|n|id|none|arc]Bind this chat to a session (scope picker โ†’ session list as inline buttons) and show the recent dialogue + running-turn actions; plain text then goes to the bound session
/createCreation sub-menu: /new (fresh session) or /fork (fork the bound session)
/operateOperation sub-menu: /archive, /stop, /curTasks
/new [path|n|none]Create a session (workspace picker; none = ungrouped)
/fork [n|id]Fork from the last completed turn of a session and bind to it
/archive [n|id]Archive a session (two-step confirm; ungroups it and unbinds)
/delete [n|id]Archive from the picker
/stopCancel the running turn of the bound session (inline list when unbound)
/keyboardRe-wake the reply-keyboard area after /close or an overlay
/status [n|id]Session details: last assistant output, state, usage footer
/model [name]Set the global default model (tap the model list, or type a name)
/rename [title]Rename the bound session (interactive without argument)
/curTasksPrint the bound session's todo list (same source as the Web sidebar)
/preset [name|n]Pick an agent preset (PTC / standard / minimalโ€ฆ)
/startFull help; /close dismisses the keyboards

๐Ÿ” Security & Privacy

  • Whitelist-gated: chats outside allowChatIds are rejected with a fixed generic reply and learn nothing about your sessions.
  • No inbound listener: the plugin only long-polls Telegram outbound; no port to open, no public IP needed, works behind NAT.
  • Secrets not in config: the token is an env reference; docs and templates carry placeholders only.
  • Rotation is cheap: if a token ever leaks, /revoke it in @BotFather โ€” the allowlist, not the token, is the real access control.

๐Ÿง  How it works

  Telegram app          dsh process (one node process)
 โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   HTTPS   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
 โ”‚ your phoneโ”‚ โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ โ”‚ grammY Bot (long polling, this plugin)    โ”‚
 โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  outbound โ”‚      โ”‚ apiProxy (the Web UI channel)      โ”‚
                         โ”‚      โ–ผ                                    โ”‚
                         โ”‚  sessions / agent loop / LLM / tools     โ”‚
                         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The plugin is a Cordis function plugin (name: telegram, inject: [apiProxy]) โ€” the same plugin architecture every dsh component uses, so it composes into any profile that stacks dsh-web-app (headless-only hosts lack apiProxy and cannot mount it). No durable stream of its own: it reads the session/question event feed, exactly like the web UI.

โ“ FAQ

Why does it need the web profile? It drives sessions through apiProxy, the same RPC channel the Web UI uses โ€” a headless-only dsh has no apiProxy.

How do I find my chat id? Leave allowChatIds empty, message the bot once, and copy the id from the log line about the rejected chat.

api.telegram.org is blocked in my region? Configure proxy in the telegram: settings section (defaults to ALL_PROXY/HTTPS_PROXY).

My dsh upstream already ships a telegram package? install.sh overwrites the plugin source with this repo's version; or use only the package route โ€” never both for the same row.

Plugin fails at startup? Usually the token didn't reach the process: write $DSH_HOME/.env and restart. Config errors fail loudly at load.

๐Ÿ› ๏ธ Development

Source mirrors packages/host/telegram from the dsh monorepo. Contributors keep working in the monorepo (its AGENTS.md applies) and sync back here:

bash scripts/sync-from-monorepo.sh [monorepo-path]   # defaults to ../dsh/src

The READMEs (this file and the Chinese mirror) are maintained in this repo and are not overwritten by the script. Built lib/ is committed so package consumers never build.

๐Ÿ“„ License

MIT. docs/official/ mirrors Telegram's public Bot API documentation for offline reference. Built on DeepSeek Harness.