dsh-telegram-multiagent 1.1.0 → 1.2.0

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.
Files changed (3) hide show
  1. package/README.md +18 -0
  2. package/package.json +1 -1
  3. package/src/client.js +44 -16
package/README.md CHANGED
@@ -142,3 +142,21 @@ MIT.
142
142
  внезапно раскрыть переписку в чужой канал. Ошибка чтения = `personal`, и о ней говорится в журнал.
143
143
 
144
144
  Копируются и вопросы, и ответы: половина разговора без второй половины нечитаема.
145
+
146
+ ## 1.2.0 — отправка переживает сбой сети
147
+
148
+ 🔴 **Отправка теперь повторяется.** До этой версии одна неудачная попытка теряла сообщение
149
+ НАВСЕГДА и не оставляла следа — снаружи это неотличимо от «агент промолчал».
150
+
151
+ Поймано числами: на нашей машине `fetch failed` случается 6-10 раз в час. Копия вопроса не
152
+ дошла, а соседняя отправка четырьмя секундами позже прошла — то есть терялось не по логике, а по
153
+ случайности. Мы к тому моменту полдня искали причину молчания агента в коде, в настройках и в
154
+ чужих ботах.
155
+
156
+ Как сделано: три попытки с растущей паузой (0.4 с, 0.8 с), в журнал пишется, с какой попытки
157
+ удалось. Повторяются **только сетевые сбои**; отказ Telegram по существу (нет прав, чат не найден,
158
+ пустой текст) не повторяется — он воспроизведётся. Длинный опрос намеренно оставлен без повторов:
159
+ у него свой цикл, повтор лишь задержал бы следующий заход.
160
+
161
+ **Урок общий:** канал доставки без повтора — это тихая потеря. Отказ, о котором никто не узнал,
162
+ дороже отказа громкого: его невозможно даже сосчитать.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-telegram-multiagent",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Telegram channel for DeepSeek Harness — one shared module serving several agents, with unforgeable sender marks, merged owner+coordinator memory, and a delivery mode you switch in a file outside the code.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/client.js CHANGED
@@ -15,22 +15,50 @@ export class TelegramClient {
15
15
  this.offset = 0;
16
16
  }
17
17
 
18
- async call(method, payload, timeoutMs = 15000) {
19
- const ctl = new AbortController();
20
- const t = setTimeout(() => ctl.abort(), timeoutMs);
21
- try {
22
- const res = await fetch(`${API}${this.token}/${method}`, {
23
- method: 'POST',
24
- headers: { 'Content-Type': 'application/json' },
25
- body: JSON.stringify(payload ?? {}),
26
- signal: ctl.signal,
27
- });
28
- const data = await res.json();
29
- if (!data.ok) throw new Error(`${method}: ${data.description ?? 'неизвестная ошибка'}`);
30
- return data.result;
31
- } finally {
32
- clearTimeout(t);
18
+ /**
19
+ * 🔴 ПОВТОРЫ ОБЯЗАТЕЛЬНЫ (21.08.2026). Сеть до Telegram бывает дёрганой:
20
+ * у нас `fetch failed` случается 6-10 раз в час. Отправка без повтора теряет
21
+ * сообщение НАВСЕГДА с одной неудачной попытки, и снаружи это выглядит как
22
+ * «агент не ответил» мы потратили вечер, ища причину в коде и в чужих ботах,
23
+ * а терялись именно отправки. Поймано на копии вопроса, которая не дошла,
24
+ * когда соседняя отправка четырьмя секундами позже прошла.
25
+ *
26
+ * Повторяем только СЕТЕВЫЕ сбои и 5xx. Отказ самого Telegram (ok:false
27
+ * нет прав, чат не найден, текст пуст) повторять бессмысленно: он воспроизведётся.
28
+ * Длинный опрос НЕ повторяем: он и так вызывается в цикле, повтор лишь
29
+ * задержит следующий заход.
30
+ */
31
+ async call(method, payload, timeoutMs = 15000, attempts = 3) {
32
+ let lastErr;
33
+ for (let n = 1; n <= attempts; n++) {
34
+ const ctl = new AbortController();
35
+ const t = setTimeout(() => ctl.abort(), timeoutMs);
36
+ try {
37
+ const res = await fetch(`${API}${this.token}/${method}`, {
38
+ method: 'POST',
39
+ headers: { 'Content-Type': 'application/json' },
40
+ body: JSON.stringify(payload ?? {}),
41
+ signal: ctl.signal,
42
+ });
43
+ const data = await res.json();
44
+ if (!data.ok) {
45
+ const err = new Error(`${method}: ${data.description ?? 'неизвестная ошибка'}`);
46
+ err.fromTelegram = true; // отказ по существу — не повторяем
47
+ throw err;
48
+ }
49
+ if (n > 1) this.log(`${method}: удалось с попытки ${n}`);
50
+ return data.result;
51
+ } catch (e) {
52
+ lastErr = e;
53
+ if (e?.fromTelegram || n === attempts) break;
54
+ const wait = 400 * n; // 0.4с, 0.8с — растущая пауза
55
+ this.log(`${method}: попытка ${n} не удалась (${e?.message ?? e}), повтор через ${wait} мс`);
56
+ await new Promise((r) => setTimeout(r, wait));
57
+ } finally {
58
+ clearTimeout(t);
59
+ }
33
60
  }
61
+ throw lastErr;
34
62
  }
35
63
 
36
64
  /** Длинный опрос. Возвращает пришедшие обновления и двигает смещение. */
@@ -38,7 +66,7 @@ export class TelegramClient {
38
66
  // timeout запроса больше, чем у сервера, иначе рвём его же длинный опрос
39
67
  const updates = await this.call('getUpdates',
40
68
  { offset: this.offset, timeout: timeoutSec, allowed_updates: ['message'] },
41
- (timeoutSec + 10) * 1000);
69
+ (timeoutSec + 10) * 1000, 1);
42
70
  for (const u of updates) {
43
71
  if (u.update_id >= this.offset) this.offset = u.update_id + 1;
44
72
  }