@besales/mcp 0.47.0 → 0.48.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 (33) hide show
  1. package/dist/instructions/server-instructions.d.ts +1 -1
  2. package/dist/instructions/server-instructions.js +14 -6
  3. package/dist/instructions/server-instructions.js.map +1 -1
  4. package/dist/resources/workflows/compare-models.md +4 -2
  5. package/dist/schemas/mcp-tools.json +256 -32
  6. package/dist/tools/definitions/broadcast-analytics.js +7 -3
  7. package/dist/tools/definitions/broadcast-analytics.js.map +1 -1
  8. package/dist/tools/definitions/broadcast-scheduler.d.ts +20 -4
  9. package/dist/tools/definitions/broadcast-scheduler.js +159 -43
  10. package/dist/tools/definitions/broadcast-scheduler.js.map +1 -1
  11. package/dist/tools/definitions/crm-platform.d.ts +2 -0
  12. package/dist/tools/definitions/crm-platform.js +26 -2
  13. package/dist/tools/definitions/crm-platform.js.map +1 -1
  14. package/dist/tools/definitions/follow-up.d.ts +1 -0
  15. package/dist/tools/definitions/follow-up.js +13 -3
  16. package/dist/tools/definitions/follow-up.js.map +1 -1
  17. package/dist/tools/definitions/getcourse-cold-welcome.d.ts +3 -0
  18. package/dist/tools/definitions/getcourse-cold-welcome.js +18 -4
  19. package/dist/tools/definitions/getcourse-cold-welcome.js.map +1 -1
  20. package/dist/tools/definitions/model-comparison.js +2 -2
  21. package/dist/tools/definitions/model-comparison.js.map +1 -1
  22. package/dist/tools/definitions/platform-setup.d.ts +18 -0
  23. package/dist/tools/definitions/platform-setup.js +11 -2
  24. package/dist/tools/definitions/platform-setup.js.map +1 -1
  25. package/dist/tools/definitions/shared.d.ts +42 -0
  26. package/dist/tools/definitions/shared.js +47 -0
  27. package/dist/tools/definitions/shared.js.map +1 -1
  28. package/dist/tools/definitions.d.ts +43 -4
  29. package/dist/tools/registry-mapping-cases-core.js +137 -15
  30. package/dist/tools/registry-mapping-cases-core.js.map +1 -1
  31. package/dist/types/api-contract.gen.d.ts +101 -6
  32. package/docs/agent-usage-guide.ru.md +5 -0
  33. package/package.json +1 -1
@@ -3272,10 +3272,18 @@
3272
3272
  "format": "uuid"
3273
3273
  },
3274
3274
  "name": {
3275
- "type": "string"
3275
+ "type": "string",
3276
+ "minLength": 1,
3277
+ "maxLength": 100,
3278
+ "pattern": "\\S",
3279
+ "description": "Название, уникальное в рамках агента. Пробелы по краям обрезаются; не длиннее 100 знаков — граница общая с формой интерфейса: форма отправляет название целиком при каждом сохранении, и более длинное оператор из неё уже не сохранил бы."
3276
3280
  },
3277
3281
  "condition": {
3278
- "type": "string"
3282
+ "type": "string",
3283
+ "minLength": 1,
3284
+ "maxLength": 4000,
3285
+ "pattern": "\\S",
3286
+ "description": "Условие срабатывания; уходит в системный промпт агента целиком при каждом сообщении. Пробелы по краям обрезаются; не длиннее 4000 знаков — граница общая с формой интерфейса: форма отправляет условие целиком при каждом сохранении, и более длинное оператор из неё уже не сохранил бы."
3279
3287
  },
3280
3288
  "is_enabled": {
3281
3289
  "type": "boolean"
@@ -3729,7 +3737,7 @@
3729
3737
  },
3730
3738
  {
3731
3739
  "name": "besales_followup_sequence_upsert",
3732
- "description": "Create or patch a follow-up sequence definition only. Runtime executions and scheduled messages are read-only; enabling requires explicit_confirmation.",
3740
+ "description": "Create or patch a follow-up sequence definition only. Runtime executions and scheduled messages are read-only; enabling requires explicit_confirmation. The response may carry warnings[] ({code, message}) when the saved chain will select nobody — the write is already done, fix the config instead of retrying.",
3733
3741
  "inputSchema": {
3734
3742
  "type": "object",
3735
3743
  "required": [
@@ -3793,7 +3801,7 @@
3793
3801
  "eventType",
3794
3802
  "variable"
3795
3803
  ],
3796
- "description": "БАЗОВЫЙ отбор: по статусу CRM, по типу события платформы либо variable = базового фильтра нет (отбор только по sequenceConditions). Условия по переменным ортогональны и добавляются через И: чтобы ветвить цепочки по варианту рассылки, сохранив отбор по событию, бери eventType и задай sequenceConditions, а НЕ variable. Неизвестное значение НЕ включает ни один фильтр — цепочка ушла бы всем контактам канала, поэтому бэкенд его отвергает."
3804
+ "description": "БАЗОВЫЙ отбор: по статусу CRM, по типу события платформы либо variable = базового фильтра нет (отбор только по sequenceConditions). Условия по переменным ортогональны и добавляются через И: чтобы ветвить цепочки по варианту рассылки, сохранив отбор по событию, бери eventType и задай sequenceConditions, а НЕ variable. Неизвестное значение НЕ включает ни один фильтр — цепочка ушла бы всем контактам канала, поэтому бэкенд его отвергает. ⚠️ status (и отсутствие поля) выбирает только контакты со статусом CRM: без активной CRM или на площадке, где статуса нет ни у одного контакта (например веб-виджет), цепочка молча не выберет никого — ответ записи вернёт warnings. Там бери eventType (только контакты с уже начатым диалогом) или variable с sequenceConditions."
3797
3805
  },
3798
3806
  "sequenceConditions": {
3799
3807
  "type": "array",
@@ -3875,14 +3883,14 @@
3875
3883
  },
3876
3884
  "dayOffset": {
3877
3885
  "type": "integer",
3878
- "minimum": 0,
3886
+ "minimum": -7,
3879
3887
  "maximum": 30,
3880
- "description": "Сдвиг в днях от якоря."
3888
+ "description": "Сдвиг в днях от якоря; отрицательный — до якоря."
3881
3889
  },
3882
3890
  "validityMinutes": {
3883
3891
  "type": "integer",
3884
3892
  "minimum": 1,
3885
- "description": "Окно валидности слота. Меньше 10 минут крон почти всегда промахнётся."
3893
+ "description": "Окно валидности слота. Крон ходит раз в 5 минут: окно меньше 5 минут он почти всегда пропустит."
3886
3894
  },
3887
3895
  "slotGroup": {
3888
3896
  "type": "string",
@@ -4354,6 +4362,63 @@
4354
4362
  "null"
4355
4363
  ]
4356
4364
  },
4365
+ "instagram_comment_templates": {
4366
+ "type": "object",
4367
+ "description": "Готовые тексты ответов на комментарии Instagram (способ PUBLIC_AND_DM). ПОЛНАЯ ЗАМЕНА: переданный объект записывается целиком, непереданные виды и языки пропадают; пустой объект очищает. Сначала прочитай текущие тексты через besales_platform_context_get и передай всё, что должно остаться.",
4368
+ "properties": {
4369
+ "dm": {
4370
+ "type": "object",
4371
+ "properties": {
4372
+ "ru": {
4373
+ "type": "string",
4374
+ "maxLength": 500
4375
+ },
4376
+ "uz": {
4377
+ "type": "string",
4378
+ "maxLength": 500
4379
+ },
4380
+ "uz_cyrl": {
4381
+ "type": "string",
4382
+ "maxLength": 500
4383
+ }
4384
+ }
4385
+ },
4386
+ "dm_complaint": {
4387
+ "type": "object",
4388
+ "properties": {
4389
+ "ru": {
4390
+ "type": "string",
4391
+ "maxLength": 500
4392
+ },
4393
+ "uz": {
4394
+ "type": "string",
4395
+ "maxLength": 500
4396
+ },
4397
+ "uz_cyrl": {
4398
+ "type": "string",
4399
+ "maxLength": 500
4400
+ }
4401
+ }
4402
+ },
4403
+ "public_complaint": {
4404
+ "type": "object",
4405
+ "properties": {
4406
+ "ru": {
4407
+ "type": "string",
4408
+ "maxLength": 500
4409
+ },
4410
+ "uz": {
4411
+ "type": "string",
4412
+ "maxLength": 500
4413
+ },
4414
+ "uz_cyrl": {
4415
+ "type": "string",
4416
+ "maxLength": 500
4417
+ }
4418
+ }
4419
+ }
4420
+ }
4421
+ },
4357
4422
  "reply_to_story_mentions": {
4358
4423
  "type": "boolean"
4359
4424
  }
@@ -4755,7 +4820,7 @@
4755
4820
  "items": {
4756
4821
  "type": "integer"
4757
4822
  },
4758
- "description": "GetCourse offer ids whose paid orders are credited to us regardless of who created the order — the agent via GETCOURSE_CREATE_DEAL or a manager by hand in the GetCourse admin. Empty array turns the rule off. The set applies to the whole school (every platform sharing the GetCourse domain), not to a single platform. Changes money attribution retroactively — dangerous, requires explicit_confirmation."
4823
+ "description": "GetCourse offer ids whose paid orders are credited to us regardless of who created the order — the agent via GETCOURSE_CREATE_DEAL or a manager by hand in the GetCourse admin. Empty array turns the rule off. An offer is credited ONLY to contacts of the platform it is set on, not school-wide. On a WebWidget platform this is the only allowed getcourse_settings field: it holds the agent sale marker offer (e.g. «Консультация по программе (Ж)»), and any paid school order carrying it is credited to the person's widget card. Changes money attribution retroactively — dangerous, requires explicit_confirmation."
4759
4824
  },
4760
4825
  "guard_active_series": {
4761
4826
  "type": [
@@ -6984,6 +7049,10 @@
6984
7049
  "constant_value": {
6985
7050
  "type": "string",
6986
7051
  "description": "Required when scope=CHANNEL AND is_constant=true"
7052
+ },
7053
+ "repeat_constant_value": {
7054
+ "type": "string",
7055
+ "description": "Cold mailings only: substituted instead of constant_value for a person we have already written to (e.g. intro «это снова Светлана.»). scope=CHANNEL AND is_constant=true only; no curly braces; empty string means not set"
6987
7056
  }
6988
7057
  }
6989
7058
  }
@@ -7078,6 +7147,13 @@
7078
7147
  "null"
7079
7148
  ],
7080
7149
  "description": "CHANNEL scope only; null clears"
7150
+ },
7151
+ "repeat_constant_value": {
7152
+ "type": [
7153
+ "string",
7154
+ "null"
7155
+ ],
7156
+ "description": "Cold mailings only: value for a person we have already written to. CHANNEL constants only; no curly braces; null or empty string clears (the repeat then gets constant_value)"
7081
7157
  }
7082
7158
  }
7083
7159
  }
@@ -8569,7 +8645,7 @@
8569
8645
  },
8570
8646
  {
8571
8647
  "name": "besales_model_comparison_run_start",
8572
- "description": "Запустить прогон сравнения промпта агента на нескольких моделях (один и тот же набор диалогов, меняется только модель агента). ДОРОГО — только по явному запросу оператора, НЕ после каждого фикса. models опционально (по умолчанию ~5 defaultSelected из besales_model_catalog_list), 1..26. dialogue_count по умолчанию 5, до 20. Возвращает run_id для besales_model_comparison_get.",
8648
+ "description": "Запустить прогон сравнения промпта агента на нескольких моделях (один и тот же набор диалогов, меняется только модель агента). ДОРОГО — только по явному запросу оператора, НЕ после каждого фикса. models опционально (по умолчанию ~5 defaultSelected из besales_model_catalog_list), 1..32. dialogue_count по умолчанию 5, до 20. Возвращает run_id для besales_model_comparison_get.",
8573
8649
  "inputSchema": {
8574
8650
  "type": "object",
8575
8651
  "required": [
@@ -8587,8 +8663,8 @@
8587
8663
  "type": "string"
8588
8664
  },
8589
8665
  "minItems": 1,
8590
- "maxItems": 26,
8591
- "description": "Runnable id моделей из каталога (1..26). По умолчанию набор defaultSelected."
8666
+ "maxItems": 35,
8667
+ "description": "Runnable id моделей из каталога (1..35). По умолчанию набор defaultSelected."
8592
8668
  },
8593
8669
  "dialogue_count": {
8594
8670
  "type": "integer",
@@ -11223,7 +11299,7 @@
11223
11299
  },
11224
11300
  {
11225
11301
  "name": "besales_broadcast_launches_list",
11226
- "description": "История запусков рассылок одним списком: наша отправка (cold_welcome, со снимком текста и разбивкой исходов) и волны по файлу (wave).",
11302
+ "description": "История запусков рассылок одним списком: ручная отправка (строка на задачу, пакет — одной строкой с блоками внутри), прогоны расписаний, автоприём регистраций (строка на день) и волны по файлу (wave).",
11227
11303
  "inputSchema": {
11228
11304
  "type": "object",
11229
11305
  "required": [],
@@ -11246,7 +11322,7 @@
11246
11322
  "schedule_id": {
11247
11323
  "type": "string",
11248
11324
  "format": "uuid",
11249
- "description": "Только прогоны этого расписания. Ручные отправки и волны при этом не возвращаются вовсе: расписанием они не запускались."
11325
+ "description": "Только прогоны этого расписания. Ручные отправки, волны и автоприём регистраций при этом не возвращаются вовсе: расписанием они не запускались."
11250
11326
  }
11251
11327
  }
11252
11328
  }
@@ -11630,7 +11706,7 @@
11630
11706
  "target_url": {
11631
11707
  "type": "string",
11632
11708
  "maxLength": 2048,
11633
- "description": "Абсолютный http(s)-адрес, куда ведёт ссылка. Только http/https. В ЗНАЧЕНИЯ query-параметров можно подставлять переменные контакта: {firstName}, {lastName}, {fullName}, {username}, {email}, {phone}, {getcourseUserId}, {getcourseDealId} — например ?email={email}&param1={getcourseUserId}&autologin=1. Переменная обязана составлять ЦЕЛОЕ значение параметра; в хосте, пути, фрагменте, имени параметра или вперемешку с текстом она отвергается 400-й. Пустое у контакта значение убирает параметр из адреса целиком.",
11709
+ "description": "Абсолютный http(s)-адрес, куда ведёт ссылка. Только http/https. В ЗНАЧЕНИЯ query-параметров можно подставлять переменные контакта: {firstName}, {lastName}, {fullName}, {username}, {email}, {phone}, {getcourseUserId}, {getcourseDealId}, а также номер шага дожима {followUpStep} (p1, p2… — вне дожима параметр выпадает) — например ?email={email}&param1={getcourseUserId}&autologin=1 или ?utm_content={followUpStep}. Переменная обязана составлять ЦЕЛОЕ значение параметра; в хосте, пути, фрагменте, имени параметра или вперемешку с текстом она отвергается 400-й. Пустое у контакта значение убирает параметр из адреса целиком.",
11634
11710
  "pattern": "^https?://"
11635
11711
  },
11636
11712
  "product_code": {
@@ -11669,7 +11745,7 @@
11669
11745
  "target_url": {
11670
11746
  "type": "string",
11671
11747
  "maxLength": 2048,
11672
- "description": "Абсолютный http(s)-адрес, куда ведёт ссылка. Только http/https. В ЗНАЧЕНИЯ query-параметров можно подставлять переменные контакта: {firstName}, {lastName}, {fullName}, {username}, {email}, {phone}, {getcourseUserId}, {getcourseDealId} — например ?email={email}&param1={getcourseUserId}&autologin=1. Переменная обязана составлять ЦЕЛОЕ значение параметра; в хосте, пути, фрагменте, имени параметра или вперемешку с текстом она отвергается 400-й. Пустое у контакта значение убирает параметр из адреса целиком.",
11748
+ "description": "Абсолютный http(s)-адрес, куда ведёт ссылка. Только http/https. В ЗНАЧЕНИЯ query-параметров можно подставлять переменные контакта: {firstName}, {lastName}, {fullName}, {username}, {email}, {phone}, {getcourseUserId}, {getcourseDealId}, а также номер шага дожима {followUpStep} (p1, p2… — вне дожима параметр выпадает) — например ?email={email}&param1={getcourseUserId}&autologin=1 или ?utm_content={followUpStep}. Переменная обязана составлять ЦЕЛОЕ значение параметра; в хосте, пути, фрагменте, имени параметра или вперемешку с текстом она отвергается 400-й. Пустое у контакта значение убирает параметр из адреса целиком.",
11673
11749
  "pattern": "^https?://"
11674
11750
  },
11675
11751
  "product_code": {
@@ -11981,6 +12057,41 @@
11981
12057
  "maximum": 365,
11982
12058
  "description": "Не писать контакту, если он получал любую нашу рассылку за N дней"
11983
12059
  },
12060
+ "handoff_from_school_platforms": {
12061
+ "type": "boolean",
12062
+ "description": "ПЕРЕДАЧА: забирать людей с ДРУГИХ площадок GetCourse той же школы В ДОПОЛНЕНИЕ к своим. Своим людям площадки и людям без карточек расписание пишет как обычно, а людей, чья карточка живёт на других площадках школы (и тех, кого прошлая попытка уже перевезла, но сообщение не ушло), перед отправкой переносит сюда — с перепином на заказ сегмента и остановкой цепочек прежней площадки. Поэтому флаг включают прямо на основной авторассылке сегмента, второе расписание на тот же сегмент не нужно (так с 15.09.2026; раньше флаг заменял пул, и свои люди площадки текста не получали). Площадки-источники считает сам отбор («вся школа минус своя»), поэтому новая площадка школы подхватывается без правки расписания. Включайте на площадке, КУДА переносить людей (обычно квал-площадка): на площадке серии флаг заберёт к ней людей квал-площадки. Флаг нужен на ВСЕХ пересекающихся старших сегментах площадки: претензия старшего сегмента о флаге не знает, и человек с соседней площадки иначе не получит ничего. Требует сегмента-ПРОДОЛЖЕНИЯ (снят «только нетронутым», без правила повтора): у человека с соседней площадки право первого сообщения уже сожжено. Ещё требует заполненного домена школы у площадки расписания и хотя бы одной другой площадки GetCourse в школе. Выключено — передачи нет."
12063
+ },
12064
+ "resend_segment_on_new_registration": {
12065
+ "type": "boolean",
12066
+ "description": "ПОВТОР СЕГМЕНТА ПО НОВОЙ РЕГИСТРАЦИИ (с 16.09.2026). Включено — человеку, которому этим сегментом уже писали, расписание напишет снова, только если он зарегистрировался ПОСЛЕ нашей последней отправки этого сегмента и с неё прошла пауза resend_segment_pause_days; продукт регистрации может быть любым. Выключено — сегмент человеку не повторяется никогда (причина alreadySentSegment). Действует только у сегмента-ПРОДОЛЖЕНИЯ без правила повтора: у сегмента «только нетронутым» второе сообщение держит право первого касания (alreadyGreeted). Не передано — у нового расписания включено, у существующего остаётся как было."
12067
+ },
12068
+ "resend_segment_pause_days": {
12069
+ "type": "integer",
12070
+ "minimum": 0,
12071
+ "maximum": 365,
12072
+ "description": "Пауза повтора сегмента: сутки по 24 часа от нашей последней отправки этого сегмента человеку. Пока не прошла — причина resendPause, человек вернётся в пул сам. Не передано — у нового расписания 3, у существующего остаётся как было. ⚠️ Меньше трёх суток повтор обычно не ускорит: разговор после нашей реплики закрывается тремя сутками тишины, и до тех пор человека держит причина dialogQuiet."
12073
+ },
12074
+ "guard_active_series": {
12075
+ "type": [
12076
+ "string",
12077
+ "null"
12078
+ ],
12079
+ "enum": [
12080
+ "OFF",
12081
+ "WARN",
12082
+ "BLOCK",
12083
+ null
12084
+ ],
12085
+ "description": "Перекрытие предохранителя «идёт вебинарная серия» ДЛЯ ЭТОГО расписания. Не передано — у существующего остаётся как было; null — вернуть под настройку площадки. OFF — расписание пишет и во время серии человека; WARN — причина пишется в аудит, отправка идёт; BLOCK — отправка останавливается. Нужно сегментам, которые пишут внутрь серии ПО ЗАМЫСЛУ (якорь FIRST_SESSION_END_PLUS_WAIT, фасет attendedFirstSession=NO): у них серия идёт по определению. Площадочный переключатель для этого не годится — он один на ВСЕ расписания площадки и ослабил бы защиту у postweb*, prelist, closed-app и cta разом. Перекрывается только правило серии: заявка и оплата остаются площадочными."
12086
+ },
12087
+ "skip_after_segment_ids": {
12088
+ "type": "array",
12089
+ "items": {
12090
+ "type": "string"
12091
+ },
12092
+ "maxItems": 20,
12093
+ "description": "НЕ ПИСАТЬ ПОСЛЕ СЕГМЕНТОВ (с 22.09.2026): id сегментов справочника (segment.id из besales_broadcast_schedule_list). Человеку, которому текст любого из них ушёл НЕ РАНЬШЕ его регистрации — той, по которой он попал в пул, — это расписание не пишет (причина skipAfterSegment); новая регистрация после того касания открывает человека снова. Сценарий: у расписания reg-no-show указать сегмент reg-no-show-day1 — получившему «пропустил первый эфир» обычное «не смогли прийти» по той же серии не уйдёт, остальным уйдёт. Не передано — у существующего остаётся как было; [] — снять правило. Сам сегмент расписания и чужие id сервер отвергает. Именно id, а не имена: имя сегмента переименовывают."
12094
+ },
11984
12095
  "is_enabled": {
11985
12096
  "type": "boolean",
11986
12097
  "description": "Включено ли расписание. Создавай выключенным и включай после предпросмотра"
@@ -12161,6 +12272,41 @@
12161
12272
  "maximum": 365,
12162
12273
  "description": "Не писать контакту, если он получал любую нашу рассылку за N дней"
12163
12274
  },
12275
+ "handoff_from_school_platforms": {
12276
+ "type": "boolean",
12277
+ "description": "ПЕРЕДАЧА: забирать людей с ДРУГИХ площадок GetCourse той же школы В ДОПОЛНЕНИЕ к своим. Своим людям площадки и людям без карточек расписание пишет как обычно, а людей, чья карточка живёт на других площадках школы (и тех, кого прошлая попытка уже перевезла, но сообщение не ушло), перед отправкой переносит сюда — с перепином на заказ сегмента и остановкой цепочек прежней площадки. Поэтому флаг включают прямо на основной авторассылке сегмента, второе расписание на тот же сегмент не нужно (так с 15.09.2026; раньше флаг заменял пул, и свои люди площадки текста не получали). Площадки-источники считает сам отбор («вся школа минус своя»), поэтому новая площадка школы подхватывается без правки расписания. Включайте на площадке, КУДА переносить людей (обычно квал-площадка): на площадке серии флаг заберёт к ней людей квал-площадки. Флаг нужен на ВСЕХ пересекающихся старших сегментах площадки: претензия старшего сегмента о флаге не знает, и человек с соседней площадки иначе не получит ничего. Требует сегмента-ПРОДОЛЖЕНИЯ (снят «только нетронутым», без правила повтора): у человека с соседней площадки право первого сообщения уже сожжено. Ещё требует заполненного домена школы у площадки расписания и хотя бы одной другой площадки GetCourse в школе. Выключено — передачи нет."
12278
+ },
12279
+ "resend_segment_on_new_registration": {
12280
+ "type": "boolean",
12281
+ "description": "ПОВТОР СЕГМЕНТА ПО НОВОЙ РЕГИСТРАЦИИ (с 16.09.2026). Включено — человеку, которому этим сегментом уже писали, расписание напишет снова, только если он зарегистрировался ПОСЛЕ нашей последней отправки этого сегмента и с неё прошла пауза resend_segment_pause_days; продукт регистрации может быть любым. Выключено — сегмент человеку не повторяется никогда (причина alreadySentSegment). Действует только у сегмента-ПРОДОЛЖЕНИЯ без правила повтора: у сегмента «только нетронутым» второе сообщение держит право первого касания (alreadyGreeted). Не передано — у нового расписания включено, у существующего остаётся как было."
12282
+ },
12283
+ "resend_segment_pause_days": {
12284
+ "type": "integer",
12285
+ "minimum": 0,
12286
+ "maximum": 365,
12287
+ "description": "Пауза повтора сегмента: сутки по 24 часа от нашей последней отправки этого сегмента человеку. Пока не прошла — причина resendPause, человек вернётся в пул сам. Не передано — у нового расписания 3, у существующего остаётся как было. ⚠️ Меньше трёх суток повтор обычно не ускорит: разговор после нашей реплики закрывается тремя сутками тишины, и до тех пор человека держит причина dialogQuiet."
12288
+ },
12289
+ "guard_active_series": {
12290
+ "type": [
12291
+ "string",
12292
+ "null"
12293
+ ],
12294
+ "enum": [
12295
+ "OFF",
12296
+ "WARN",
12297
+ "BLOCK",
12298
+ null
12299
+ ],
12300
+ "description": "Перекрытие предохранителя «идёт вебинарная серия» ДЛЯ ЭТОГО расписания. Не передано — у существующего остаётся как было; null — вернуть под настройку площадки. OFF — расписание пишет и во время серии человека; WARN — причина пишется в аудит, отправка идёт; BLOCK — отправка останавливается. Нужно сегментам, которые пишут внутрь серии ПО ЗАМЫСЛУ (якорь FIRST_SESSION_END_PLUS_WAIT, фасет attendedFirstSession=NO): у них серия идёт по определению. Площадочный переключатель для этого не годится — он один на ВСЕ расписания площадки и ослабил бы защиту у postweb*, prelist, closed-app и cta разом. Перекрывается только правило серии: заявка и оплата остаются площадочными."
12301
+ },
12302
+ "skip_after_segment_ids": {
12303
+ "type": "array",
12304
+ "items": {
12305
+ "type": "string"
12306
+ },
12307
+ "maxItems": 20,
12308
+ "description": "НЕ ПИСАТЬ ПОСЛЕ СЕГМЕНТОВ (с 22.09.2026): id сегментов справочника (segment.id из besales_broadcast_schedule_list). Человеку, которому текст любого из них ушёл НЕ РАНЬШЕ его регистрации — той, по которой он попал в пул, — это расписание не пишет (причина skipAfterSegment); новая регистрация после того касания открывает человека снова. Сценарий: у расписания reg-no-show указать сегмент reg-no-show-day1 — получившему «пропустил первый эфир» обычное «не смогли прийти» по той же серии не уйдёт, остальным уйдёт. Не передано — у существующего остаётся как было; [] — снять правило. Сам сегмент расписания и чужие id сервер отвергает. Именно id, а не имена: имя сегмента переименовывают."
12309
+ },
12164
12310
  "is_enabled": {
12165
12311
  "type": "boolean",
12166
12312
  "description": "Включено ли расписание. Создавай выключенным и включай после предпросмотра"
@@ -12259,7 +12405,7 @@
12259
12405
  },
12260
12406
  {
12261
12407
  "name": "besales_broadcast_schedule_runs",
12262
- "description": "История прогонов расписания со счётчиками исключений. Scope: read:campaigns. ⚠️ selectedCount — сколько ОТОБРАНО, launchedCount — сколько фактически доставлено; расхождение нормально. WAITING_RESOURCE — воркспейс был занят другим импортом, период не сгорел.",
12408
+ "description": "История прогонов расписания со счётчиками исключений. Scope: read:campaigns. ⚠️ selectedCount — сколько ОТОБРАНО, launchedCount — сколько фактически доставлено; расхождение нормально. WAITING_RESOURCE — прогон отложен (воркспейс занят импортом или пакетом, не ответил источник фактов или не прочитаны старшие сегменты — причина в error), период не сгорел.",
12263
12409
  "inputSchema": {
12264
12410
  "type": "object",
12265
12411
  "properties": {
@@ -12284,8 +12430,8 @@
12284
12430
  }
12285
12431
  },
12286
12432
  {
12287
- "name": "besales_broadcast_schedule_pool_snapshots",
12288
- "description": "Суточные снимки пула отбора по всем расписаниям воркспейса. Scope: read:campaigns. Строка на «расписание × локальный день площадки», включая дни без запуска и выключенные расписания. Три состояния РАЗНЫЕ: строки нет — не считали; status=FAILED — считали и не смогли (poolSize/selectedCount = null, причина в error); truncated — снимок упёрся в бюджет строк источника, показано начало окна, а не окно. Читается мгновенно, в отличие от besales_broadcast_schedule_preview.",
12433
+ "name": "besales_broadcast_pool_today",
12434
+ "description": "«Пул сегодня»: сегменты с правилом на площадке — сколько сейчас в пуле и сколько уйдёт. Scope: read:campaigns. Расчёт идёт на сервере фоном, одной выкачкой источника фактов на площадку: вызов заказывает его и сразу отдаёт последний посчитанный (run); activeRun не пуст — вызови ещё раз через минуту с теми же параметрами, это опрос. Строки с расписанием считаются как его тик — параметры вызова на них не действуют; строки без расписания — по параметрам вызова (окно по умолчанию 3 суток), это верхняя граница. local_day — утренний расчёт того дня (история), без фильтров.",
12289
12435
  "inputSchema": {
12290
12436
  "type": "object",
12291
12437
  "properties": {
@@ -12293,21 +12439,86 @@
12293
12439
  "type": "string",
12294
12440
  "description": "UUID воркспейса. Подставляется MCP-сервером из подключения — спрашивать у пользователя не нужно."
12295
12441
  },
12296
- "from": {
12442
+ "platform_id": {
12297
12443
  "type": "string",
12298
- "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
12299
- "description": "Начало периода включительно: локальный день ПЛОЩАДКИ в формате ГГГГ-ММ-ДД. Проверяется форма; существование даты сверяет сервер."
12444
+ "description": "GetCourse-площадка. Не передан — первая активная площадка с расписанием."
12300
12445
  },
12301
- "to": {
12446
+ "window_days": {
12447
+ "type": "integer",
12448
+ "minimum": 1,
12449
+ "maximum": 16,
12450
+ "description": "Окно свежести строк БЕЗ расписания, в сутках (по умолчанию 3). Потолок 16: окно регистраций строки (окно + 14 суток) обязано уложиться в общую выборку 30 суток."
12451
+ },
12452
+ "funnels": {
12453
+ "description": "Коды воронок источника для строк без расписания, либо \"all\" (без фильтра)",
12454
+ "oneOf": [
12455
+ {
12456
+ "type": "string",
12457
+ "enum": [
12458
+ "all"
12459
+ ]
12460
+ },
12461
+ {
12462
+ "type": "array",
12463
+ "items": {
12464
+ "type": "string"
12465
+ },
12466
+ "maxItems": 100
12467
+ }
12468
+ ]
12469
+ },
12470
+ "product_codes": {
12471
+ "description": "Канонические коды продуктов для строк без расписания, либо \"all\" (без фильтра)",
12472
+ "oneOf": [
12473
+ {
12474
+ "type": "string",
12475
+ "enum": [
12476
+ "all"
12477
+ ]
12478
+ },
12479
+ {
12480
+ "type": "array",
12481
+ "items": {
12482
+ "type": "string"
12483
+ },
12484
+ "maxItems": 50
12485
+ }
12486
+ ]
12487
+ },
12488
+ "channels": {
12489
+ "type": "array",
12490
+ "maxItems": 10,
12491
+ "items": {
12492
+ "type": "string",
12493
+ "enum": [
12494
+ "Site",
12495
+ "Email",
12496
+ "SMS",
12497
+ "Telegram",
12498
+ "Facebook",
12499
+ "VK",
12500
+ "Chatium",
12501
+ "WhatsApp",
12502
+ "Viber",
12503
+ "MAX"
12504
+ ]
12505
+ },
12506
+ "description": "Каналы для строк без расписания (GetCourseTransport). Пусто — без ограничения. Канал, который площадка не обслуживает, отвергается 400: школа отвергает набор целиком."
12507
+ },
12508
+ "cooldown_days": {
12509
+ "type": "integer",
12510
+ "minimum": 0,
12511
+ "maximum": 365,
12512
+ "description": "Тихий период строк без расписания, в сутках; 0 — правило выключено."
12513
+ },
12514
+ "local_day": {
12302
12515
  "type": "string",
12303
12516
  "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
12304
- "description": "Конец периода включительно: локальный день ПЛОЩАДКИ в формате ГГГГ-ММ-ДД."
12517
+ "description": "Локальный день ПЛОЩАДКИ ГГГГ-ММ-ДД: вернуть утренний расчёт этого дня (история «сегмент × день»). С окном, воронками, продуктами, каналами, кулдауном и force не сочетается."
12305
12518
  },
12306
- "limit": {
12307
- "type": "integer",
12308
- "minimum": 1,
12309
- "maximum": 2000,
12310
- "description": "Сколько строк вернуть, считая от самого свежего дня. По умолчанию 2000. Ответ печатается целиком, поэтому у полного периода это мегабайты текста — спрашивай окно, которое читаешь."
12519
+ "force": {
12520
+ "type": "boolean",
12521
+ "description": "Посчитать в обход свежего (до 5 минут) расчёта — только после своей правки расписания, правила сегмента или запуска."
12311
12522
  }
12312
12523
  },
12313
12524
  "required": []
@@ -12315,7 +12526,7 @@
12315
12526
  },
12316
12527
  {
12317
12528
  "name": "besales_broadcast_segment_rule_set",
12318
- "description": "Задать правило отбора сегмента: фасеты поведения, якорь свежести, окно ожидания и приоритет. Scope: launch:campaigns. ⚠️⚠️ 409 означает, что сегментом уже отправляли и правило заморожено — заведи НОВЫЙ сегмент, правка задним числом сделала бы разрез отчёта несопоставимым. Секунды, а не минуты: «от 120 минут» — это activeSecondsMin=7200.",
12529
+ "description": "Задать правило отбора сегмента: фасеты поведения, якорь свежести, окно ожидания и приоритет. Scope: launch:campaigns. ⚠️⚠️ 409 означает, что сегментом уже отправляли и правило заморожено — заведи НОВЫЙ сегмент, правка задним числом сделала бы разрез отчёта несопоставимым. У замороженного меняются только priority и facets.untouchedOnly (остальное правило передай тем же). Секунды, а не минуты: «от 120 минут» — это activeSecondsMin=7200.",
12319
12530
  "inputSchema": {
12320
12531
  "type": "object",
12321
12532
  "properties": {
@@ -12340,6 +12551,15 @@
12340
12551
  ],
12341
12552
  "description": "Был на вебинаре. NO проходит только достоверный «не пришёл»"
12342
12553
  },
12554
+ "attendedFirstSession": {
12555
+ "type": "string",
12556
+ "enum": [
12557
+ "YES",
12558
+ "NO",
12559
+ "ANY"
12560
+ ],
12561
+ "description": "Был на ПЕРВОМ эфире серии. NO — пропустил первый эфир, и это единственный способ написать человеку, пока его серия ИДЁТ (attended спрашивает про всю серию и до её конца равен «нечем проверить»). Требует freshness_anchor=FIRST_SESSION_END_PLUS_WAIT и guard_active_series=OFF у расписания, иначе предохранитель площадки остановит отправку."
12562
+ },
12343
12563
  "activeSecondsMin": {
12344
12564
  "type": "integer",
12345
12565
  "minimum": 0,
@@ -12417,7 +12637,7 @@
12417
12637
  "key",
12418
12638
  "minDaysSince"
12419
12639
  ],
12420
- "description": "ПОВТОРНОЕ касание: сегмент уходит тем, у кого названный сегмент был ПОСЛЕДНИМ нашим касанием и кто нам не ответил. Снимает вечный одноразовый claim приветствия — единственную защиту от второго сообщения живому человеку"
12640
+ "description": "ПОВТОРНОЕ касание: сегмент уходит тем, у кого названный сегмент был ПОСЛЕДНИМ касанием — нашей отправкой или волной школы на этой площадке — и кто нам не ответил; нужна живая карточка человека на этой площадке. Снимает вечный одноразовый claim приветствия — единственную защиту от второго сообщения живому человеку"
12421
12641
  },
12422
12642
  "untouchedOnly": {
12423
12643
  "type": "boolean",
@@ -12434,11 +12654,12 @@
12434
12654
  "enum": [
12435
12655
  "SERIES_END",
12436
12656
  "SERIES_END_PLUS_WAIT",
12657
+ "FIRST_SESSION_END_PLUS_WAIT",
12437
12658
  "LAST_APPLICATION",
12438
12659
  "REGISTRATION",
12439
12660
  "PRELIST"
12440
12661
  ],
12441
- "description": "Откуда берётся момент входа контакта в сегмент"
12662
+ "description": "Откуда берётся момент входа контакта в сегмент. FIRST_SESSION_END_PLUS_WAIT — конец ПЕРВОГО эфира серии плюс ожидание: для сегментов, которые пишут пропустившему первый эфир, пока серия ещё идёт"
12442
12663
  },
12443
12664
  "wait_after_series_end_hours": {
12444
12665
  "type": "integer",
@@ -13194,7 +13415,10 @@
13194
13415
  "besales_trigger_claim_list",
13195
13416
  "besales_trigger_claim_reset"
13196
13417
  ],
13197
- "newly_added_in_broadcast_pool_snapshots": [
13418
+ "newly_added_in_broadcast_pool_today": [
13419
+ "besales_broadcast_pool_today"
13420
+ ],
13421
+ "removed_in_broadcast_pool_today_2026_09_11": [
13198
13422
  "besales_broadcast_schedule_pool_snapshots"
13199
13423
  ],
13200
13424
  "newly_added_in_task_state": [
@@ -506,12 +506,16 @@ export const broadcastLaunchTools = [
506
506
  };
507
507
  },
508
508
  enhanceResponse: (response) => withInstructions(response, [
509
- 'История запусков ТРЁХ видов. kind=cold_welcome — наша отправка (messageSnapshot = что реально ушло), kind=wave — волна по файлу (текст доставляет процесс школы).',
510
- '⚠️⚠️ Третий вид РАЗЛИЧАЕТСЯ НЕ ПО kind, а по наличию scheduleRun: у прогона расписания механизм тот же cold_welcome (пишем первыми сами), различает их то, ЧТО запустило отправку. У такой строки totalContacts — это ПУЛ до исключений (а не размер когорты), sentCount — фактически доставленные, failedCount ВСЕГДА 0, outcomes ВСЕГДА null: разницу объясняет цепочка в scheduleRun (poolSize, excludedBy по причинам, selectedCount, reason).',
509
+ 'История запусков ЧЕТЫРЁХ видов. kind=cold_welcome — наша отправка (messageSnapshot = что реально ушло), kind=wave — волна по файлу (текст доставляет процесс школы). Прогон расписания и автоприём регистраций — тоже kind=cold_welcome: их различают НЕ по kind, а по наличию scheduleRun и registrationDay.',
510
+ '⚠️⚠️ Прогон расписания — строка с scheduleRun: механизм тот же cold_welcome (пишем первыми сами), различает их то, ЧТО запустило отправку. У такой строки totalContacts — это ПУЛ до исключений (а не размер когорты), sentCount — фактически доставленные, failedCount ВСЕГДА 0, outcomes ВСЕГДА null: разницу объясняют scheduleRun.selection и scheduleRun.facts (и reason).',
511
+ '⚠️⚠️ Числа прогона называй так же, как вкладки «История» и «Пул сегодня»: в строке «взято · отправлено», а план по людям — scheduleRun.dayBreakdown: расклад сегмента на день прогона из утреннего расчёта «Пула сегодня», те же числа, что вкладка «Пул» показывает у сегмента на этот день (registrations — регистраций, вошедших в сегмент от прошлого запуска; people — разных людей среди них; excluded — людей по группам отсева, словарь как у selection.excluded; selected — уйдёт из людей дня; carriedOver — уйдёт людей с прошлых дней; посчитан до запуска, поэтому selected + carriedOver может расходиться с selection.taken; null — утро этот сегмент не посчитало, плана нет, а не ноль). Пул запуска человеку людьми дня не называй: это регистрации всего окна свежести, почти всем из которых уже писали. scheduleRun.poolSize — пул запуска: регистрации в окне свежести авторассылки, которые запуск рассмотрел (не люди: человек с несколькими регистрациями посчитан несколько раз; null — пул ещё не измерен). scheduleRun.selection — те же числа, что facts.scheduleRuns у besales_broadcast_pool_today: collapsed — несколько регистраций у одного человека (poolSize − collapsed = людей в пуле); excluded — людей по группам отсева (noMessenger — нет мессенджера (в том числе прошлая подача этим расписанием закончилась «карточку не завели» или «каналами не достать»), inContact — на связи или писали недавно (этим сегментом уже писали, идёт разговор или трое суток тишины после нашей реплики, пауза повтора сегмента, пауза после любого нашего сообщения (поле расписания «Не писать, если писали за N дней»), подача этим расписанием ушла или ещё идёт), paid — купил продукт после регистрации, по которой попал в сегмент, withManager — с ним работает менеджер (действующая заявка на продукт после этой регистрации, операторский статус или передача диалога в отдел моложе трёх суток), blocked — бан, удалённая карточка или действующий мьют, activeSeries — идёт другая вебинарная серия, retryPause — прошлая попытка отправки этим расписанием не удалась, ждём паузу, overCap — не поместились в дневной потолок, higherPriority — уйдут в сегмент с большим приоритетом, otherPlatform — карточка на другой площадке школы, и эта авторассылка её не забирает (передача выключена, там ещё идёт серия эфиров или её дата неизвестна, карточек несколько, перенос не удался или уже увёз человека отсюда; у расчётов и прогонов до 22.09.2026 этой группы нет — эти люди в other), other — прочее (предохранитель не смог проверить заявки и оплаты, карточки на этой площадке нет, повтору не нашлось прошлого касания, непригодный номер человека); у прогонов до 16.09.2026 к новым группам добавляются прежние held (купил, менеджер, бан или мьют) и pause (пауза), только ненулевые; alreadyWritten у прогонов не бывает — называй их так и не складывай с новыми); unrecorded — людей пула без записанной причины (должно быть 0); taken — взято запуском (люди журнала подач, с этого числа начинается отправка); selection=null — пул не измерен. scheduleRun.facts — отправка по людям: sent — отправлено, noMessenger — карточку не завели или ни один канал не доходит, pending — ещё отправляется, notSent — не ушло по другой причине, notSentByReason — те же notSent по причинам: один исход на человека, сумма равна notSent; ключ — исход отказа отправки (SKIPPED_BOT_SILENCED — бот выключен у контакта, SKIPPED_ALREADY_IN_DIALOG — разговор уже идёт, SKIPPED_UNRESOLVED_VARS — переменные текста без значения и соседи, те же коды, что в outcomes ручной отправки), без следа отправки — исход журнала подач (UNRESOLVED_AFTER_BATCH — причина не записана: прогоны до 16.09.2026, RELEASED_LAUNCH_FAILED — запуск сорвался); человеку называй причины словами, а не кодами; facts=null — людей прогон не брал. excludedBy и selectedCount — сырые счётчики прогона: selectedCount после подачи уже без людей, которых импорт не завёл, — «взято» по нему не называй; причины источника в excludedBy (unverifiable, lowCoverageSkipped и соседи) отсеяны ДО пула — с пулом их не складывай и человеку не показывай как отсев пула.',
512
+ '⚠️⚠️ Ручная отправка ПАКЕТОМ — ОДНА строка с batch: id строки = batch.batchId, batch.blocks — блоки «сегмент × продукт × воронка» в порядке исполнения (status=pending и importJobId=null — блок ещё не стартовал). totalContacts — план всего пакета, sentCount/failedCount/outcomes — суммы по блокам, tracedOutcomes у строки пакета ВСЕГДА null: поимённый разбор — у блоков, по их importJobId. Пакет из одного блока отдаётся обычной строкой задачи (batch=null). Остановить пакет — besales_getcourse_cold_welcome_batch_cancel с batch.batchId, а не отменой задачи по id строки.',
513
+ '⚠️ Автоприём регистраций (приветствие по вебхуку регистрации) — ОДНА строка на день с registrationDay.day (дата МСК): sentCount = totalContacts = отправлено за день (списка «взяли» у потока нет), outcomes и tracedOutcomes null, status=running значит «день ещё идёт», а не «идёт прогон». Останавливать у такой строки нечего.',
511
514
  '⚠️ scheduleRun.reason приходит и у прогона, доставившего ЧАСТЬ подачи: пакет пишет ошибку вместе с числом отправленных. status=failed у такой строки означает «дочерний пакет вернул не COMPLETED», а не «не ушло ничего».',
512
- 'schedule_id — только прогоны этого расписания; ручные отправки и волны при этом не возвращаются вовсе, они расписанием не запускались.',
515
+ 'schedule_id — только прогоны этого расписания; ручные отправки, волны и автоприём регистраций при этом не возвращаются вовсе, они расписанием не запускались.',
513
516
  'status=failed у cold_welcome означает и обрыв процесса: прогон не финализировал себя. outcomes — разбивка «почему не ушло».',
514
517
  'В outcomes чинится оператором: SKIPPED_NO_ANCHOR (нет даты эфира), SKIPPED_TEXT_TOO_LONG (текст длиннее лимита после подстановки), SKIPPED_UNRESOLVED_VARS (переменные текста без значения), SKIPPED_BOT_SILENCED (бот выключен у контакта), SKIPPED_NOT_IN_BASE (контакта нет в базе — сначала импорт файла). Отметка приветствия у них не сгорала — после починки повторный запуск их доберёт.',
518
+ 'SKIPPED_RESEND_PAUSE — оператором НЕ чинится: этим сегментом человеку уже писали, после новой регистрации расписание повторяет ему сегмент, но пауза повтора после последней отправки ещё не прошла (чаще всего сегмент успел уйти ему соседним запуском). Бывает только у прогона расписания; человек вернётся в пул сам.',
515
519
  '⚠️⚠️ sentWithoutChannelEvidence — сколько из sentCount ушло ВСЛЕПУЮ: канал взят резервным транспортом площадки, а не доказан привязками контакта или его ответом. Это ПОДМНОЖЕСТВО отправленных, а не отдельный исход: сообщение ушло, повторять его нельзя. Такие контакты с большой вероятностью получили сообщение в личный кабинет сайта, а не в мессенджер, — по outcomes они неотличимы от дошедших. null (а не 0) означает «счётчик не считался»: волна либо запуск до его появления.',
516
520
  '⚠️ SKIPPED_ANCHOR_IN_PAST — ИСКЛЮЧЕНИЕ из строки выше: дата эфира у контакта ЕСТЬ и устарела (эфир перенесли). Повторный запуск даст ровно тот же исход, сколько ни запускай: автоматический добор якоря — set-if-absent и к заполненному значению не возвращается. Лечится ТОЛЬКО явным перечитыванием якорей из запинённой сделки (POST /api/chat-users/getcourse-anchor-refresh), после которого контакт доберётся обычным запуском.',
517
521
  ]),