dsh-tg-bridge 1.3.1 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Aleks Vega
3
+ Copyright (c) 2026 dsh-tg-bridge contributors
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,179 +1,179 @@
1
1
  # dsh-tg-bridge
2
2
 
3
- Telegram-мост для [DeepSeek Harness](https://deepseek.com): управляй агентом из
4
- бота — текстом, голосом и фото, без открытия десктопного приложения.
3
+ A Telegram bridge for [DeepSeek Harness](https://deepseek.com): drive the agent from a
4
+ bot with text, voice and photos, without opening the desktop app.
5
5
 
6
- Каждая тема форума = отдельный проект со своей сессией и своим рабочим
7
- каталогом. Ответы приходят с прогрессом, кнопкой «Стоп» и копируемыми блоками
8
- кода.
6
+ Each forum topic is a separate project with its own session and its own working
7
+ directory. Replies arrive with progress, a "Stop" button and copyable code
8
+ blocks.
9
9
 
10
- ## Что умеет
10
+ ## Features
11
11
 
12
- - **Текст** — обычная задача уходит агенту, ответ приходит в ту же тему.
13
- - **Голос** — локальный faster-whisper, расшифровка показывается до ответа.
14
- - **Фото** — уходит в модель со зрением; текст и интерфейс распознаются.
15
- - **Проект на тему** — `/cd pixel-creator`, и все ходы идут из этого каталога.
16
- - **Живой прогресс** — «⏳ Работаю…» с шагами, счётчиком времени и кэшем в финале.
17
- - **Не теряет сообщения** — журнал с подтверждением и переигрыванием.
18
- - **Fail-fast** — при исчерпанной квоте сразу переключается на free-модель.
19
- - **`dsh-tg check`** — диагностика одной командой без изменений в системе.
20
- - **Языки** — меню и служебные сообщения на `en`, `ru`, `zh`; `/lang` переключает язык темы, новый пользователь получает свой по `language_code` из Telegram (всё вне ru/zh → `en`).
21
- - **Кнопки «Стоп»/«Повторить», подтверждения инструментов** — прямо в сообщении, без набора команд.
12
+ - **Text**: a regular task goes to the agent, the answer comes back in the same topic.
13
+ - **Voice**: local faster-whisper, the transcript is shown before the answer.
14
+ - **Photos**: sent to a vision-capable model; text and UI are recognized.
15
+ - **Project per topic**: `/cd my-app`, and all turns run from that directory.
16
+ - **Live progress**: "⏳ Working..." with steps, a timer and the cache hit rate at the end.
17
+ - **No lost messages**: a journal with acknowledgement and replay.
18
+ - **Fail-fast**: when the quota runs out it switches to a free model right away.
19
+ - **`dsh-tg check`**: diagnostics in one command, no changes to the system.
20
+ - **Languages**: menus and service messages in `en`, `ru`, `zh`; `/lang` switches the topic language, a new user gets theirs from the Telegram `language_code` (anything outside ru/zh → `en`).
21
+ - **"Stop"/"Retry" buttons, tool confirmations**: right in the message, no commands to type.
22
22
 
23
- ## Требования
23
+ ## Requirements
24
24
 
25
25
  - Node.js ≥ 20
26
- - Установленный DeepSeek Harness (`~/.dsh/profiles/headless`)
27
- - Токен Telegram-бота от [@BotFather](https://t.me/BotFather)
26
+ - DeepSeek Harness installed (`~/.dsh/profiles/headless`)
27
+ - A Telegram bot token from [@BotFather](https://t.me/BotFather)
28
28
 
29
- ## Языки
29
+ ## Languages
30
30
 
31
- - Меню (`/` в чате) — три набора: `ru` по умолчанию, `en`, `zh`.
32
- - Язык темы: `/lang en|ru|zh`; хранится в `state.json` и переживает рестарт.
33
- - Новый пользователь получает язык из `language_code` Telegram; всё, что не `ru`/`zh`, → `en`.
34
- - Переведены: меню, стартовое сообщение, статус хода, ошибки, `/cd`, `/new`, `/stop`, `/lang`,
35
- ответ на неизвестную команду. Длинные отчёты (`/status`, `/sessions`, `/models` и т. п.)
36
- пока на русском — это следующий проход.
31
+ - Menus (`/` in chat): three sets, `ru` by default, plus `en` and `zh`.
32
+ - Topic language: `/lang en|ru|zh`; stored in `state.json` and survives a restart.
33
+ - A new user gets the language from the Telegram `language_code`; anything that is not `ru`/`zh` → `en`.
34
+ - Translated so far: menus, the start message, turn status, errors, `/cd`, `/new`, `/stop`, `/lang`,
35
+ the reply to an unknown command. Long reports (`/status`, `/sessions`, `/models` and similar)
36
+ are still in Russian, that is the next pass.
37
37
 
38
- ## Установка
38
+ ## Installation
39
39
 
40
- Одна команда: токен бота и свой числовой id. Руками править ничего не нужно.
40
+ One command: your bot token and your numeric id. Nothing to edit by hand.
41
41
 
42
42
  **Windows (PowerShell)**
43
43
 
44
44
  ```powershell
45
- npx dsh-tg-bridge@latest init --token 123456789:AA... --user 311217285
45
+ npx dsh-tg-bridge@latest init --token 123456789:AA... --user 123456789
46
46
  npx dsh-tg-bridge@latest setup
47
47
  ```
48
48
 
49
49
  **macOS / Linux**
50
50
 
51
51
  ```bash
52
- DSH_TG_TOKEN=123456789:AA... DSH_TG_USER=311217285 \
52
+ DSH_TG_TOKEN=123456789:AA... DSH_TG_USER=123456789 \
53
53
  bash -c "$(curl -fsSL https://raw.githubusercontent.com/aleksvega/dsh-tg-bridge/main/install.sh)"
54
54
  ```
55
55
 
56
- **Windows одной строкой** (то же самое, ставит и запускает):
56
+ **Windows, one line** (same thing, installs and starts):
57
57
 
58
58
  ```powershell
59
- $env:DSH_TG_TOKEN='123456789:AA...'; $env:DSH_TG_USER='311217285'
59
+ $env:DSH_TG_TOKEN='123456789:AA...'; $env:DSH_TG_USER='123456789'
60
60
  irm https://raw.githubusercontent.com/aleksvega/dsh-tg-bridge/main/install.ps1 | iex
61
61
  ```
62
62
 
63
- Что делает `init`:
63
+ What `init` does:
64
64
 
65
- | Что | Куда |
65
+ | What | Where |
66
66
  |---|---|
67
- | токен бота | `~/.dsh/.credentials.yaml` → `refs.DSH_TELEGRAM_BOT_TOKEN`; остальные ключи не трогает |
68
- | твой id и рабочий каталог | `~/.dsh/tg-bridge/config.json`; существующий файл не перезаписывает |
69
- | профиль `headless` | проверяет и подсказывает, как создать, если его нет |
67
+ | bot token | `~/.dsh/.credentials.yaml` → `refs.DSH_TELEGRAM_BOT_TOKEN`; other keys are left alone |
68
+ | your id and working directory | `~/.dsh/tg-bridge/config.json`; an existing file is not overwritten |
69
+ | `headless` profile | checked, with a hint on how to create it if missing |
70
70
 
71
- Флаги `init`: `--cwd <путь>` (по умолчанию `~/Projects`), `--voice` (включить
72
- расшифровку голоса), `--dry-run` (показать, что будет записано, и ничего не менять).
71
+ `init` flags: `--cwd <path>` (default `~/Projects`), `--voice` (enable
72
+ voice transcription), `--dry-run` (show what would be written, change nothing).
73
73
 
74
- Id берётся у [@userinfobot](https://t.me/userinfobot), токен — у
74
+ Get your id from [@userinfobot](https://t.me/userinfobot), the token from
75
75
  [@BotFather](https://t.me/BotFather).
76
76
 
77
- ### Настройки бота в Telegram
77
+ ### Bot settings in Telegram
78
78
 
79
- Три вещи, без которых бот будет молчать в группе или работать наполовину:
79
+ Three things without which the bot will stay silent in a group or work only halfway:
80
80
 
81
- 1. **Privacy mode → Disable** (`/setprivacy` у BotFather). Иначе бот видит
82
- только команды, а обычный текст в теме — нет.
83
- 2. **Темы включены** в настройках группы (Edit → Topics). Без них вся работа
84
- идёт в один чат, отдельные сессии на тему не создаются.
85
- 3. **Право Pin Messages** для бота. Нужно только для закрепления первого
86
- сообщения темы; без него мост продолжит работать, но пин не поставит.
81
+ 1. **Privacy mode → Disable** (`/setprivacy` in BotFather). Otherwise the bot sees
82
+ only commands, not regular text in a topic.
83
+ 2. **Topics enabled** in the group settings (Edit → Topics). Without them all work
84
+ goes to a single chat and per-topic sessions are not created.
85
+ 3. **Pin Messages** permission for the bot. Only needed to pin the first
86
+ message of a topic; without it the bridge keeps working but will not pin.
87
87
 
88
- Меню команд настраивать руками не нужно: мост ставит его сам при старте, сразу
89
- на русском, английском и китайском. Полная таблица настроек, права, проверка и
90
- типичные ошибки — [docs/TELEGRAM-SETUP.md](docs/TELEGRAM-SETUP.md).
88
+ You do not need to set up the command menu by hand: the bridge installs it on start,
89
+ in Russian, English and Chinese at once. The full settings table, permissions, checks and
90
+ common errors are in [docs/TELEGRAM-SETUP.md](docs/TELEGRAM-SETUP.md).
91
91
 
92
- **Автозапуск** установщик ставит сам: `launchd` на macOS, `systemd --user` на
93
- Linux. На Windows `setup` регистрирует задачу `DSH-TG-Bridge` и минутный сторож
94
- `DSH-Bridge-Kick`, который поднимает мост после падения.
92
+ **Autostart** is installed by the setup: `launchd` on macOS, `systemd --user` on
93
+ Linux. On Windows `setup` registers the `DSH-TG-Bridge` task and a one-minute watchdog
94
+ `DSH-Bridge-Kick` that brings the bridge back after a crash.
95
95
 
96
- **Вручную** (если нужен полный контроль):
96
+ **Manual install** (if you want full control):
97
97
 
98
98
  ```bash
99
99
  git clone https://github.com/aleksvega/dsh-tg-bridge.git ~/.dsh/tg-bridge
100
100
  cd ~/.dsh/tg-bridge
101
- node cli.mjs init --token 123456789:AA... --user 311217285
102
- node cli.mjs check # отчёт, ничего не меняет
103
- node cli.mjs setup # остановить старый мост и поднять новый
101
+ node cli.mjs init --token 123456789:AA... --user 123456789
102
+ node cli.mjs check # report only, changes nothing
103
+ node cli.mjs setup # stop the old bridge and start the new one
104
104
  ```
105
105
 
106
- Если мост уже стоит, `setup` перезапускает его на новом коде и переустанавливает
107
- меню команд.
106
+ If the bridge is already installed, `setup` restarts it on the new code and reinstalls
107
+ the command menu.
108
108
 
109
- ## Если что-то не так
109
+ ## Troubleshooting
110
110
 
111
- Ответ на ошибку заканчивается кодом — по нему видно, что чинить:
111
+ Error replies end with a code, which tells you what to fix:
112
112
 
113
- | Код | Что значит |
113
+ | Code | Meaning |
114
114
  |---|---|
115
- | `INSUFFICIENT_QUOTA` | баланс провайдера пуст: пополни или оставь free-модели (`/prices`, `/models`) |
116
- | `TG_CONFLICT` | того же бота опрашивает второй процесс: `/doctor`, затем `/restart` |
117
- | `MISSING_CREDENTIAL` | в профиле нет ключа провайдера: добавь в приложении DSH |
118
- | `NO_DSH_CLI` | не найден CLI DeepSeek Harness: проверь установку приложения |
119
- | `NETWORK` | нет сети до провайдера: повтори, проверь VPN/DNS |
120
- | `SANDBOX_DENY` | DSH запретил доступ к файлу или каталогу: проверь путь проекта и права |
121
- | `UNKNOWN_MODEL` | модель из конфига недоступна: список рабочих в `/models` |
122
- | `EMPTY_RESPONSE` | модель ничего не вернула: повтори или смени модель (`/model`) |
123
- | `TURN_FAILED` | причина не распознана: `/doctor` и `/logs errors` |
115
+ | `INSUFFICIENT_QUOTA` | provider balance is empty: top up or stay on free models (`/prices`, `/models`) |
116
+ | `TG_CONFLICT` | a second process is polling the same bot: `/doctor`, then `/restart` |
117
+ | `MISSING_CREDENTIAL` | no provider key in the profile: add it in the DSH app |
118
+ | `NO_DSH_CLI` | DeepSeek Harness CLI not found: check the app installation |
119
+ | `NETWORK` | no network to the provider: retry, check VPN/DNS |
120
+ | `SANDBOX_DENY` | DSH denied access to a file or directory: check the project path and permissions |
121
+ | `UNKNOWN_MODEL` | the model from the config is unavailable: `/models` lists the working ones |
122
+ | `EMPTY_RESPONSE` | the model returned nothing: retry or switch model (`/model`) |
123
+ | `TURN_FAILED` | cause not recognized: `/doctor` and `/logs errors` |
124
124
 
125
- ## Проверка
125
+ ## Checks
126
126
 
127
- Напиши боту:
127
+ Send the bot:
128
128
 
129
- | Команда | Ожидание |
129
+ | Command | Expected |
130
130
  |---|---|
131
- | `/doctor` | отчёт: pid, Telegram, диск, каталог, сессия |
132
- | `/help` | список команд по группам |
133
- | `/cd pixel-creator` | тема привязана к проекту |
134
- | `/prices` | цены моделей и стоимость текущей |
131
+ | `/doctor` | report: pid, Telegram, disk, directory, session |
132
+ | `/help` | commands grouped by category |
133
+ | `/cd my-app` | topic bound to the project |
134
+ | `/prices` | model prices and the cost of the current one |
135
135
 
136
- ## Команды
136
+ ## Commands
137
137
 
138
138
  ```
139
- Работа: /cd · /new · /stop · /send · /seed
140
- Сессии: /status · /sessions · /resume
141
- Модели: /models · /model · /prices
142
- Расширения: /skills · /plugins · /plugin · /soul
143
- Диагностика: /doctor · /logs [errors|N|текст]
144
- Служебное: /app · /restart · /help
139
+ Work: /cd · /new · /stop · /send · /seed
140
+ Sessions: /status · /sessions · /resume
141
+ Models: /models · /model · /prices
142
+ Extensions: /skills · /plugins · /plugin · /soul
143
+ Diagnostics: /doctor · /logs [errors|N|text]
144
+ Service: /app · /restart · /help
145
145
  ```
146
146
 
147
- ## Как это работает
147
+ ## How it works
148
148
 
149
149
  ```
150
- Telegram → bridge.mjs → dsh --profile headless --json → ответ
150
+ Telegram → bridge.mjs → dsh --profile headless --json → reply
151
151
  │
152
- ├─ журнал pending/ + ack (сообщения не теряются)
153
- ├─ очередь на тему (ходы в темах параллельны)
154
- ├─ прогресс + кнопки (видно, что работа идёт)
155
- └─ fail-fast по квоте (free-модели как резерв)
152
+ ├─ pending/ journal + ack (messages are not lost)
153
+ ├─ per-topic queue (turns in topics run in parallel)
154
+ ├─ progress + buttons (you can see the work is going)
155
+ └─ fail-fast on quota (free models as a fallback)
156
156
  ```
157
157
 
158
- Мост — один процесс с long-polling. На каждый ход поднимается отдельный
159
- процесс CLI, который умирает после ответа; поэтому «нет процесса DSH» между
160
- ходами — это норма, а не сбой.
158
+ The bridge is a single process with long polling. Each turn spawns a separate
159
+ CLI process that exits after the answer, so "no DSH process" between
160
+ turns is normal, not a failure.
161
161
 
162
- ### Дополнение во время хода
162
+ ### Steering during a turn
163
163
 
164
- Если написать в тему, пока ход ещё идёт, сообщение не встаёт в очередь за ним:
165
- мост кладёт задание в `~/.dsh/tg-bridge/steer/`, плагин `dsh-steer-inbox`
166
- отдаёт его живому агенту на следующем шаге, и тот сразу отвечает короткой
167
- строкой в чат — что делает сейчас и куда поставил добавку. Если агент молчит
168
- 90 секунд, мост сам пишет одну нейтральную строку, чтобы не было тишины.
164
+ If you write in a topic while a turn is still running, the message does not queue
165
+ behind it: the bridge drops the task into `~/.dsh/tg-bridge/steer/`, the `dsh-steer-inbox`
166
+ plugin hands it to the live agent on its next step, and the agent immediately replies with a short
167
+ line in the chat: what it is doing now and where it put the addition. If the agent stays silent
168
+ for 90 seconds, the bridge writes one neutral line itself so there is no silence.
169
169
 
170
- Нужен плагин из этого репозитория:
170
+ This needs the plugin from this repository:
171
171
 
172
172
  ```bash
173
- dsh plugin --profile headless add link:<путь-к-репозиторию>/plugin/steer-inbox
173
+ dsh plugin --profile headless add link:<path-to-repository>/plugin/steer-inbox
174
174
  ```
175
175
 
176
- и строка в `~/.dsh/profiles/headless/cordis.patch.yml`:
176
+ and a line in `~/.dsh/profiles/headless/cordis.patch.yml`:
177
177
 
178
178
  ```yaml
179
179
  - insert:
@@ -181,27 +181,25 @@ dsh plugin --profile headless add link:<путь-к-репозиторию>/plug
181
181
  name: 'dsh-steer-inbox'
182
182
  ```
183
183
 
184
- Мост сам проверяет профиль: если строки нет, он не пишет задание (иначе оно
185
- просто истечёт), а ведёт себя как раньше — ставит сообщение в очередь.
184
+ The bridge checks the profile itself: if the line is missing, it does not write the task
185
+ (it would just expire) and behaves as before, queueing the message.
186
186
 
187
- ## Безопасность
187
+ ## Security
188
188
 
189
- - `config.json`, `state.json`, `pending/`, логи — в `.gitignore`, в репозиторий
190
- не попадают.
191
- - Токен хранится в хранилище DSH, не в коде и не в конфиге.
192
- - Мост отвечает только id из `allowedUserIds`.
189
+ - `config.json`, `state.json`, `pending/`, logs are in `.gitignore` and never
190
+ end up in the repository.
191
+ - The token is stored in the DSH credentials store, not in code or config.
192
+ - The bridge only replies to ids in `allowedUserIds`.
193
193
 
194
- ## Документация
194
+ ## Documentation
195
195
 
196
- - [docs/TELEGRAM-SETUP.md](docs/TELEGRAM-SETUP.md) — настройки BotFather, группы и права бота
197
- - [docs/STATE.md](docs/STATE.md) — что работает, известные проблемы
198
- - [docs/ROADMAP.md](docs/ROADMAP.md) — что дальше
199
- - [docs/UX-ANALYSIS.md](docs/UX-ANALYSIS.md) — разбор интерактива
200
- - [docs/ACCEPTANCE.md](docs/ACCEPTANCE.md) — критерии приёмки
201
- - [docs/COST.md](docs/COST.md) — сколько стоит один ход и как это уменьшить
202
- - [docs/CHANGELOG.md](docs/CHANGELOG.md) — история изменений
203
- - [docs/RELEASE-CRITERIA.md](docs/RELEASE-CRITERIA.md) — критерии готовности релиза
196
+ - [docs/TELEGRAM-SETUP.md](docs/TELEGRAM-SETUP.md): BotFather settings, group and bot permissions
197
+ - [docs/ROADMAP.md](docs/ROADMAP.md): what comes next
198
+ - [docs/UX-ANALYSIS.md](docs/UX-ANALYSIS.md): interactive UX review
199
+ - [docs/COST.md](docs/COST.md): what one turn costs and how to reduce it
200
+ - [docs/CHANGELOG.md](docs/CHANGELOG.md): change history
201
+ - [docs/RELEASE-CRITERIA.md](docs/RELEASE-CRITERIA.md): release readiness criteria
204
202
 
205
- ## Лицензия
203
+ ## License
206
204
 
207
- MIT — см. [LICENSE](LICENSE).
205
+ MIT, see [LICENSE](LICENSE).
package/bot-menu.mjs CHANGED
@@ -30,7 +30,7 @@ const token = (() => {
30
30
  /** The real, implemented surface of dsh-tg-bridge — nothing aspirational. */
31
31
  const COMMANDS = [
32
32
  { command: 'new', description: 'Новая сессия в этой теме' },
33
- { command: 'cd', description: 'Привязать тему к проекту: /cd pixel-creator' },
33
+ { command: 'cd', description: 'Привязать тему к проекту: /cd my-app' },
34
34
  { command: 'status', description: 'Сессии, модель, активные ходы' },
35
35
  { command: 'stop', description: 'Остановить текущий ход' },
36
36
  { command: 'prices', description: 'Цены моделей и что стоит текущая' },
@@ -54,7 +54,7 @@ const COMMANDS = [
54
54
  /** English menu (language_code: en). Same commands, translated descriptions. */
55
55
  const COMMANDS_EN = [
56
56
  { command: 'new', description: 'New session in this topic' },
57
- { command: 'cd', description: 'Bind this topic to a project: /cd pixel-creator' },
57
+ { command: 'cd', description: 'Bind this topic to a project: /cd my-app' },
58
58
  { command: 'status', description: 'Sessions, model, active turns' },
59
59
  { command: 'stop', description: 'Stop the current turn' },
60
60
  { command: 'prices', description: 'Model prices and the cost of the current one' },
@@ -78,7 +78,7 @@ const COMMANDS_EN = [
78
78
  /** Chinese menu (language_code: zh-hans). */
79
79
  const COMMANDS_ZH = [
80
80
  { command: 'new', description: '在本话题开始新会话' },
81
- { command: 'cd', description: '将本话题绑定到项目:/cd pixel-creator' },
81
+ { command: 'cd', description: '将本话题绑定到项目:/cd my-app' },
82
82
  { command: 'status', description: '会话、模型、进行中的任务' },
83
83
  { command: 'stop', description: '停止当前任务' },
84
84
  { command: 'prices', description: '模型价格与当前模型花费' },
package/bridge.mjs CHANGED
@@ -95,7 +95,7 @@ process.on('exit', (code) => {
95
95
  });
96
96
 
97
97
  const config = {
98
- allowedUserIds: [311217285],
98
+ allowedUserIds: [],
99
99
  profile: 'headless',
100
100
  cwd: join(HOME, 'Documents', 'deepseek-harness', 'default-workspace'),
101
101
  turnTimeoutMs: 20 * 60 * 1000,
@@ -709,7 +709,7 @@ function runTurn(task, sessionId, key = '', onEvent = null, extraArgs = [], cwd
709
709
  args.push(task);
710
710
  const child = spawn(EXE, args, {
711
711
  // Per-topic project directory: a topic can be bound to one repository so
712
- // "работай по pixel-creator" stops meaning "cd in every command".
712
+ // "work in my-app" stops meaning "cd in every command".
713
713
  cwd: cwd && existsSync(cwd) ? cwd : config.cwd,
714
714
  env: {
715
715
  ...process.env,
package/cli.mjs CHANGED
@@ -87,7 +87,7 @@ init:
87
87
  --voice включить голос (нужен python + faster-whisper)
88
88
  --dry-run показать, что будет записано, ничего не менять
89
89
 
90
- Docs: docs/STATE.md, docs/ROADMAP.md, docs/UX-ANALYSIS.md.
90
+ Docs: README.md, docs/TELEGRAM-SETUP.md, docs/ROADMAP.md.
91
91
  `;
92
92
 
93
93
  /**
@@ -1,5 +1,5 @@
1
1
  {
2
- "_comment": "Скопируй в config.json и заполни. chat id узнаёшь у @userinfobot; без него мост не отвечает никому.",
2
+ "_comment": "Copy to config.json and fill in. Get your chat id from @userinfobot; without it the bridge answers nobody.",
3
3
  "allowedUserIds": [],
4
4
  "profile": "headless",
5
5
  "cwd": "",
package/control.mjs CHANGED
@@ -622,7 +622,7 @@ export async function handleControl(ctx) {
622
622
 
623
623
  case '/cd': {
624
624
  // Bind THIS topic to a project directory. Every later turn in the topic
625
- // spawns the CLI with that cwd, so "работай по pixel-creator" stops
625
+ // spawns the CLI with that cwd, so "work in my-app" stops
626
626
  // meaning "cd ... in every single command".
627
627
  const wanted = (arg || '').trim().replace(/^"|"$/g, '');
628
628
  state.cwd = state.cwd || {};
@@ -630,7 +630,7 @@ export async function handleControl(ctx) {
630
630
  const current = state.cwd[key] || config.cwd;
631
631
  const lines = ['Текущий каталог темы:', current, '', 'Привязать: /cd <путь>', 'Сбросить: /cd reset'];
632
632
  try {
633
- const dirs = readdirSync('C:/Users/bistr/Projects', { withFileTypes: true })
633
+ const dirs = readdirSync(config.projectsDir || join(HOME, 'Projects'), { withFileTypes: true })
634
634
  .filter((entry) => entry.isDirectory())
635
635
  .map((entry) => entry.name);
636
636
  if (dirs.length > 0) lines.push('', 'Проекты:', dirs.slice(0, 20).join(' · '));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-tg-bridge",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "description": "Telegram bridge for DeepSeek Harness: text, voice, photos and project-per-topic sessions without opening the desktop app",
5
5
  "type": "module",
6
6
  "main": "bridge.mjs",
@@ -6,6 +6,7 @@
6
6
  // `steer()` — next-step input, not a queued next turn — which is what makes
7
7
  // "добавь ещё вот это" land inside the current work instead of after it.
8
8
  import { mkdirSync, readFileSync, readdirSync, renameSync, statSync, unlinkSync, appendFileSync } from 'node:fs';
9
+ import { randomUUID } from 'node:crypto';
9
10
  import { homedir } from 'node:os';
10
11
  import { join } from 'node:path';
11
12
 
@@ -83,6 +84,12 @@ function apply(ctx, config = {}) {
83
84
  'Then continue the work and include this addition. Do not repeat that line in the final answer.',
84
85
  ].join('\n');
85
86
  agent.steer({
87
+ // DSH stores the steered message verbatim and validates the session
88
+ // log on load: an event whose message has no `id`/`role` makes every
89
+ // later turn fail with "event at seq N lacks an identified message",
90
+ // and the whole topic dies until the log is repaired by hand.
91
+ id: randomUUID(),
92
+ role: 'user',
86
93
  content: [{ type: 'text', text: `${String(job.text ?? '')}${line}` }],
87
94
  source: { kind: 'tg-bridge' },
88
95
  });