DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

Telegram Multiagent — DSH Plugin for DeepSeek Harness
← Plugins

dsh-telegram-multiagent

Telegram Multiagent

Канал Telegram для DeepSeek Harness: один модуль на машину обслуживает несколько агентов, у каждого свой бот, свой файл токена и свой список разрешённых. Метка отправителя неподделываема, память владельца и координатора слита в одну, режим доставки перекл

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add dsh-telegram-multiagent@1.5.5
READMECompatibilityVersions

Compatibility and provenance

Telegram Multiagent is published as dsh-telegram-multiagent and currently resolves to version 1.5.5. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
any
Release source
npm
Registry updated
9/20/2026

Versions

1.5.5stable
9/4/2026
1.5.4stable
9/4/2026
1.5.3stable
9/4/2026
Show 15 more versionsCollapse versions
1.5.2stable
9/4/2026
1.5.1stable
9/4/2026
1.5.0stable
9/4/2026
1.4.3stable
9/3/2026
1.4.2stable
9/2/2026
1.4.1stable
9/1/2026
1.4.0stable
9/1/2026
1.3.0stable
8/23/2026
1.2.3stable
8/23/2026
1.2.2stable
8/21/2026
1.2.1stable
8/21/2026
1.2.0stable
8/20/2026
1.1.0stable
8/20/2026
1.0.1stable
8/20/2026
1.0.0stable
8/19/2026

Related plugins

Loading related plugins…

Latest
1.5.5
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
271.3 kB
Files
16
Surface
any
License
MIT
Source
npm
GitHub
★ 4
Weekly downloads
93
Last push
9/6/2026
View source ↗Project homepage ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in

Related plugins

More verified plugins in integrations-communication.

Acp App@deepseek-ai/dsh-acp-appThe dsh ACP profile bundle: automation-only JSON-RPC stdio and process lifecycle over dsh-baseRemote Web Ui@linxin666/dsh-remote-web-uiScan-to-pair remote access for the dsh web GUI that shares one official interface: a QR beside the settings button pairs phones and PCs into the same Web GUI (a portrait-touch adaptation layer for phones, full desktop on PCs) through one-time tokens and rIm@xmanrui/dsh-im把十一种 IM 渠道和公网 AI Office 接入本机 DeepSeek Harness。 Connect eleven IM channels and a public AI Office to a local DeepSeek Harness.Pocketdsh-pocketPut DeepSeek Harness in your pocket: one package, one settings page, and scan a QR code on your phone to access DSH on your computer in sync (LAN + public network, real-time screen mirroring).

README

dsh-telegram-multiagent

Канал Telegram для платформы DeepSeek Harness. Один модуль на машину обслуживает несколько агентов: у каждого свой бот, свой файл токена и свой список тех, кому он отвечает. Агенты не видят переписку друг друга.

Установка

npm install dsh-telegram-multiagent

Строка подключения кладётся в пресет агента (см. «Куда класть строку» ниже):

# внутри каталога профиля ($DSH_HOME/profiles/<имя>)

Настройки

ПолеОбязательноЧто делает
tokenFileодно из трёхПуть к файлу с токеном бота. Предпочтительно: секрет принадлежит машине, настройка несёт только путь.
token / tokenEnvодно из трёхСам токен либо имя переменной окружения. Для отладки.
appDirдаКаталог, где установлена платформа этого агента. Пакеты платформы разрешаются отсюда, а не от места самого плагина — см. «Почему appDir».
agentNameнетПодпись в строках журнала. Полезно, когда на машине несколько ботов.
allowedUsersнетЧисловые идентификаторы тех, кому можно говорить с агентом. Пусто означает «всем» — а у агента обычно есть настоящий доступ к машине, так что задайте.
workspaceнетРабочий каталог, передаваемый сессии агента.
presetнетПресет агента для сессий, созданных этим каналом.
provider / modelнетЗапасной вариант, если в установке нет службы модели по умолчанию.
a2aDirнетКаталог файлового канала между агентами (in/, out/). Не задан — канала нет.
a2aSessionнетИдентификатор сессии для этого канала. По умолчанию a2a.
spoolDirнетСистемный почтовый ящик агента. Не задан — ящик не читается.
transcribeCommandнетВнешняя команда для голосовых: <команда> <файл> auto → расшифровка в stdout. Не задана — голос вежливо отклоняется.
goalUsersнетКому можно ставить цель из Telegram. Пусто — команда отвергается для всех. Намеренно не наследуется от allowedUsers: говорить с агентом и запускать ему платный автономный цикл — разные права.
goalA2ASendersнетКому можно ставить цель из канала между агентами. Отправитель называет себя первой строкой файла From: <имя>. Это учёт, а не удостоверение личности.

🔴 Предел: 3 постановки цели за скользящий час. Считается по отметкам состоявшихся постановок, отдельно на каждый канал. Исчерпан — команда отвергается со словами «зовите человека». СНЯТИЕ цели при исчерпанном пределе разрешено: предел бережёт от разгона, а не запирает возможность остановиться.

Предел не защищает от намеренного действия агента с правами администратора: такой агент снимет и счётчик, и сам предел. Защита здесь от разгона и случайности, а не от намерения.

Пять вещей, стоивших нам дня

Каждая из них отказывает выглядя успехом. В исходниках они помечены комментариями рядом с местом; здесь короткая версия.

1. Опрос принадлежит боту, а не монтажу. Платформа монтирует композицию больше одного раза на процесс и снимает лишний монтаж. С одним общим флагом «идёт» вы получите два опроса, дерущихся за одного бота, и Telegram оборвёт оба с ошибкой Conflict. С вежливым «новый монтаж просит старый посторониться» вы не получите опроса вовсе — потому что снимут именно новый. Лечение — счёт ссылок: первый монтаж запускает опрос, последний снятый его останавливает.

2. Входящее письмо удаляется только после того, как дошло до агента. Прочитать файл и сразу удалить — значит терять сообщение всякий раз, когда передача не удалась. А она не удастся, см. (3). Снаружи это неотличимо от «бот меня проигнорировал».

3. Фабрика агентов появляется позже канала. Первое сообщение может прийти раньше, чем платформа её зарегистрировала, и вы получите no agent factory registered. Ждите платформу, а не предполагайте, что она готова.

4. Сессию, уже лежащую на диске, надо продолжать, а не создавать. Вызов create() с существующим идентификатором даёт новую пустую сессию, и переписка начинается с чистого листа — при том, что старая цела и лежит рядом.

5. Отказ незнакомцу должен быть громким. Молчаливый отказ прячет событие безопасности: владелец не узнает, что кто-то нашёл его бота.

Куда класть строку

В профиле web инструменты общего плана выключены намеренно; набор приходит из пресета агента. Строка плагина, положенная в слой патча профиля, соберётся без единой ошибки, будет числиться смонтированной — и никогда не дойдёт до агента. Кладите строку в пресет агента.

Пресет читается ещё и при создании сессии: правка файла не действует на уже идущую. Перезапустите платформу либо смените пресет по умолчанию в настройках (перечитывается на ходу, действует для следующей созданной сессии).

Почему appDir

Плагин разрешает @deepseek-ai/dsh-llm и @deepseek-ai/dsh-agent из каталога, который вы передали в appDir, а не от собственного расположения. Это сделано намеренно: модуль должен лежать на машине один раз и обслуживать нескольких агентов, у каждого своя установка платформы. Привяжи его жёстко к node_modules одного агента — и удаление ЭТОГО агента сломает канал всем остальным.

Заметки о безопасности

  • Пустой allowedUsers означает, что любой нашедший бота говорит с агентом, у которого обычно есть доступ к оболочке машины. Задайте его.
  • Отвергнутый незнакомец попадает в журнал и в сообщение владельцу. Молчаливый отказ прячет событие безопасности.
  • Токен читается из файла при запуске; настройка несёт путь, а не секрет.
  • Изоляция агентов держится на правах файлов токенов, а не на самом модуле: модуль общий, разделяет их файловая система.

Состояние

Написан для собственного парка агентов и работает в бою на нескольких. Платформа молодая (0.1.0-rc), её интерфейс плагинов меняется — рассчитывайте подстраиваться. Комментарии в исходниках русские: они несут причину каждой неочевидной строки.

MIT.

1.4.3 — команды платформы прямо из чата

Ломающих изменений НЕТ. Добавлены два стенда, из состава ничего не пропало.

Сообщение, начинающееся со слэша, теперь отдаётся реестру команд платформы, а не модели. До этого владелец слал /compact, агент отвечал «сейчас сожму» — и не сжимал ничего: у модели такого инструмента нет вовсе.

КомандаЧто делает
/compact, /cccШтатное сжатие истории (dsh-command-compact).
/compact-status, /csСколько занято контекста и далеко ли до порога сжатия. Команда наша: у платформы своей нет.

Всё остальное со слэшем модель получает как обычный текст: реестр отвечает undefined на незнакомое имя, и проглотить такое сообщение было бы хуже, чем не понять его.

Ответ называет сессию, над которой выполнен. Строчка (сессия <id>) приписывается к любому ответу команды. Стоила она получаса пустого расследования: команда была подана из личного чата, а сжалась другая сессия — выглядело дефектом, а было настройкой mergeChatIntoA2A, сводящей два канала в одну память. Числа настоящие, расхождение настоящее, объяснения нет.

Четыре правила, купленные боем

Служба берётся через ctx.get(имя), а не чтением ctx.имя. Платформа запрещает читать службу без объявления в inject, и проверяет это в момент обращения, а не при монтаже. Обе команды владельца упали в бою на cannot get property "commands" without inject.

И не через inject. Объявление встало бы в pending и обрубило связь с владельцем целиком: канал важнее команды. Проверено канарейкой на другой службе.

Получение службы — внутри try, а общий перехватчик тоже отвечает в чат. Первая правка бросок вылечила, но он улетал в общий перехватчик обновления и оставался только в журнале: владелец не получал ни ответа, ни отказа. Условие «отвечать при любом исходе» было выполнено внутри try и не выполнено снаружи — у защиты было место, где она не применяется, и оно не было названо.

Алиасы разворачиваются в имя штатной команды, а не в свою реализацию. /ccc → /compact. Прямой вызов движка сжатия сработал бы, но не записал бы событие command/run — то самое, которым отличают «команда не дошла» от «механизм молчит». Чинить доставку способом, ослепляющим проверку доставки, нельзя. Концевой якорь в шаблоне обязателен: без него /compactstatus сматчится как /compact и молча сожмёт историю.

Чего /compact-status не выдумывает

Занятость берётся у платформенного tokenMeter.measure — тем же числом платформа решает, пора ли сжимать. Не из usage последнего ответа: там inputTokens бывает равен двум при шестистах тысячах прочитанного из кэша.

Окно контекста спрашивается у службы llm тем же путём, каким его берёт сама платформа: цель из заголовка запроса сессии, окно — у адаптера модели. Не нашлось — команда так и говорит, называя причину, и умолчание не подставляет. Выдуманное окно выглядит как замер и врёт ровно там, где на него посмотрят.

Порог считается как «окно × 0.8». 0.8 — умолчание платформы, и оно верно ровно потому, что в нашем составе сжатие смонтировано без своей настройки. Переопределите thresholdRatio — это число станет неверным, и стенд не покраснеет: он читает наш файл, а не ваш состав.

1.4.0 — причина сетевого отказа и чтение почтового ящика

Ломающих изменений НЕТ. Проверено сравнением состава: из 1.3.0 не пропало ни одного файла, добавилось два стенда. Пути внутри пакета прежние.

пропало:      ничего
добавилось:   test/stend-imena.mjs, test/stend-yashchika-shiny.mjs
изменилось:   src/index.js, test/test-goal-control.mjs, cordis.patch.yml, README.md

Причина сетевого отказа печатается рядом с сообщением. Раньше fetch failed скрывал причину: обрыв соединения, истёкшее ожидание и ненайденное имя выглядели одинаково, и по журналу нельзя было понять, что чинить. Теперь рядом печатается e.cause. Если причины нет — пишется «не указана», а не пустое место: пустое читается как «причины нет» и возвращает ту же слепоту.

🔴 У двух каналов РАЗНЫЕ способы назвать отправителя, и путать их дорого:

канал a2a (каталог a2aDir)  — отправитель из заголовка первой строки: From: <имя>
почтовый ящик (spoolDir)    — отправитель из ИМЕНИ ФАЙЛА: ГГГГММДДTЧЧММССZ-<имя>-<хвост>

Положив в ящик письмо с заголовком From:, вы получите отправителем то, что стоит в имени файла, а сам заголовок останется в тексте письма. Ошибки не будет, отказа тоже — просто отправитель окажется не тем, кого вы назвали. Проверено пробой.

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

И третье следствие того же различия: команды (/goal) из ящика не принимаются вовсе — они живут только в канале a2a, где отправитель подписывает файл сам. Положив /goal в ящик, вы получите обычное письмо с этим текстом: ни выполнения, ни отказа. Право ставить цель поимённое, и проверять его по имени файла, которое задаёт посторонний процесс, означало бы раздать это право владельцу каталога.

Чтение писем из системного почтового ящика (spoolDir). Письмо старше 12 часов подаётся с явной пометкой возраста — иначе двухсуточное письмо толкает отвечать на устаревшее. Больше пяти писем подаются одной пачкой, а не по одному. Ящик не задан — не читается, молчаливого умолчания нет.

Блок управления целями появился в cordis.patch.yml. Раньше goalUsers и goalA2ASenders были описаны в README, но отсутствовали в патче монтажа — то есть настройка была недоступна тому, кто ставил пакет по инструкции.

Проверка, что работает

Стенды едут вместе с кодом:

node test/test-goal-control.mjs        управление целями из канала
node test/stend-imena.mjs              метки отправителя и обезличенность
node test/stend-yashchika-shiny.mjs    чтение почтового ящика

Коды: 0 — сошлось, 1 — расхождение, 2 — проверить нечем (слепота). 🔴 Код 2 не означает «всё хорошо»: он означает, что часть проверок не состоялась. Чаще всего это отсутствие необязательных зависимостей или файлов настроек, которых у только что установленного модуля ещё нет.

Чего 1.4.0 НЕ делает

  • не хранит переписку — она живёт в журнале сессии платформы;
  • не гарантирует доставку при недоступной сети: повторяет опрос и пишет причину отказа в журнал, но письмо в этот момент не уходит;
  • не проверяет, что пишущий в канал между агентами — тот, за кого себя выдаёт: метка отправителя берётся из имени файла, а право положить файл раздаётся правами каталога.

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 по существу (нет прав, чат не найден, пустой текст) не повторяется — он воспроизведётся. Длинный опрос намеренно оставлен без повторов: у него свой цикл, повтор лишь задержал бы следующий заход.

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

1.3.0 — постановка цели прямо из канала

🔴 Зачем это вообще нужно. Если цикл агента ведёт внешний движок — например, агент работает через подписку, а не через родной цикл платформы, — родные инструменты платформы до модели не доходят вовсе. Доходит только то, что имеет форму сообщения. Значит поставить агенту цель изнутри разговора нельзя: руками в веб-интерфейсе можно, а из чата нет. Здесь мы даём постановку из любого канала: сервису целей не нужен инструмент модели, он доступен коду плагина напрямую.

Команды одинаковы в обоих каналах:

/goal                     показать состояние цели
/goal <замысел>           поставить цель
/goal stop                снять цель

Кто вправе — двумя отдельными списками, оба по умолчанию ПУСТЫЕ, то есть команда отказывает всем. Право не наследуется от allowedUsers сознательно: «с кем я разговариваю» и «кто вправе завести дорогой автономный цикл» — разные вопросы, а в allowedUsers завтра добавят гостя.

goalUsers: [123456789]          # из Telegram — по числовому id
goalA2ASenders: ["coordinator"] # из служебного канала — по имени в заголовке

🔴 Чем за это платят, сказать обязано само описание. Проверки платформы (currentInitiator, hasDirectHumanInput, права на корневого агента) живут в её инструменте целей и на этом пути не участвуют. Плагин не обходит политику — он пишет её заново, и две строки выше есть вся политика целиком. Ставя goalUsers, вы соглашаетесь именно на это.

Счётчик постановок: не более трёх за скользящий час на канал. Предел раундов у платформы стоит на ЦЕЛЬ, а не на агента: новая цель — новый счётчик, и поток целей ничем не ограничен. А каталог входящих принадлежит самому агенту, то есть в автономном раунде он способен положить себе файл /goal … и продлить цикл собственными руками.

🔴 Где счётчик НЕ применяется — назвать важнее, чем сам счётчик. Он защищает от РАЗГОНА: от петли, в которую механизм вошёл сам, от ошибки в коде, от лавины повторов. Он не защищает от намеренного действия агента с правами администратора: такой агент снимет и счётчик, и предел раундов одной командой. Изоляция здесь поведенческая, технической границы против самого агента не существует. Настоящий сторож живёт снаружи машины. Снятию цели (/goal stop) счётчик не мешает никогда: механизм, включаемый снаружи и невыключаемый, хуже отсутствия механизма.

Заголовок отправителя — учёт, а не защита. Служебный канал файловый, и вид источника получает любое сообщение в нём; правом «всем, кто пишет в канал» вы раздали бы дорогие циклы кому попало. Поэтому отправитель называет себя первой строкой файла:

From: coordinator
/goal разобрать вчерашние отказы и доложить

Имя подделает любой, кто может положить файл в каталог входящих. Настоящая граница здесь — права ОС на этот каталог. Нужна проверка сильнее самоназвания — это общий секрет в файле с правами, и заводить его надо отдельным решением, а не походя.

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

Служебный маркер цели вырезается из всего, что уходит наружу. Его дело — остановить цикл, а не попасть человеку в чат. Вырезается маркер последней строки; маркер в середине текста оставлен видимым нарочно — он и так отвергнут, и пусть будет заметен читателю.

Ожидание сервисов: где оно есть и где его нет — решение по каждому месту

ctx.get() возвращает undefined молча, пока волокно поставщика не активно. «Сервиса нет в сборке» и «сервис ещё поднимается» приходят одним и тем же значением — и код, написанный как const x = ctx.get('x'); if (x), тихо уходит по ветке «этой возможности просто нет».

местоожиданиепочему
заведение агента (пресет, модель, персистенция)есть, до 30 сидёт вплотную к подъёму платформы, отказ молчалив и разрушителен
/goal → сервисы целей, маркера, мостаесть, короткоответ «сервис не подключён» был бы диагнозом, которого код поставить не может; но на том конце живой собеседник, и полминуты молчания в чате читаются как «бот умер»
вырезание маркера из исходящегонетместо синхронное, а отказ не молчалив: маркер уедет в чат видимым текстом
сообщение об исходе целинетсюда попадают только сессии, где цель уже поставлена, — значит пустота означает не «ещё не поднялся», а «сервис пропал», и это надо сказать, а не переждать

Замечено на живом: порядок готовности волокон меняется от подъёма к подъёму. В одном подъёме ждала фабрика агентов, в другом — набор пресета, причём гонка была в обоих. Значит судить о гонке по соседнему сервису нельзя, и ожидание ставится по устройству места, а не по тому, где однажды видели примету.

Чего эта версия НЕ делает

  • не даёт модели инструмент постановки цели — команда приходит от человека или от координатора через канал, модель её не вызывает и в заголовке запроса плагин невидим;
  • не заменяет права платформы — она их здесь не применяет вовсе (см. выше);
  • не защищает от агента с правами администратора — вся защита поведенческая;
  • не проверяет подлинность отправителя в служебном канале — только самоназвание;
  • не переносит цель через перезапуск процесса: счётчик постановок обнуляется, а связь «сессия → куда сообщить об исходе» живёт в памяти процесса. Сама цель хранится платформой и переживает перезапуск, а вот сообщение об её исходе после перезапуска не придёт.