dsh-telegram-multiagent 1.5.1 → 1.5.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/CHANGELOG.md CHANGED
@@ -1,3 +1,41 @@
1
+ ## 1.5.3
2
+
3
+ 🔴 **Починка стенда, который я сломала правкой 1.5.2 — и выпустила, не прочитав его вывод.**
4
+ `stend-svezhesti-opory` вырезает блок свежести из предмета и собирает через `new Function`.
5
+ В 1.5.2 блок стал звать `prichina(e)`, объявленную рядом, но ВНЕ блока — вырезанное перестало
6
+ быть самодостаточным, стенд дал «prichina is not defined» и покраснел на исправном коде.
7
+
8
+ Стенд теперь вырезает блок ВМЕСТЕ С ЗАВИСИМОСТЬЮ; отсутствие `prichina` в предмете даёт
9
+ СЛЕПОТУ с названной причиной, а не расхождение.
10
+
11
+ ⚠️ Мой промах в порядке, а не в коде: я прогнала стенды перед выпуском, увидела вывод и не
12
+ прочла в нём «расхождений 1». Обёртка публикации стенды не проверяет — это её граница, и
13
+ 1.5.2 ушёл в реестр с красным стендом. Получатель прогнал бы `npm test` и увидел красное на
14
+ работающем пакете.
15
+
16
+ ## 1.5.2
17
+
18
+ **Разбор брошенного — по ДВУМ ОСЯМ.** Три места (`Измеритель отказал`, `обход событий отказал`,
19
+ `llm.resolveModelInfo отказал`) собирали причину как `String(e?.message ?? e)`.
20
+
21
+ ось 1 — пустое исключение -> строка НЕ пуста было 6 из 10
22
+ ось 2 — брошенный ПРИМИТИВ -> текст СОХРАНЁН было 4 из 5
23
+ стало: ось 1 — 10 из 10, ось 2 — 4 из 5 (не хуже)
24
+
25
+ 🔴 Первая предложенная редакция закрывала ось 1 и ЛОМАЛА ось 2: `throw 'сессия не открыта'`
26
+ давала «string: (без пояснения)». Пустоту закрыли, потерю открыли рядом. Нашлось это не чтением
27
+ кода, а второй осью — пробы одной оси слепы к дефекту другой, сколько бы их ни было.
28
+
29
+ `code` добавлен сверх присланного замысла: у нас ДОКАЗАНО замером, что при пустом `message`
30
+ причина живёт именно в нём (`ECONNRESET`, `ENOTFOUND` у fetch). Без него три случая из пяти
31
+ дают «(без пояснения)» там, где код известен.
32
+
33
+ ⚠️ Остаток названный: `throw null` и `{}` текста не несут вовсе — там «(без пояснения)» честно,
34
+ а не потеря.
35
+
36
+ Две порчи, каждая ломает СВОЮ ось: первая редакция роняет ось 2, прежнее выражение — ось 1.
37
+ Стенд 8 из 8.
38
+
1
39
  ## 1.5.1
2
40
 
3
41
  🔴 **`errDetail` печатал пустоту после слова «причина».** Собирал причину через `??`, а нулевое
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-telegram-multiagent",
3
- "version": "1.5.1",
3
+ "version": "1.5.3",
4
4
  "description": "Канал Telegram для DeepSeek Harness: один модуль на машину обслуживает несколько агентов, у каждого свой бот, свой файл токена и свой список разрешённых. Метка отправителя неподделываема, память владельца и координатора слита в одну, режим доставки переключается файлом вне кода, цели управляются из канала.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/index.js CHANGED
@@ -1372,6 +1372,30 @@ export function apply(ctx, config = {}) {
1372
1372
  // двоеточия. Хуже отсутствия поля: «причина:» с пустотой читается как «причину
1373
1373
  // узнали, она пустая», а её просто не достали.
1374
1374
  // Воспроизведено: errDetail(cause с message='') -> '', errDetail(AggregateError) -> ''.
1375
+ // 🔴 РАЗБОР БРОШЕННОГО. Замысел соавтора от 04.09 плюс `code` — и то, и другое
1376
+ // по замеру, а не по вкусу.
1377
+ //
1378
+ // ДВЕ ОСИ ПРИЁМКИ, и вторая нашла дефект, к которому первая слепа по построению:
1379
+ // ось 1 — пустое исключение -> строка НЕ пуста
1380
+ // ось 2 — брошенный ПРИМИТИВ -> текст СОХРАНЁН
1381
+ // Прежнее `String(e?.message ?? e)` держало ось 2 (4 из 5) и проваливало ось 1
1382
+ // (6 из 10): у Error с пустым message и у AggregateError печаталась пустота.
1383
+ // Первая присланная редакция закрывала ось 1 и ЛОМАЛА ось 2: `throw 'сессия не
1384
+ // открыта'` давала «string: (без пояснения)» — пустоту закрыли, потерю открыли рядом.
1385
+ //
1386
+ // Здесь: ось 1 — 10 из 10, ось 2 — 4 из 5 (не хуже прежнего).
1387
+ // `code` добавлен сверх замысла соавтора, потому что у нас ДОКАЗАНО замером: при
1388
+ // пустом message причина живёт именно в нём (ECONNRESET, ENOTFOUND у fetch). Без
1389
+ // него три случая из пяти дают «(без пояснения)» там, где код известен.
1390
+ // Остаток названный, а не замазанный: `throw null` и `{}` текста не несут вовсе —
1391
+ // терять там нечего, и «(без пояснения)» тут ЧЕСТНО, а не потеря.
1392
+ function prichina(e) {
1393
+ const tekst = String(e?.message ?? (typeof e === 'object' ? '' : (e ?? ''))).trim();
1394
+ const kod = e?.code ? String(e.code) : '';
1395
+ const hvost = [kod, tekst].filter(Boolean).join(' ') || '(без пояснения)';
1396
+ return ((e?.name ?? typeof e) + ': ' + hvost).slice(0, 90);
1397
+ }
1398
+
1375
1399
  function errDetail(e) {
1376
1400
  const c = e?.cause;
1377
1401
  if (!c) return 'не указана';
@@ -1495,7 +1519,7 @@ export function apply(ctx, config = {}) {
1495
1519
  if (!sess) return { kind: 'error', text: 'Сессия недоступна.' };
1496
1520
  let m;
1497
1521
  try { m = schetchik.measure(sess); }
1498
- catch (e) { return { kind: 'error', text: `Измеритель отказал: ${String(e?.message ?? e).slice(0, 160)}` }; }
1522
+ catch (e) { return { kind: 'error', text: `Измеритель отказал: ${prichina(e)}` }; }
1499
1523
  // 🔴 СВЕЖЕСТЬ ОПОРЫ. baseline.kind различает ПРОИСХОЖДЕНИЕ опоры и НЕ различает
1500
1524
  // её СВЕЖЕСТЬ: при surface-replace (compaction/summary|prune) поверхность
1501
1525
  // пересчитывается, а якорь остаётся прежним и тащит свой kind='usage'
@@ -1536,7 +1560,7 @@ export function apply(ctx, config = {}) {
1536
1560
  return { kind: 'ne-znayu', pochemu: 'у сессии нет ни events, ни eventAt+seq' };
1537
1561
  }
1538
1562
  } catch (e) {
1539
- return { kind: 'ne-znayu', pochemu: `обход событий отказал: ${String(e?.message ?? e).slice(0, 90)}` };
1563
+ return { kind: 'ne-znayu', pochemu: `обход событий отказал: ${prichina(e)}` };
1540
1564
  }
1541
1565
  if (poslednijKompakt < 0) return { kind: 'svezhaya', kompaktov: 0 };
1542
1566
  if (poslednijYakor > poslednijKompakt) return { kind: 'svezhaya', kompaktov: 1 };
@@ -1597,7 +1621,7 @@ export function apply(ctx, config = {}) {
1597
1621
  okno = undefined;
1598
1622
  }
1599
1623
  } catch (e) {
1600
- pochemuNet = `llm.resolveModelInfo отказал: ${String(e?.message ?? e).slice(0, 120)}`;
1624
+ pochemuNet = `llm.resolveModelInfo отказал: ${prichina(e)}`;
1601
1625
  }
1602
1626
  }
1603
1627
  }
@@ -69,5 +69,47 @@ t('cause без единого поля → сказано, что описан
69
69
  nePusto(r, 'пустой объект')
70
70
  })
71
71
 
72
+ // ── ДВЕ ОСИ ПРИЁМКИ ДЛЯ prichina(). ────────────────────────────────────────
73
+ // 🔴 ОСИ, А НЕ «ЕЩЁ ПРОБЫ». Ось — это другой ВОПРОС к тому же предмету, и пробы одной
74
+ // оси слепы к дефекту другой, сколько бы их ни было.
75
+ // ось 1 — пустое исключение -> строка НЕ пуста
76
+ // ось 2 — брошенный ПРИМИТИВ -> текст СОХРАНЁН
77
+ // Первая присланная редакция закрывала ось 1 и ломала ось 2 (`throw 'текст'` давала
78
+ // «string: (без пояснения)»): пустоту закрыли, потерю открыли рядом. Нашлось это не
79
+ // чтением кода, а второй осью.
80
+ const mP = text.match(/function prichina\(e\) \{[\s\S]*?\n \}/)
81
+ if (!mP) { console.log('СЛЕПОТА: функция prichina в предмете не найдена'); process.exit(2) }
82
+ let prichina
83
+ try { prichina = new Function(mP[0] + '; return prichina;')() }
84
+ catch (e) { console.log(`СЛЕПОТА: prichina не собирается: ${e?.message ?? e}`); process.exit(2) }
85
+
86
+ t('ОСЬ 1: пустые исключения дают НЕпустую строку', () => {
87
+ const bed = []
88
+ for (const [imya, v] of [['Error с пустым message', new Error('')],
89
+ ['AggregateError пустой', new AggregateError([], '')],
90
+ ['throw null', null], ['throw undefined', undefined],
91
+ ['объект пустой', {}]]) {
92
+ const r = prichina(v)
93
+ if (typeof r !== 'string' || r.trim() === '' || /^(undefined|null):?\s*$/.test(r.trim()))
94
+ bed.push(`${imya}: «${r}»`)
95
+ }
96
+ if (bed.length) throw new Error(bed.join(' | '))
97
+ })
98
+
99
+ t('ОСЬ 2: брошенный примитив — текст СОХРАНЁН, не заменён пояснением', () => {
100
+ const bed = []
101
+ for (const [v, chto] of [['сессия не открыта', 'сессия не открыта'], [42, '42'],
102
+ [{ message: 'своё' }, 'своё'], [new Error('сеть'), 'сеть']]) {
103
+ const r = prichina(v)
104
+ if (!r.includes(chto)) bed.push(`потерян текст «${chto}»: «${r}»`)
105
+ }
106
+ if (bed.length) throw new Error(bed.join(' | '))
107
+ })
108
+
109
+ t('код ошибки попадает в причину, когда message пуст', () => {
110
+ const r = prichina(Object.assign(new Error(''), { code: 'ECONNRESET' }))
111
+ if (!/ECONNRESET/.test(r)) throw new Error(`код потерян: «${r}»`)
112
+ })
113
+
72
114
  console.log(`итог: ${ok} из ${ok + bed}`)
73
115
  process.exit(bed === 0 ? 0 : 1)
@@ -36,9 +36,21 @@ if (!m) {
36
36
  process.exit(2)
37
37
  }
38
38
  // Оборачиваем вырезанное в функцию от sess: внутри блок обращается только к `sess`.
39
+ // 🔴 ВЫРЕЗАЕМ ВМЕСТЕ С ЗАВИСИМОСТЬЮ. Блок свежести зовёт `prichina(e)` — функцию,
40
+ // объявленную рядом, но ВНЕ блока. Вырезав один блок, стенд получал «prichina is not
41
+ // defined» и краснел на исправном предмете.
42
+ // Это не оговорка к приёму «вырезать из предмета», а его цена: вырезанное должно быть
43
+ // самодостаточным, и когда предмет теряет самодостаточность, стенд обязан сказать об
44
+ // этом — он и сказал. Правка предмета сломала способ его проверки, и это верное красное.
45
+ const mPr = text.match(/function prichina\(e\) \{[\s\S]*?\n \}/)
46
+ if (!mPr) {
47
+ console.log('СЛЕПОТА: блок свежести зовёт prichina(), а её объявления в предмете нет —')
48
+ console.log(' вырезанное не соберётся. Проверить руками, не переименована ли она.')
49
+ process.exit(2)
50
+ }
39
51
  let svezhestOt
40
52
  try {
41
- svezhestOt = new Function('sess', m[0].replace('const svezhest = ', 'return ') .replace(/;$/, '') + ';')
53
+ svezhestOt = new Function('sess', mPr[0] + '\n' + m[0].replace('const svezhest = ', 'return ').replace(/;$/, '') + ';')
42
54
  } catch (e) {
43
55
  console.log(`СЛЕПОТА: вырезанный блок не собирается: ${e?.message ?? e}`); process.exit(2)
44
56
  }