# Telegram-мост для DSH [English](README.md) | Русский Плагин для [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH): у каждой сессии DSH появляется свой топик в форум-группе Telegram. Сессию можно вести и из DSH, и из Telegram; обе стороны видят один и тот же диалог. - **Одна сессия — один топик.** У каждой корневой сессии есть топик с именем `воркспейс · название сессии`. Он переименовывается, когда DSH даёт сессии название, закрывается при архивации и открывается при восстановлении. - **В обе стороны.** Сообщения, написанные в DSH, появляются в топике вместе с картинками (несколько — альбомом). Текст, фото, файлы и голосовые из топика попадают в сессию так же, как сообщение из поля ввода DSH. - **Вывод агента.** Ответ стримится в одно сообщение, которое редактируется по мере генерации. Вызовы инструментов сворачиваются в один раскрываемый блок. Длинные ответы приходят файлом `answer.md`; картинки, которые агент вставил в ответ или показал через `present`, приходят фото. Пока агент работает, в топике видно «печатает…»; в конце хода приходит `✅ Done` со звуком. - **Сабагенты.** Сабагенты не создают топиков. Каждый виден одной строкой в топике родителя: «working, waiting for result ⏳», затем «done ✅» (или failed ❌ / cancelled ⏹). - **Подтверждения и вопросы.** Запросы подтверждения и `ask_user_question` приходят с кнопками. Telegram и GUI DSH соревнуются: побеждает тот, кто ответил первым, а запрос на другой стороне снимается; если на вопрос ответили в DSH, в топике показывается выбранный ответ. Подтверждения, вопросы и строка конца хода упоминают (@) того, кто отправил запрос из Telegram, а для запросов из DSH — первого разрешённого пользователя. - **Команды.** `/stop`, `/status`, `/diff`, `/compact`, `/model`, `/permission` и другие в топике сессии; `/sessions`, `/workspaces` и `/new` в General. Остальные команды DSH тоже работают. - **Закреплённая карточка.** Первое сообщение топика (воркспейс, путь, id сессии, модель) закрепляется. - **Страница настроек и ссылка в поле ввода.** Всё настраивается на странице плагина в DSH. В поле ввода DSH есть ссылка **Telegram**, открывающая топик текущей сессии. ## Требования - DeepSeek Harness с менеджером плагинов (desktop или `dsh web`). - Node.js 22 или новее (тот, на котором работает DSH). - Бот Telegram от [@BotFather](https://t.me/BotFather). - Супергруппа Telegram с включёнными **Topics** (темами). - По желанию: `ffmpeg` для голосовых сообщений. ## Установка 1. В DSH откройте **Plugins → Add plugin** и введите: - `github:Nergous/dsh-tg-bridge`, или - абсолютный путь к локальному клону, например `/home/me/projects/dsh-tg-bridge`. 2. Нажмите **Enable now**, затем откройте **Plugins → Telegram bridge**. У плагина нет сборки и зависимостей. После изменения кода хоста (`index.js`, `lib/`) перезапустите DSH; `client.js` перезагружается в открытой странице. ## Настройка 1. **Токен бота.** Создайте бота в @BotFather (`/newbot`), вставьте токен на странице настроек и нажмите **Save token**. Токен хранится в credentials DSH (`DSH_TELEGRAM_BOT_TOKEN`), а не в файлах конфигурации. Переменная окружения с тем же именем тоже работает. 2. **Группа.** Создайте группу, в настройках включите **Topics**. Добавьте бота и сделайте его администратором с правами **Manage topics** и **Pin messages**. 3. **Привязка.** Нажмите **Get bind code** и в течение 10 минут отправьте в группу `/bind <код>`. Группа сохранится, а вы будете добавлены в разрешённые пользователи. Чтобы писать в сессии могли другие, добавьте их Telegram id на странице настроек. Сообщения остальных, ботов и анонимных админов игнорируются. 4. Нажмите **Test connection**: проверяются токен, группа, Topics и права бота. 5. Напишите что-нибудь в любой сессии DSH — в группе появится её топик. Один бот может опрашивать только один процесс. Если одновременно запущены DSH desktop и `dsh web` с этим плагином, Telegram ответит `409 Conflict`; используйте что-то одно или второго бота. ## Использование ### Какие сессии получают топик - Каждая корневая сессия, новая или старая. По умолчанию топик создаётся при первом сообщении или ходе сессии после запуска плагина, так что старая сессия получает топик, когда вы продолжаете её в DSH, или сразу через `/sessions` в General. В **Create topic** можно выбрать «As soon as a session opens» — топик создаётся при открытии сессии, или «Never automatically» — топики создают только `/new` и `/sessions` в General (существующие продолжают работать). - Топик начинается с карточки статуса; прежняя история не копируется. - Если топик удалён в Telegram, следующее сообщение создаст новый. Закрытый топик открывается заново, когда он нужен сессии. - Сессии сабагентов топиков не получают. ### Общение с сессией Пишите в топике сессии, чтобы говорить с ней. Сообщения встают в очередь за текущим ходом, как в DSH. Сообщение, начинающееся с `!`, вместо этого корректирует текущий ход (steer). Пока агент ждёт ответа на вопрос, ваше следующее текстовое сообщение становится ответом. Того, кто последним дал сессии задачу из Telegram, бот упоминает (@username или ссылка с именем) в запросах подтверждения, в первом вопросе пачки и в строке конца хода — уведомление приходит тому, кто ждёт. После сообщения, написанного в DSH, упоминается первый разрешённый пользователь. Когда тегать, выбирается в **Mention me**: на подтверждениях, вопросах и в конце хода (по умолчанию), только на подтверждениях и вопросах, или никогда. Сообщения из DSH показываются как `👤 You (DSH)`; их картинки идут следом — фото, а если их несколько, альбомом (GIF — файлами). Фото прикрепляются как изображения (если модель их принимает), документы — как файлы (до **Max download, MB**). Голосовые расшифровываются и отправляются текстом; расшифровка показывается в топике. Без расшифровщика прикрепляется аудиофайл. Файлы, которые агент показывает инструментом `present`, отправляются в топик документами, а PNG, JPEG и WebP фото (документом, если Telegram не примет фото). Локальные картинки, вставленные в ответ как `![подпись](путь)` (PNG, JPEG, WebP, GIF, BMP, SVG; до 10 на ответ), приходят после ответа фото с подписью, а в тексте на их месте `🖼 подпись`. ### Команды в топике | Команда | Что делает | |---|---| | `/stop` | Остановить текущий ход (очередь сохраняется). | | `/status` | Воркспейс, состояние, модель, заполнение контекста, режим вывода. | | `/diff` | `git diff --stat` каталога сессии, полный патч файлом. | | `/compact` | Сжать диалог (когда сессия свободна). | | `/model [имя]` | Показать модели кнопками или переключиться по имени. | | `/permission [ro\|write\|full]` | Показать доступ (Read Only, Workspace Write, Full Access) кнопками или переключить. Синоним `/access`. Для новой сессии: `/new --ro\|--write\|--full <воркспейс> [промпт]` или кнопки в мастере `/new`. | | `/verbosity full\|tools\|final` | Подробность вывода только в этом топике. Ответ длиннее порога answer.md приходит строкой «Answer in answer.md» и файлом `answer.md`. | | `/mute`, `/unmute` | Приостановить или вернуть вывод агента в топик. Подтверждения и вопросы приходят всегда. | | `/steer <текст>` | То же, что `!текст`. | | `/help` | Список команд. | Остальные команды DSH, например `/goal`, выполняются через реестр команд DSH, как из поля ввода. Неизвестный текст со слэшем уходит в сессию обычным сообщением. ### Команды в General | Команда | Что делает | |---|---| | `/sessions` | Последние сессии DSH кнопками. 🧵 открывает топик сессии; ➕ создаёт (или открывает заново) топик и присылает ссылку — так можно продолжить любую сессию из Telegram. | | `/workspaces` | Воркспейсы DSH. | | `/new` | Выбрать воркспейс кнопкой, затем отправить первое сообщение (текст, фото, файл или голосовое) или начать без него. Топик сессии открывается с вашим сообщением. | | `/new <воркспейс> [текст]` | Сразу начать в воркспейсе (по имени или пути). Без текста бот попросит первое сообщение. | | `/cancel` | Отменить `/new`, который ждёт выбора или сообщения. | | `/help` | Список команд. | ## Параметры Все параметры — на странице настроек плагина, применяются без перезапуска. **Output** | Параметр | По умолчанию | Значение | |---|---|---| | Output detail | инструменты свёрнуты | Всё с раскрытыми инструментами, инструменты свёрнуты или только финальные ответы. `/verbosity` переопределяет для топика. | | Stream answers live | вкл | Редактировать одно сообщение по мере генерации. Выкл — ответ приходит целиком. | | Notify when a turn ends | вкл | Строка `✅ Done` со звуком; всё остальное — без звука. | | Mention me | на подтверждениях, вопросах и в конце хода | Или только на подтверждениях и вопросах, или никогда. @-упоминание автора запроса (из Telegram) или первого разрешённого пользователя (запрос из DSH). | | Show "typing…" while working | вкл | Индикатор «печатает…» во время хода. | | React 👀 / ⚡ when a message is delivered | вкл | Реакция на ваше сообщение после доставки: 👀 в очередь, ⚡ steer. | | Mirror my DSH messages | вкл | Показывать в топике сообщения, написанные в DSH. | | Pinned status card per topic | вкл | Закреплять первое сообщение топика. | | Send answer.md above, characters | 3400 | Ответ длиннее — строка-пометка и `answer.md`; короче — одним или несколькими сообщениями. | **Topics** | Параметр | По умолчанию | Значение | |---|---|---| | Create topic | при первом сообщении | Или сразу при открытии сессии, или никогда автоматически. | | When a session is archived | закрыть топик | Закрыть, удалить или оставить открытым. Восстановление сессии открывает закрытый топик. | **Interaction** | Параметр | По умолчанию | Значение | |---|---|---| | Approvals with buttons | вкл | Выкл — запросы подтверждения только в GUI DSH. | | Agent questions with buttons | вкл | Выкл — вопросы агента только в GUI DSH. | | Send files the agent presents | вкл | Пересылать файлы `present`: картинки фото, остальное документами. | | Steer prefix | `!` | Пусто — префикс выключен. | | Transcribe voice messages | вкл | Выкл — голосовые прикрепляются аудиофайлом. | | Transcription command | пусто | Команда оболочки (POSIX `sh`); `{file}` — WAV 16 кГц моно. Пример: `whisper-cli -l ru -nt -f {file}`. Без неё используется speech-to-text DSH, если установлен. | | Speech language (DSH STT) | пусто | Подсказка языка для speech-to-text DSH. | **Advanced** | Параметр | По умолчанию | Значение | |---|---|---| | Bridge enabled | вкл | Выкл — опрос и зеркалирование останавливаются. | | Stream edit interval, ms | 2500 | Минимальный интервал между правками стримящегося сообщения. | | Messages per minute | 20 | Лимит Telegram на группу, общий для всех топиков. | | Max download, MB | 20 | Максимальный размер файла из Telegram (лимит Bot API — 20). | | ffmpeg path | `ffmpeg` | Для декодирования голосовых. | | Bot API URL | `https://api.telegram.org` | Для своего сервера Bot API. Только `https` или `http` на loopback. | Speech-to-text DSH (`@deepseek-ai/dsh-experimental-voice-input-bundle`) необязателен и не распознаёт русский; для русских голосовых задайте команду расшифровки, например whisper.cpp. ## Ограничения - История в топик не переносится: топик показывает сессию с момента своего создания. - `/sessions` показывает 15 последних сессий; более старые получают топик, когда вы продолжаете их в DSH. - `/model` использует переключатель моделей DSH, который меняет и модель по умолчанию для новых сессий. - В DSH нет события удаления сессии, поэтому при удалении топик остаётся; архивация его закрывает. - На вопрос, время которого в Telegram истекло, можно ответить только в DSH. - Один бот на один запущенный процесс DSH (см. `409 Conflict` выше). ## Как это работает Плагин работает внутри хост-процесса DSH. - Он слушает события сессий DSH (`session/event`, `agent/assistant-stream`, `subagent/start`, `subagent/end`) и отправляет сообщения в Telegram через одну очередь с лимитом на группу. Правки стрима объединяются, поэтому загруженная группа получает меньше правок, а не очередь из них. Сообщения, которые нельзя безопасно повторить, переотправляются, только если Telegram их точно не получил. - Ввод из Telegram идёт через `sessionController.prompt` — тот же метод, что у поля ввода DSH. Неактивная сессия возобновляется со своим пресетом и моделью. - Подтверждения и вопросы перехватываются в цепочках `approval/request` и `user-questions/request` до GUI, и оба ответа соревнуются. GUI DSH показывает запрос сразу; Telegram его не задерживает. - Корневые сессии отличаются от сабагентов по заголовку сессии (`origin: 'subagent'`). Подтверждения сабагентов показываются в топике корневой сессии. - Соответствие сессий и топиков (с последним автором запроса из Telegram для каждой сессии), смещение опроса и имена разрешённых пользователей (для упоминаний) хранятся в хранилище DSH (`~/.dsh/storages/telegram_bridge.json`). - Картинки сообщений из DSH читаются из хранилища вложений DSH (сервис `attachments`). ## Безопасность - Обслуживаются только привязанная группа и разрешённые id. Всё остальное, включая нажатия кнопок, ботов и анонимных админов, игнорируется. - Токен бота хранится в credentials DSH и не попадает в логи, ошибки, конфигурацию и хранилище плагина. Маршруты настроек не могут менять имя credential и URL Bot API. - Маршруты настроек используют аутентификацию DSH. - Любой разрешённый пользователь в группе может управлять вашими сессиями с их правами. Держите группу закрытой. Об уязвимостях сообщайте по [SECURITY.md](SECURITY.md). ## Разработка ```sh npm test # node --test, без зависимостей node scripts/check-repo.mjs # синтаксис, манифест, все тесты ``` Тесты используют поддельный Bot API и поддельные сервисы DSH; к Telegram и настоящему DSH они не обращаются. CI запускает проверку на Linux (Node 22 и 24) и Windows, а также `npm audit`. Прежний Go-мост для Codex App Server сохранён в теге `v0.1.5`. ## Лицензия [MIT](LICENSE)