DeepSeek Harness plugin

dsh-telegram-gum97

Telegram channel for DeepSeek Harness — rich tables and lists, real image delivery both ways, inline-keyboard questions, and in-chat model/preset/mode control

Jump to install

Source facts

Repository
Gum97/dsh-telegram
Latest update
Aug 19, 2026
Category
Remote & Mobile
GitHub stars
0
Format
plugin
Catalog evidence
Upstream dsh.bundle evidence
Evidence path
package.json#dsh.bundle
Checked against
0.1.0-rc.8
Upstream check date
2026-08-21

This evidence comes from the upstream catalog. This site has not installed, run, or security-reviewed the plugin.

Install

Start with a prompt that asks an agent to review the GitHub repository and source. Switch to the command if you want to install it yourself.

Copy this prompt into DSH, Codex, or another agent and ask it to review the GitHub repository and source first.

Do not install or run any commands yet. Read this plugin's GitHub repository, README, and relevant source code. Then answer the questions below clearly and directly so I can decide whether it fits my needs:

1. What is this plugin, and what problem does it solve?
2. Who is it for, and what are its typical use cases?
3. How is it used after installation? Include one minimal example.
4. What known limitations or privacy, security, compatibility, or maintenance risks does it have?
5. Give a clear recommendation: recommend, conditionally recommend, or do not recommend, with reasons.

Distinguish statements documented by the repository, inferences from source code, and unknowns. If evidence is insufficient, say so explicitly. Do not guess or simply repeat the README.

GitHub: https://github.com/Gum97/dsh-telegram
Plugin: dsh-telegram-gum97
Author: Gum97

Check the source files

Read the README and other files from this plugin directory before installing.

File explorer3 files
README.mdSource · read only

dsh-telegram

![npm](https://www.npmjs.com/package/dsh-telegram)

A Telegram channel for DeepSeek Harness.

Written to fix four concrete failures of the generic channel bridge:

ProblemCauseFix
Bold, links and tables render as literal markdownparse_mode was never setMarkdown is compiled to Telegram's rich blocks — real tables and lists — with an HTML path behind it
The agent cannot send imagesthe reply path only ever passed { text }file references in a reply become real sendPhoto / sendDocument uploads
Questions have no answer buttonsno UserQuestionProvider was registeredquestions render as inline keyboards, with multi-select and free-text answers
No way to change model from chatonly /new existed/model, /preset, /mode, /status, /stop, /compact

> Status: early. Verified end-to-end against a live bot on DSH 0.1.0-rc.7, > but not yet tested by anyone else or against another DSH release. Expect the > rough edges of a 0.1.0.

Install

dsh plugin --profile web add dsh-telegram

dsh plugin forwards to pnpm in the profile directory, so the published package above, a git URL (github:Gum97/dsh-telegram) or a local path (file:../dsh-telegram) all work.

Then add the bundle to the profile's package.json:

{
  "dsh": {
    "profile": {
      "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dsh-telegram"]
    }
  }
}

Store the bot token — it is never read from plugin config:

# $DSH_HOME/.credentials.yaml
TELEGRAM_BOT_TOKEN: "123456:ABC-DEF..."

Or paste it into Settings → Plugins → Telegram once the profile is running.

Settings page

The plugin registers the telegram settings namespace and ships a browser card keyed on it, so the bot token, the allowed-user list, the working directory and the display switches are editable from Settings → Plugins without touching a file.

Two behaviours are worth knowing:

  • The token is not stored in the settings document. Secrets are stripped

from every wire response, so the card can only report whether a token is configured, never show one. It writes through the credentials domain, and a blank field leaves the stored token alone.

  • Not every change applies live. The allowed-user list and the display

switches take effect on the next message. The token reference, the working directory and the on/off switch are read when the channel starts, so they apply after DSH restarts — the card says so before you save, and the log repeats it afterwards.

Values pinned in the profile's cordis.patch.yml become the namespace's base layer, so a deployment's choice survives until a user deliberately overrides it, and Reset on a field returns it to that value.

Building from source

The Host half is plain ESM JavaScript and runs as-is. The browser half is the one build step, because DSH's page loader takes a registered CJS factory rather than an ES module:

npm install
npm run build:client   # emits lib/client.js
npm test

prepublishOnly runs both, so a published tarball always carries a bundle that matches its source. React and the DSH client packages stay external require calls resolved by the page — bundling React would give the card a second React instance and break its hooks at runtime.

> The upstream clientBundle build preset is not published, so tsdown.client.ts > here reproduces its contract. This is a known gap in the out-of-repo plugin > story, documented in dsh-client-ui-settings-plugins' own README.

Commands

CommandEffect
/startClaim an unclaimed bot (see below)
/newStart a fresh session for this chat
/modelPick a provider/model from buttons, or /model provider/model
/presetChoose the agent preset used by the next session
/modeSet the sandbox mode (read-only, workspace-write, danger-full-access)
/statusSession id, model, preset, sandbox mode, working directory
/stopCancel the running turn
/compactCompact conversation history
/helpList commands

Who may use the bot

allowedUsers decides, and an empty list means the bot is unclaimed rather than open to everyone.

The first person to send /start to an unclaimed bot is recorded as its owner and the door closes behind them — the way first-run device setup works. Every later stranger is refused and shown their own user id to pass along, and the owner widens access from Settings → Plugins → Telegram.

This matters because a bot's username is discoverable. A bot that accepted anyone would hand whoever found it an agent running shell commands on the owner's machine.

Language

The channel speaks Vietnamese or English, chosen by the language setting and switchable from the settings card. It governs both faces at once — the bot's messages and the card's own labels — because a bot answering in Vietnamese from a card labelled in English is one product speaking two languages at the user.

This is a channel setting rather than the host locale on purpose: DSH ships zh and en only, and its locale schema is a union over exactly those two, so a vi preference is rejected outright. Following the host would have meant hiding Vietnamese behind the Chinese option.

Configuration

KeyDefaultMeaning
enabledtrueTurn the channel off without removing it
tokenRefTELEGRAM_BOT_TOKENCredential name holding the bot token
allowedUsers[]Telegram user ids allowed to talk to the bot; empty means unclaimed
languagevivi or en, for bot messages and the settings card
workspaceRootprocess.cwd()Working directory for channel sessions
routeQuestionstrueAnswer a Telegram session's questions in Telegram, not the browser
streamingtrueEdit one message in place while the answer streams
showToolActivitytrueShow which tool is running inside the preview
richtrueUse real tables and lists (sendRichMessage)
presetAgent preset for new channel sessions
bindingFile$DSH_HOME/dsh-telegram/bindings.jsonWhere the chat→session map lives

Everything above except preset and bindingFile is also user-editable from the Settings page; those two stay deployment concerns, since pointing the channel at an unwritable binding file would break it with no way back.

Design notes

Escaping happens once. Raw text is HTML-escaped up front; inline patterns then run against the escaped text. Because escaping removes every < and >, no user content can forge a tag. Code spans and fences are lifted out before escaping and restored after, so their contents never see emphasis processing.

Chunking splits the source, not the HTML. Slicing rendered HTML at 4096 bytes would cut a message mid-tag and Telegram would reject the whole send. Splitting markdown and rendering each piece guarantees balanced tags, and a fence spanning a boundary is closed and reopened.

Media never escapes the workspace. A referenced path is resolved against the session's workspace root and rejected if it lands outside; the reference stays as text rather than uploading an arbitrary file.

HTML failure degrades, never drops. If Telegram rejects a rendered payload, the client retries once as plain text. A formatting bug costs formatting, not the message.

Only channel-owned turns are delivered. A session can also be driven from the Web UI. The reply router tracks which turns it opened so those replies are not duplicated into the chat.

Questions in a web profile

The host allows exactly one user-questions provider process-wide, and in a web profile the Web bridge (dsh-host-apiproxy) registers first. Left alone, that means a question raised during a Telegram turn is answered in the browser, and the bot appears to stop mid-turn — the worst outcome for someone holding a phone.

So the channel installs a router over whichever provider holds the slot: sessions bound to a Telegram chat get inline keyboards, and every other session reaches the browser exactly as before. Set routeQuestions: false to switch it off and restore the plain behaviour.

This shares a slot the host models as exclusive, so it is written to fail safe: it checks the field is writable and the incumbent is recognisable before touching anything, hands the incumbent every session it cannot resolve, passes that provider's rejections through untouched, and restores it on disposal. A future DSH that makes the field private costs this feature, not the boot.

Tests

node --test 'test/*.test.js'

Covers the renderer (escaping, emphasis, tables, chunk balance, injection attempts) and media extraction (classification, workspace confinement, deduplication, album grouping) without contacting Telegram.