DeepSeek Harness 插件

dsh-plugins-iiaarg

Telegram channel for DeepSeek Harness — one shared module serving several agents on a machine, each with its own bot, token file and allow-list.(英文原文)

跳到安装方式

来源信息

GitHub 仓库
iia-arg/dsh-plugins
最近更新
2026年8月21日
分类
工具与能力
GitHub stars
1
载体类型
plugin
包路径
packages/telegram-multiagent
目录证据
上游声明已找到 dsh.bundle
证据路径
packages/telegram-multiagent/package.json#dsh.bundle
核对版本
0.1.0-rc.8
上游核对日期
2026-08-20

该证据由上游目录提供。本站没有安装、运行或安全审核这个插件。

安装

默认先复制一段 Prompt,让 Agent 读 GitHub 仓库和源码;需要自己装时再切到命令。

复制这段 Prompt,发给 DSH、Codex 或其他 Agent,让它先读 GitHub 仓库和源码。

请先不要安装或执行任何命令。阅读这个插件的 GitHub 仓库、README 和关键源码,然后用清楚、直接的方式回答以下问题,帮助我判断它是否适合我的需求:

1. 这个插件是什么,解决什么问题;
2. 适合哪些用户和典型使用场景;
3. 安装后如何使用,并给出一个最小使用示例;
4. 有哪些已知限制,以及隐私、安全、兼容性或维护风险;
5. 给出“推荐 / 有条件推荐 / 不推荐”的明确建议和理由。

请区分仓库明确说明、根据源码推断和未知信息。证据不足时请明确说明,不要猜测或照抄 README。

GitHub:https://github.com/iia-arg/dsh-plugins/tree/HEAD/packages/telegram-multiagent
插件名:dsh-plugins-iiaarg
作者:iia-arg

检查来源文件

安装前先看这个插件目录里的 README 和其他文件。

文件资源管理器2 个文件
README.md来源说明 · 只读预览

dsh-telegram-multiagent

A Telegram channel for DeepSeek Harness: message your agent from a phone, get answers back, keep one conversation per chat.

The harness ships no messenger channel — there is no channel abstraction in the product at all. This plugin builds one out of the two ends the harness does give you:

in:   ctx.agents.create/resume(...) → agent.send(message)
out:  ctx.on('session/event', ...)  → send to Telegram

One chat = one session = one agent. Sessions live independently, the way the core intends.

Install

# inside your profile directory ($DSH_HOME/profiles/<name>)
pnpm add dsh-telegram-multiagent

Then add one row to your agent preset (agent.cordis.yml), not to the profile patch layer — see "Where to put the row" below:

- insert:
    - name: dsh-telegram-multiagent
      config:
        agentName: my-agent            # log label only
        tokenFile: /etc/dsh/bot.token  # preferred: a file readable by this agent
        appDir: /opt/my-agent/app      # where THIS agent's harness is installed
        workspace: /opt/my-agent/work
        allowedUsers: [123456789]      # empty = everyone; you do not want that

Configuration

FieldRequiredWhat it does
tokenFileone of thesePath to a file holding the bot token. Preferred: the secret belongs to the machine, the config only carries a path.
token / tokenEnvone of theseLiteral token, or the name of an env var. For debugging.
appDiryesDirectory where this agent's harness is installed. Platform packages are resolved from here, not from the plugin's own location — see "Why appDir" below.
agentNamenoLabel in log lines. Useful when several bots run on one machine.
allowedUsersnoNumeric user ids allowed to talk to the agent. Empty means everyone — an agent usually has real access to the machine, so set it.
workspacenoWorking directory handed to the agent session.
presetnoAgent preset to mount for sessions created by this channel.
provider / modelnoFallback if the deployment has no default model service.
a2aDirnoDirectory for a file-based agent-to-agent channel (in/, out/). Omit and no such channel exists.
a2aSessionnoSession id used for that channel. Default a2a.
transcribeCommandnoExternal command for voice messages: <cmd> <audio-file> auto → transcript on stdout. Omit and voice is politely refused.

Four things that cost us a day

Each of these fails while looking like success. They are commented inline in the source; this is the short version.

1. Polling belongs to the bot, not to the mount. The harness mounts a composition more than once per process and unmounts the extra one. With a single shared running flag you get two pollers fighting over one bot and Telegram cuts both with Conflict. With a polite "new mount asks the old one to step aside" you get no poller at all — because the new mount is the one that gets unmounted. The fix is reference counting: the first mount starts polling, the last unmount stops it.

2. Delete the incoming message only after it reached the agent. Reading a file and unlinking it immediately loses the message whenever the handoff fails — and the handoff will fail, see (3). From the outside that is indistinguishable from "the bot ignored me".

3. The agent factory appears later than the channel. The first message can arrive before the harness has registered it, and you get no agent factory registered. Wait for the platform instead of assuming it is ready.

4. A session already on disk must be resumed, not created. Calling create() with an existing session id makes the persistence layer abort every turn with an id-collision error. Externally: "accepted the message and went quiet" — the turn honestly starts and dies in milliseconds. It works until the first restart, which is what makes it nasty. Use resume() when the session exists.

Where to put the row

In the web profile the common-plane tools are disabled on purpose; the toolset comes from the agent preset. A plugin row placed in the profile patch layer composes without a single error, is listed as mounted — and never reaches the agent. Put the row in the agent preset.

The preset is also picked up when a session is created: editing the file does not affect a running session. Restart the platform, or change the default preset in settings (hot-reloaded, takes effect for the next created session).

Why appDir

The plugin resolves @deepseek-ai/dsh-llm and @deepseek-ai/dsh-agent from the directory you pass in appDir, not from its own location. That is deliberate: the module is meant to live once on a machine and serve several agents, each with its own harness installation. Hard-linking it to one agent's node_modules would mean that removing that agent breaks the channel for everybody else.

Security notes

  • allowedUsers empty means anyone who finds the bot talks to an agent that usually has shell access

to the machine. Set it.

  • A rejected stranger is logged and reported to the owner. A silent refusal hides a security event.
  • The token is read from a file at startup; the config carries a path, not a secret.

Status

Written for our own fleet and running in production on several agents. The harness is young (0.1.0-rc) and its plugin API moves; expect to adapt. Inline comments are currently in Russian — they carry the reasoning behind each non-obvious line, and a translation is welcome.

MIT.

1.1.0 — кто спросил, кому отвечено, и кто это видит

Три возможности, выросшие из одной задачи: с агентом работают ДВОЕ — владелец из личного чата и координатор по служебному каналу, — и они должны делить одну память, не путаясь, кто есть кто.

Пометка отправителя, которую нельзя подделать. Каждое входящее помечается каналом, из которого пришло. 🔴 Ключевое: перед тем как поставить свою пометку, модуль ВЫРЕЗАЕТ из текста всё похожее на неё. Без этого пометка была бы подписью в тексте, а подпись подделывает любой, кто умеет печатать. Проверено нападением: сообщение из личного чата с готовой строкой «служебный канал» приходит агенту помеченным как личный чат.

Общая память двух собеседников. mergeChatIntoA2A: <идентификатор чата> — сообщения этого чата идут не в свою сессию, а в служебную. Один агент, одна история разговора, два различаемых лица. Побочно лечит столкновение имён инструментов: второй экземпляр набора не монтируется вовсе.

Адресат ответа привязан к ХОДУ, а не к последнему сообщению. Ответ помечается [ответ: кто] с цитатой вопроса. 🔴 Почему не «отвечаем последнему спросившему»: если второй вопрос пришёл, пока первый считается, ответы разъезжаются не тем — причём с правильными на вид пометками.

Режим доставки — в файле ВНЕ кода. Путь берётся из settingsFile (по умолчанию — файл токена с расширением .json). Читается на лету по времени изменения: правка действует со следующего сообщения, перезапуск не нужен. Файл переживает обновление модуля.

{ "deliveryMode": "personal" }
режимкто что видит
personal (умолчание)каждый видит только свои вопросы и ответы
broadcastоба видят весь обмен, чужое помечено «адресовано не вам»
owner-allвладелец видит всё, координатор только своё

🔴 Умолчание выбрано самым тихим намеренно: испорченный или недоступный файл настроек НЕ должен внезапно раскрыть переписку в чужой канал. Ошибка чтения = personal, и о ней говорится в журнал.

Копируются и вопросы, и ответы: половина разговора без второй половины нечитаема.

1.2.0 — отправка переживает сбой сети

🔴 Отправка теперь повторяется. До этой версии одна неудачная попытка теряла сообщение НАВСЕГДА и не оставляла следа — снаружи это неотличимо от «агент промолчал».

Поймано числами: на нашей машине fetch failed случается 6-10 раз в час. Копия вопроса не дошла, а соседняя отправка четырьмя секундами позже прошла — то есть терялось не по логике, а по случайности. Мы к тому моменту полдня искали причину молчания агента в коде, в настройках и в чужих ботах.

Как сделано: три попытки с растущей паузой (0.4 с, 0.8 с), в журнал пишется, с какой попытки удалось. Повторяются только сетевые сбои; отказ Telegram по существу (нет прав, чат не найден, пустой текст) не повторяется — он воспроизведётся. Длинный опрос намеренно оставлен без повторов: у него свой цикл, повтор лишь задержал бы следующий заход.

Урок общий: канал доставки без повтора — это тихая потеря. Отказ, о котором никто не узнал, дороже отказа громкого: его невозможно даже сосчитать.