@besales/mcp 0.19.0 → 0.25.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.
package/README.md CHANGED
@@ -279,7 +279,7 @@ connect each once and pin sessions via `BESALES_WORKSPACE_ID` (or switch the
279
279
  global default with `besales-mcp use <id>`) — see
280
280
  [Multiple Workspaces](#multiple-workspaces).
281
281
 
282
- ## Available tools (128 tools by category)
282
+ ## Available tools (143 tools by category)
283
283
 
284
284
  Counts are derived from `src/schemas/mcp-tools.json` (`summary.tools_by_category`)
285
285
  and asserted in `src/tools/registry.spec.ts`:
@@ -291,7 +291,7 @@ and asserted in `src/tools/registry.spec.ts`:
291
291
  | Prompt generation / analysis / lint / patch sessions | 13 | `besales_prompt_*`, `besales_qa_generation_*` external pairs |
292
292
  | Sandbox / simulation / test-scenario generation | 7 | `besales_sandbox_*`, `besales_simulation_*`, `besales_test_scenario_*` |
293
293
  | Model comparison (bake-off) | 4 | `besales_model_catalog_list` + `besales_model_comparison_run_start` / `_get` / `_eval_submit` (on-demand multi-model prompt comparison) |
294
- | Agent / router / triggers / behavior / facts | 13 | Platform Setup Bridge (incl. `besales_behavior_update`, `besales_pipeline_settings_update`, `besales_platform_settings_update`) + `besales_facts_*` |
294
+ | Agent / router / triggers / behavior / facts | 14 | Platform Setup Bridge (incl. `besales_agent_clone`, `besales_behavior_update`, `besales_pipeline_settings_update`, `besales_platform_settings_update`) + `besales_facts_*` |
295
295
  | Knowledge — spaces | 6 | `besales_knowledge_space_*` |
296
296
  | Knowledge — documents | 4 | incl. `besales_knowledge_document_upload` |
297
297
  | Knowledge — Q&A | 7 | CRUD + semantic search + move |
@@ -344,7 +344,7 @@ Inspect them locally:
344
344
  yarn dlx @modelcontextprotocol/inspector node bin/besales-mcp.js
345
345
  ```
346
346
 
347
- The Inspector should show 128 tools and 23 resources.
347
+ The Inspector should show 143 tools and 30 resources.
348
348
 
349
349
  ## Claude Desktop
350
350
 
@@ -16,7 +16,10 @@
16
16
  > пополнение 2026-06-16 (A34-A38, B40, C8-C9): A34-A37/B40/C8 — из gap-анализа трёх референс-промптов
17
17
  > (Ksamata reactivation, used-equipment Avito-inbound, jewelry inbound), провенанс
18
18
  > `docs/dialog-corpus/deep/candidates-2026-06-16-reference-prompts.md`; A38 (эмодзи) и C9 (few-shot
19
- > на каждом языке агента) — из боевых правок Aslzar-бота.
19
+ > на каждом языке агента) — из боевых правок Aslzar-бота. Пополнение 2026-06-25 (B41, B42, C10) — из
20
+ > сессии Ksamata reactivation (qwen-max ВСЁ РАВНО сливал на 1-м возражении): неплатёжеспособность как
21
+ > отрабатываемое возражение (≠ отказ), лид без боли / из любопытства (≠ тупик), единый close-гейт без
22
+ > конкурирующих exit-скриптов.
20
23
  > Часть пунктов раздела A дополнительно ловится автоматически линтером
21
24
  > (`besales_prompt_lint` / при finalize/commit) — но промпт должен соответствовать им by design,
22
25
  > не полагайся только на линтер.
@@ -638,6 +641,23 @@ A11 (markdown) и A30 (чистота). В реплику клиенту не д
638
641
  - ❌ оплативший «не могу найти курс в кабинете» → агент начинает квалифицировать или диагностировать
639
642
  - ✅ «передаю в отдел заботы, свяжутся и разберутся с доступом» (без диагностики и без «ваш вопрос передан»)
640
643
 
644
+ **B41. «Не могу платить / маленькая пенсия / нет денег сейчас» — НЕ отказ, а самое отрабатываемое возражение.**
645
+ Модель часто путает «не могу заплатить» (решаемо) с «платить не буду / только бесплатное» (отказ) и сразу
646
+ прощается — это слив. На жёсткую неплатёжеспособность не закрывай и не отсылай «досмотреть / подумать»:
647
+ предложи рассрочку, помесячную или совместную с близкими оплату ЛИБО передай человеку, чтобы разобрать
648
+ варианты оплаты. Отказ по деньгам — только принципиальное «платно в принципе не планирую», а не «дорого /
649
+ не потяну сейчас». (Расширяет B8: B8 — про мягкую подачу цены, B41 — про КЛАССИФИКАЦИЮ «нет денег» как НЕ-отказа.)
650
+ - ❌ «Понимаю, ситуация непростая. Рекомендую досмотреть, будут вопросы — пишите.» (слив на «не могу платить»)
651
+ - ✅ «Понимаю. Сумму можно разбить — рассрочка, помесячно, многие оформляют вместе с близкими. Хотите, коллега коротко разберёт удобный вариант?»
652
+
653
+ **B42. Лид без острой боли («здоров / ничего не болит / зашёл из любопытства / для профилактики») — НЕ тупик.**
654
+ Отсутствие жалобы модель читает как «продавать нечего» и сама уходит в прощание — это слив. Любопытство и
655
+ профилактика — валидный интерес: не выходи, зайди новым углом (что показалось новым/полезным; для себя или
656
+ для близких; профилактика как мотив). Не сворачивай реактивацию/квалификацию только потому, что человека
657
+ сейчас ничего не беспокоит. (Расширяет B7: B7 — про «нет/всё хорошо» на квалификацию, B42 — про «нет БОЛИ/нужды».)
658
+ - ❌ «Раз всё в норме — не буду задерживать, будут вопросы пишите, хорошего вечера!» (слив на «здоров/любопытство»)
659
+ - ✅ «Здорово следить за собой ещё до проблем — это и есть профилактика. А что показалось самым полезным? Для себя или, может, кому-то из близких актуально?»
660
+
641
661
  ## Раздел C. Процесс генерации (как писать промпт, а не только что писать)
642
662
 
643
663
  Эти правила — для того, кто СОЗДАЁТ промпт (Claude), а не для самого агента.
@@ -705,6 +725,16 @@ ONCE_STRICT — первый ответ дословный, дальше мяг
705
725
  (`adapt_to_user_language`), продублируй ключевые few-shot (опенер без зачина, без эха, один вопрос,
706
726
  корректный отказ) на КАЖДОМ активном языке — иначе дисциплина держится только в языке примеров.
707
727
 
728
+ **C10. Закрытие диалога — ОДНО авторитетное решение «закрыть vs продолжать», без конкурирующих exit-скриптов
729
+ в промпте.** Главный рецидив сливов: рядом с правилом «не сдавайся» лежит дословный прощальный шаблон (или
730
+ мягкий сигнал ошибочно отнесён к «закрыть»), и модель тянет именно exit. Сделай бинарный гейт ВЫШЕ остальных
731
+ правил раздела: «закрыть» — узкий ЯВНЫЙ список (принципиальный отказ от платного / «не пишите» / N безуспешных
732
+ заходов), всё прочее → «продолжать», по умолчанию «продолжать». Перечисли ПОИМЁННО каждое мягкое «нет» как
733
+ «продолжать» (включая «не сейчас», «присматриваюсь», «не могу платить» → B41, «здоров / из любопытства» → B42).
734
+ И вычисти дубль-exit: ни одного дословного прощального текста рядом с «не сливайся» — модель эхоит его на любом
735
+ пограничном вводе. (Класс багов: ПРОМПТ-ФИКС №1/2/4 Ksamata — «не сдавайся» сосуществовало со встроенным
736
+ прощальным скриптом, и exit перебивал; чинилось только удалением дубль-exit + поимённым перечислением мягких «нет».)
737
+
708
738
  ---
709
739
 
710
740
  ### Что НЕ кладём в промпт по этим принципам
@@ -20,7 +20,7 @@
20
20
 
21
21
  2. **`besales_model_comparison_run_start`** `{ agent_id, models?, dialogue_count? }`
22
22
  — запуск (async). По умолчанию `dialogue_count=5`; `models` опционально (тогда
23
- набор по умолчанию). До **16 моделей** и **20 диалогов**, если оператор просит
23
+ набор по умолчанию). До **18 моделей** и **20 диалогов**, если оператор просит
24
24
  больше. Возвращает `run_id`.
25
25
 
26
26
  3. **`besales_model_comparison_get`** `{ agent_id, run_id }` — поллируй до
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "title": "besales-mcp Tool Definitions",
4
4
  "version": "1.2.0-draft",
5
- "lastUpdated": "2026-06-10",
5
+ "lastUpdated": "2026-06-25",
6
6
  "description": "MCP tool schemas для besales-mcp npm package. Active v1.5 set содержит PromptHub/ICP/Sandbox/Knowledge external-execution operations и stateless Platform Setup Bridge direct tools. Setup Pipeline session tools сохранены как Phase 4+ future design, но не входят в active toolset.",
7
7
  "executionModes": {
8
8
  "summary": "Каждая active LLM-операция имеет 2 MCP tools: get_instructions + submit. Claude локально между tools выполняет LLM-вызовы своими моделями. Auto-detection через JWT scope mcp:* → external default.",
@@ -1038,6 +1038,18 @@
1038
1038
  }
1039
1039
  }
1040
1040
  },
1041
+ {
1042
+ "name": "besales_agent_variable_overrides_get",
1043
+ "description": "Read-only: current variable overrides set on an agent — workspaceOverrides[] (WorkspaceVariable, referenced as {{{name}}}) and customOverrides[] (platform CustomVariable, {name}), each with variable id, name, type, overrideValue. Read this BEFORE besales_agent_update: agent_workspace_variable_overrides / agent_variable_overrides replace the whole set (delete missing + upsert sent), so resend any existing override you want to keep.",
1044
+ "inputSchema": {
1045
+ "type": "object",
1046
+ "required": ["platform_id", "agent_id"],
1047
+ "properties": {
1048
+ "platform_id": { "type": "string", "format": "uuid" },
1049
+ "agent_id": { "type": "string", "format": "uuid" }
1050
+ }
1051
+ }
1052
+ },
1041
1053
  {
1042
1054
  "name": "besales_getcourse_departments_list",
1043
1055
  "description": "List live GetCourse Inbox departments ({ id, title }) for the platform's school. Use department.id as getcourse_settings.target_department_id in besales_platform_settings_update for department-routing. GetCourse platform only.",
@@ -1049,6 +1061,68 @@
1049
1061
  }
1050
1062
  }
1051
1063
  },
1064
+ {
1065
+ "name": "besales_getcourse_wave_import",
1066
+ "description": "Launch a GetCourse outbound wave (Step 1 import contacts + pin deals -> Step 2 send). Default launch_type=status (stamp deal fields -> GetCourse Process sends the welcome). WITHOUT confirm=true it is a DRY-RUN: returns a summary (totalContacts/withDeal/channels/dealFields/sample) and sends nothing. Show the summary, then re-call with confirm=true to actually send. channels are GetCourseTransport values (VK|Telegram|MAX|SMS|Email|Site) for analytics. launch_type=status requires deal_fields = { \"<numeric GetCourse fieldId>\": value }; board requires pipeline_id+status_id; group requires group_id. preflight default enabled=true: row-level pre-send gate blocks contacts with active/paid same-product deals and requires product context (product_title/product_match_key or funnel). Returns waveJobId — poll besales_getcourse_wave_status until COMPLETED. Scope: mcp:platform-setup.",
1067
+ "inputSchema": {
1068
+ "type": "object",
1069
+ "required": ["platform_id", "crm_id", "channels", "contacts"],
1070
+ "properties": {
1071
+ "workspace_id": { "type": "string", "format": "uuid", "description": "Optional override. Normally omit — resolved from the connected session." },
1072
+ "platform_id": { "type": "string", "format": "uuid", "description": "GetCourse PLATFORM uuid (Step 1)." },
1073
+ "crm_id": { "type": "string", "format": "uuid", "description": "GetCourse CRM uuid (Step 2)." },
1074
+ "launch_type": { "type": "string", "enum": ["status", "board", "group"], "description": "Wave launch mode. Default status." },
1075
+ "channels": { "type": "array", "items": { "type": "string" }, "minItems": 1, "description": "Wave channels (GetCourseTransport: VK|Telegram|MAX|SMS|Email|Site) for analytics." },
1076
+ "funnel": { "type": "string", "maxLength": 200, "description": "Wave funnel label (analytics)." },
1077
+ "segment": { "type": "string", "maxLength": 200, "description": "Wave segment label (analytics)." },
1078
+ "deal_fields": { "type": "object", "additionalProperties": { "type": ["string", "number", "boolean", "null"] }, "description": "status mode: { \"<numeric fieldId>\": value } applied to every contact's deal." },
1079
+ "pipeline_id": { "type": "integer", "minimum": 1, "description": "board mode: CrmPipeline.id." },
1080
+ "status_id": { "type": "integer", "minimum": 1, "description": "board mode: CrmPipelineStatus.id." },
1081
+ "group_id": { "type": "integer", "minimum": 1, "description": "group mode: GetCourse group id." },
1082
+ "contacts": {
1083
+ "type": "array",
1084
+ "minItems": 1,
1085
+ "maxItems": 500,
1086
+ "description": "Wave contacts.",
1087
+ "items": {
1088
+ "type": "object",
1089
+ "required": ["contact_id"],
1090
+ "properties": {
1091
+ "contact_id": { "type": "string", "description": "getcourse_user_id (numeric)." },
1092
+ "deal_id": { "type": "string", "description": "Optional order id to pin (registration_deal_id)." },
1093
+ "transport": { "type": "string", "description": "Optional known transport of the contact." },
1094
+ "dialog_id": { "type": "string", "description": "Optional GetCourse dialog id (resp_id) — needed for board-mode welcome." }
1095
+ }
1096
+ }
1097
+ },
1098
+ "confirm": { "type": "boolean", "description": "true = actually run import+send; false/omitted = dry-run (summary only)." },
1099
+ "check_messenger_presence": { "type": "boolean", "description": "Default TRUE. On Step 1 skips contacts with no messenger (Telegram/Max/VK) — they are not imported and not sent (count in contactsSkippedNoMessenger). Adds one API call per contact and launch waits for Step 1 synchronously → for LARGE cohorts (hundreds) set false and run backfill POST /chat-users/import/getcourse-refresh-messengers first." },
1100
+ "preflight": {
1101
+ "type": "object",
1102
+ "description": "Pre-send gate before each Step 2 row. Default enabled=true. Pass product_title/product_match_key or funnel; without any product context backend rejects before creating the job.",
1103
+ "properties": {
1104
+ "enabled": { "type": "boolean", "description": "false disables the row-level preflight gate." },
1105
+ "product_title": { "type": "string", "maxLength": 500, "description": "Human product title used to build the match key." },
1106
+ "product_match_key": { "type": "string", "maxLength": 500, "description": "Pre-normalized product match key." },
1107
+ "offer_id": { "type": "integer", "minimum": 1, "description": "Audit/future-proof only; candidate deals do not expose offer_id through current GetCourse read API." },
1108
+ "other_product_active_deal_mode": { "type": "string", "enum": ["warn", "block", "ignore"], "description": "Default warn. Active deals for another product are report-only unless set to block." }
1109
+ }
1110
+ }
1111
+ }
1112
+ }
1113
+ },
1114
+ {
1115
+ "name": "besales_getcourse_wave_status",
1116
+ "description": "Poll the status of a GetCourse wave (Step 2 job): status (PROCESSING|COMPLETED), processedRows/successCount/failedCount, errorReasons (first per-row failures), and preflight aggregates. Scope: mcp:platform-setup.",
1117
+ "inputSchema": {
1118
+ "type": "object",
1119
+ "required": ["job_id"],
1120
+ "properties": {
1121
+ "workspace_id": { "type": "string", "format": "uuid", "description": "Optional override. Normally omit — resolved from the connected session." },
1122
+ "job_id": { "type": "string", "format": "uuid", "description": "waveJobId returned by besales_getcourse_wave_import." }
1123
+ }
1124
+ }
1125
+ },
1052
1126
  {
1053
1127
  "name": "besales_agent_create",
1054
1128
  "description": "Create a REGULAR AgentChat inside an existing platform. Does not set production prompt content; use PromptHub tools for prompt generation/finalize.",
@@ -1089,6 +1163,20 @@
1089
1163
  }
1090
1164
  }
1091
1165
  },
1166
+ {
1167
+ "name": "besales_agent_clone",
1168
+ "description": "Deep-clone an existing AgentChat into a NEW agent on the SAME platform (mirrors the besales GUI clone button). Copies baseSystemPrompt, knowledge namespaces, model/model_lane/runtime config, tools + builtin tools, agent + workspace variable overrides, ICP links, and ALL triggers + actions (with FK remap to new ids). Does NOT copy routing rules (per-router) or per-platform behavior — the clone is inert until wired into the router. Prompt is identical to source; PromptHub auto-bootstraps a fresh version on the first prompt op. Cross-platform clone is not supported — use besales_platform_clone_* for that.",
1169
+ "inputSchema": {
1170
+ "type": "object",
1171
+ "required": ["platform_id", "source_agent_id", "name"],
1172
+ "properties": {
1173
+ "platform_id": { "type": "string", "format": "uuid", "description": "Платформа-источник; клон приземляется на неё же (cross-platform не поддерживается)." },
1174
+ "source_agent_id": { "type": "string", "format": "uuid", "description": "Исходный AgentChat для глубокого копирования (обязан принадлежать platform_id)." },
1175
+ "name": { "type": "string", "description": "Системное имя нового агента; при коллизии backend добавит суффикс _N." },
1176
+ "display_name": { "type": "string", "description": "Отображаемое имя нового агента; если не задано — зеркалит name." }
1177
+ }
1178
+ }
1179
+ },
1092
1180
  {
1093
1181
  "name": "besales_agent_update",
1094
1182
  "description": "Patch safe AgentChat metadata + typed runtime/output config + knowledge spaces + agent scope for an existing platform agent. Direct baseSystemPrompt edits are rejected; use PromptHub flow for prompt changes.",
@@ -1117,7 +1205,7 @@
1117
1205
  "qa_min_score": { "type": "number", "minimum": 0, "maximum": 1 },
1118
1206
  "qa_context_limit": { "type": "integer", "minimum": 1, "maximum": 10 },
1119
1207
  "max_response_symbols": { "type": "integer", "minimum": 1 },
1120
- "reasoning_effort": { "type": "string", "enum": ["minimal", "low", "medium", "high", "max"] },
1208
+ "reasoning_effort": { "type": "string", "enum": ["none", "minimal", "low", "medium", "high", "xhigh", "max"] },
1121
1209
  "verbosity": { "type": "string", "enum": ["low", "medium", "high"] },
1122
1210
  "enable_thinking": { "type": "boolean" },
1123
1211
  "timezone": { "type": "string" },
@@ -1131,9 +1219,10 @@
1131
1219
  },
1132
1220
  "output_config": {
1133
1221
  "type": "object",
1134
- "description": "v1.5 Increment 3 — UI/output (displayName, button generator). self_analysis НЕ часть — DROPPED в v2 (Phase B, 2026-05-27); используй post_processors (Routing Studio Critic node).",
1222
+ "description": "v1.5 Increment 3 — UI/output (displayName, response_mode, button generator). self_analysis НЕ часть — DROPPED в v2 (Phase B, 2026-05-27); используй post_processors (Routing Studio Critic node).",
1135
1223
  "properties": {
1136
1224
  "display_name": { "type": "string" },
1225
+ "response_mode": { "type": "string", "enum": ["TEXT", "SILENT"], "description": "Режим ответов: TEXT — обычный, SILENT — «тихий» агент (ответы в канал не отправляются, триггеры/действия выполняются)" },
1137
1226
  "button_generator": {
1138
1227
  "type": "object",
1139
1228
  "properties": {
@@ -1229,7 +1318,7 @@
1229
1318
  },
1230
1319
  {
1231
1320
  "name": "besales_routing_rule_upsert",
1232
- "description": "Create or patch one router routing rule. Does not bulk replace the router. Direct agent routing requires explicit_confirmation.",
1321
+ "description": "Create or patch one router routing rule. Does not bulk replace the router. Direct agent routing requires explicit_confirmation. For CUSTOM_VARIABLE pass either conditions[] (N conditions joined by condition_logic ALL=AND / ANY=OR) or the legacy custom_variable_id+variable_value pair (single condition).",
1233
1322
  "inputSchema": {
1234
1323
  "type": "object",
1235
1324
  "required": ["platform_id", "routing_strategy", "custom_router_prompt"],
@@ -1240,6 +1329,19 @@
1240
1329
  "deal_mode": { "type": ["string", "null"] },
1241
1330
  "custom_variable_id": { "type": ["string", "null"], "format": "uuid" },
1242
1331
  "variable_value": { "type": ["string", "null"] },
1332
+ "condition_logic": { "type": "string", "enum": ["ALL", "ANY"] },
1333
+ "conditions": {
1334
+ "type": "array",
1335
+ "maxItems": 50,
1336
+ "items": {
1337
+ "type": "object",
1338
+ "required": ["custom_variable_id", "variable_value"],
1339
+ "properties": {
1340
+ "custom_variable_id": { "type": "string", "format": "uuid" },
1341
+ "variable_value": { "type": "string" }
1342
+ }
1343
+ }
1344
+ },
1243
1345
  "custom_router_prompt": { "type": "string" },
1244
1346
  "is_enabled": { "type": "boolean" },
1245
1347
  "priority": { "type": "integer", "minimum": 0 },
@@ -1527,7 +1629,11 @@
1527
1629
  "send_failed_note_to_crm": { "type": "boolean" },
1528
1630
  "failed_note_template": { "type": ["string", "null"] },
1529
1631
  "send_operator_takeover_note_to_crm": { "type": "boolean" },
1530
- "operator_takeover_note_template": { "type": ["string", "null"] }
1632
+ "operator_takeover_note_template": { "type": ["string", "null"] },
1633
+ "inbound_deal_filter_enabled": { "type": "boolean" },
1634
+ "inbound_deal_filter_type": { "type": "string", "enum": ["NONE", "TAG", "CUSTOM_FIELD"] },
1635
+ "inbound_deal_filter_value": { "type": ["string", "null"] },
1636
+ "inbound_deal_filter_field_id": { "type": ["string", "null"] }
1531
1637
  }
1532
1638
  },
1533
1639
  "widget_marketing": {
@@ -1588,13 +1694,15 @@
1588
1694
  "properties": {
1589
1695
  "account_domain": { "type": ["string", "null"] },
1590
1696
  "employee_user_id": { "type": ["integer", "null"] },
1591
- "available_transports": { "type": "array", "items": { "type": "string", "enum": ["Site", "Email", "SMS", "Telegram", "Facebook", "VK", "Chatium", "WhatsApp", "Viber"] } },
1592
- "default_transport": { "type": ["string", "null"], "enum": ["Site", "Email", "SMS", "Telegram", "Facebook", "VK", "Chatium", "WhatsApp", "Viber", null] },
1697
+ "available_transports": { "type": "array", "items": { "type": "string", "enum": ["Site", "Email", "SMS", "Telegram", "Facebook", "VK", "Chatium", "WhatsApp", "Viber", "MAX"] } },
1698
+ "default_transport": { "type": ["string", "null"], "enum": ["Site", "Email", "SMS", "Telegram", "Facebook", "VK", "Chatium", "WhatsApp", "Viber", "MAX", null] },
1593
1699
  "departments": { "type": ["array", "null"], "items": { "type": "object", "properties": { "id": { "type": "string" }, "label": { "type": "string" } }, "required": ["id", "label"] } },
1594
1700
  "template_user_id": { "type": ["integer", "null"] },
1595
1701
  "template_deal_id": { "type": ["integer", "null"] },
1596
1702
  "field_variable_map": { "type": ["array", "null"], "items": { "type": "object", "properties": { "getcourse_field_name": { "type": "string" }, "variable_name": { "type": "string" } }, "required": ["getcourse_field_name", "variable_name"] } },
1597
- "target_department_id": { "type": ["integer", "null"], "minimum": 1 }
1703
+ "target_department_id": { "type": ["integer", "null"], "minimum": 1 },
1704
+ "delivery_check_enabled": { "type": "boolean", "description": "Nightly wave delivery check (04:00 MSK): cron reads per-transport deal custom fields (=1 → sent); broadcast analytics for the platform counts only confirmed deliveries." },
1705
+ "delivery_field_map": { "type": "object", "description": "Transport → EXACT deal custom field NAME (GetCourse read API returns no field ids), e.g. { \"Telegram\": \"Отправлено ТГ\" }. Clear with {}.", "additionalProperties": { "type": "string" }, "propertyNames": { "enum": ["Site", "Email", "SMS", "Telegram", "Facebook", "VK", "Chatium", "WhatsApp", "Viber", "MAX"] } }
1598
1706
  }
1599
1707
  },
1600
1708
  "explicit_confirmation": { "type": "boolean" },
@@ -3337,14 +3445,14 @@
3337
3445
  },
3338
3446
  {
3339
3447
  "name": "besales_model_comparison_run_start",
3340
- "description": "Запустить прогон сравнения промпта агента на нескольких моделях (один и тот же набор диалогов, меняется только модель агента). ДОРОГО — только по явному запросу оператора, НЕ после каждого фикса. models опционально (по умолчанию ~5 defaultSelected из besales_model_catalog_list), 1..16. dialogue_count по умолчанию 5, до 20. Возвращает run_id для besales_model_comparison_get.",
3448
+ "description": "Запустить прогон сравнения промпта агента на нескольких моделях (один и тот же набор диалогов, меняется только модель агента). ДОРОГО — только по явному запросу оператора, НЕ после каждого фикса. models опционально (по умолчанию ~5 defaultSelected из besales_model_catalog_list), 1..18. dialogue_count по умолчанию 5, до 20. Возвращает run_id для besales_model_comparison_get.",
3341
3449
  "inputSchema": {
3342
3450
  "type": "object",
3343
3451
  "required": ["agent_id"],
3344
3452
  "additionalProperties": false,
3345
3453
  "properties": {
3346
3454
  "agent_id": { "type": "string", "format": "uuid" },
3347
- "models": { "type": "array", "items": { "type": "string" }, "minItems": 1, "maxItems": 16, "description": "Runnable id моделей из каталога (1..16). По умолчанию набор defaultSelected." },
3455
+ "models": { "type": "array", "items": { "type": "string" }, "minItems": 1, "maxItems": 18, "description": "Runnable id моделей из каталога (1..18). По умолчанию набор defaultSelected." },
3348
3456
  "dialogue_count": { "type": "integer", "minimum": 1, "maximum": 20, "description": "Диалогов на модель (по умолчанию 5)." },
3349
3457
  "message_direction": { "type": "string", "enum": ["INBOUND", "OUTBOUND"] }
3350
3458
  }
@@ -3391,6 +3499,100 @@
3391
3499
  }
3392
3500
  }
3393
3501
  }
3502
+ },
3503
+ {
3504
+ "name": "besales_external_api_channel_create",
3505
+ "description": "v1.5: создать канал External API (партнёрский relay, ChannelType.EXTERNAL_API). Ответ { channelId, apiKey, webhookSecret, ingestPath } — apiKey (Bearer для входящих партнёра) и webhookSecret (HMAC-подпись наших колбэков) показываются ОДИН раз. Перевыпуск — rotate-тулы. Scope: mcp:platform-setup.",
3506
+ "inputSchema": {
3507
+ "type": "object",
3508
+ "required": ["callback_url"],
3509
+ "additionalProperties": false,
3510
+ "properties": {
3511
+ "workspace_id": { "type": "string", "format": "uuid", "description": "Optional override. Normally omit — defaults to the workspace bound to this session (BESALES_WORKSPACE_ID or active connection)." },
3512
+ "name": { "type": "string", "minLength": 1, "maxLength": 255, "description": "Опционально: display-имя канала." },
3513
+ "callback_url": { "type": "string", "format": "uri", "description": "URL партнёра (HTTPS), куда POST'им ответы AI + follow-up (HMAC-подписанные колбэки)." },
3514
+ "source_channel": { "type": "string", "minLength": 1, "maxLength": 64, "description": "Опционально: метка транспорта по умолчанию (telegram|whatsapp|web|...)." }
3515
+ }
3516
+ }
3517
+ },
3518
+ {
3519
+ "name": "besales_external_api_channel_rotate_key",
3520
+ "description": "v1.5: перевыпустить bsl-ключ External API канала. Ответ { apiKey } — новый ключ, показывается ОДИН раз; старый ОТОЗВАН немедленно (партнёр должен обновить интеграцию). Scope: mcp:platform-setup.",
3521
+ "inputSchema": {
3522
+ "type": "object",
3523
+ "required": ["channel_id"],
3524
+ "additionalProperties": false,
3525
+ "properties": {
3526
+ "workspace_id": { "type": "string", "format": "uuid", "description": "Optional override. Normally omit — defaults to the workspace bound to this session (BESALES_WORKSPACE_ID or active connection)." },
3527
+ "channel_id": { "type": "string", "format": "uuid" }
3528
+ }
3529
+ }
3530
+ },
3531
+ {
3532
+ "name": "besales_external_api_channel_rotate_secret",
3533
+ "description": "v1.5: перевыпустить webhook-secret External API канала. Ответ { webhookSecret } — новый секрет, показывается ОДИН раз; старый инвалидирован (партнёр должен обновить проверку подписи колбэков). Scope: mcp:platform-setup.",
3534
+ "inputSchema": {
3535
+ "type": "object",
3536
+ "required": ["channel_id"],
3537
+ "additionalProperties": false,
3538
+ "properties": {
3539
+ "workspace_id": { "type": "string", "format": "uuid", "description": "Optional override. Normally omit — defaults to the workspace bound to this session (BESALES_WORKSPACE_ID or active connection)." },
3540
+ "channel_id": { "type": "string", "format": "uuid" }
3541
+ }
3542
+ }
3543
+ },
3544
+ {
3545
+ "name": "besales_external_api_channel_update",
3546
+ "description": "v1.5: обновить несекретные настройки External API канала (callback_url / source_channel). Ключ и секрет НЕ меняются (для них rotate-тулы). Смена callback_url применяется немедленно. Scope: mcp:platform-setup.",
3547
+ "inputSchema": {
3548
+ "type": "object",
3549
+ "required": ["channel_id"],
3550
+ "additionalProperties": false,
3551
+ "properties": {
3552
+ "workspace_id": { "type": "string", "format": "uuid", "description": "Optional override. Normally omit — defaults to the workspace bound to this session (BESALES_WORKSPACE_ID or active connection)." },
3553
+ "channel_id": { "type": "string", "format": "uuid" },
3554
+ "callback_url": { "type": "string", "format": "uri", "description": "Опционально: новый callback URL." },
3555
+ "source_channel": { "type": "string", "minLength": 1, "maxLength": 64, "description": "Опционально: новая метка транспорта." }
3556
+ }
3557
+ }
3558
+ },
3559
+ {
3560
+ "name": "besales_dialogues_query",
3561
+ "description": "Read-only манифест диалогов workspace по фильтрам: каналы, окно дат активности (по сообщениям), CRM лид-статус, причина деактивации, ПОСЛЕДНЕЕ событие platform_work_logs, ИИ/фоллоуапы, наличие сообщений. Пагинация cursor + total («найдено N»). Тексты переписки не отдаёт — по каждому conversationId вызывай besales_dialogue_transcript. MCP файлы не пишет: вызывающий AI сохраняет результат сам. Scope: read:conversations.",
3562
+ "inputSchema": {
3563
+ "type": "object",
3564
+ "required": [],
3565
+ "additionalProperties": false,
3566
+ "properties": {
3567
+ "workspace_id": { "type": "string", "format": "uuid", "description": "Optional override. Normally omit — defaults to the workspace bound to this session." },
3568
+ "platform_ids": { "type": "array", "items": { "type": "string", "format": "uuid" }, "description": "Фильтр по каналам (ChatUser.platformId)." },
3569
+ "date_from": { "type": "string", "format": "date-time", "description": "ISO 8601, нижняя граница активности диалога." },
3570
+ "date_to": { "type": "string", "format": "date-time", "description": "ISO 8601, верхняя граница активности диалога." },
3571
+ "lead_status": { "type": "array", "items": { "type": "string" }, "description": "CRM лид-статусы (ChatUser.leadStatus)." },
3572
+ "deactivate_reason": { "type": "array", "items": { "type": "string" }, "description": "Причины деактивации бота (ChatUser.deactivateReason)." },
3573
+ "work_log_event": { "type": "array", "items": { "type": "string", "enum": ["START", "IN_PROCESS", "FINISH_WIN", "FINISH_LOST", "FAILED", "OPERATOR_TAKEOVER", "FINISH_AUTO"] }, "description": "Фильтр по ПОСЛЕДНЕМУ событию platform_work_logs диалога." },
3574
+ "ai_enabled": { "type": "boolean", "description": "ChatUser.isBotActive." },
3575
+ "followups_enabled": { "type": "boolean", "description": "ChatUser.isFollowUpActive." },
3576
+ "has_messages": { "type": "boolean", "description": "true — только диалоги с сообщениями; false — только без." },
3577
+ "limit": { "type": "integer", "minimum": 1, "maximum": 100, "description": "Размер страницы (дефолт 50)." },
3578
+ "cursor": { "type": "string", "description": "Курсор пагинации из предыдущего ответа." }
3579
+ }
3580
+ }
3581
+ },
3582
+ {
3583
+ "name": "besales_dialogue_transcript",
3584
+ "description": "Read-only полный транскрипт одного диалога (хронологический, роли client/agent) + готовый transcriptMarkdown для сохранения в отдельный файл на диалог. Пагинация сообщений через cursor. conversation_id = conversationId из besales_dialogues_query. Scope: read:conversations.",
3585
+ "inputSchema": {
3586
+ "type": "object",
3587
+ "required": ["conversation_id"],
3588
+ "additionalProperties": false,
3589
+ "properties": {
3590
+ "workspace_id": { "type": "string", "format": "uuid", "description": "Optional override. Normally omit — defaults to the workspace bound to this session." },
3591
+ "conversation_id": { "type": "string", "format": "uuid", "description": "ID диалога (= conversationId из манифеста / ChatUser.id)." },
3592
+ "limit": { "type": "integer", "minimum": 1, "maximum": 100, "description": "Сообщений на страницу (дефолт 50)." },
3593
+ "cursor": { "type": "string", "description": "Курсор пагинации сообщений (хронологический asc)." }
3594
+ }
3595
+ }
3394
3596
  }
3395
3597
  ],
3396
3598
  "resources": [
@@ -3576,8 +3778,9 @@
3576
3778
  }
3577
3779
  ],
3578
3780
  "summary": {
3579
- "total_tools": 130,
3781
+ "total_tools": 143,
3580
3782
  "tools_by_category": {
3783
+ "getcourse_wave": 2,
3581
3784
  "files_upload": 3,
3582
3785
  "platform_provisioning": 3,
3583
3786
  "crm_provisioning": 6,
@@ -3594,7 +3797,7 @@
3594
3797
  "sandbox_findings_external": 2,
3595
3798
  "simulation_findings_external": 2,
3596
3799
  "qa_generation_external": 2,
3597
- "platform_setup_bridge": 13,
3800
+ "platform_setup_bridge": 14,
3598
3801
  "feedback_sheets": 5,
3599
3802
  "facts_management": 2,
3600
3803
  "prompt_lint_standalone": 1,
@@ -3636,6 +3839,7 @@
3636
3839
  "newly_added_in_p3_x_platform_setup_bridge": [
3637
3840
  "besales_platform_context_get",
3638
3841
  "besales_agent_list",
3842
+ "besales_agent_variable_overrides_get",
3639
3843
  "besales_getcourse_departments_list",
3640
3844
  "besales_agent_create",
3641
3845
  "besales_agent_update",
@@ -0,0 +1,19 @@
1
+ export declare const dialogueExportTools: (import("./shared.js").ToolDefinition<{
2
+ workspace_id?: string | undefined;
3
+ platform_ids?: string[] | undefined;
4
+ date_from?: string | undefined;
5
+ date_to?: string | undefined;
6
+ lead_status?: string[] | undefined;
7
+ deactivate_reason?: string[] | undefined;
8
+ work_log_event?: ("FAILED" | "START" | "IN_PROCESS" | "FINISH_WIN" | "FINISH_LOST" | "OPERATOR_TAKEOVER" | "FINISH_AUTO")[] | undefined;
9
+ ai_enabled?: boolean | undefined;
10
+ followups_enabled?: boolean | undefined;
11
+ has_messages?: boolean | undefined;
12
+ limit?: number | undefined;
13
+ cursor?: string | undefined;
14
+ }> | import("./shared.js").ToolDefinition<{
15
+ conversation_id: string;
16
+ workspace_id?: string | undefined;
17
+ limit?: number | undefined;
18
+ cursor?: string | undefined;
19
+ }>)[];
@@ -0,0 +1,129 @@
1
+ // v1.5 — Экспорт диалогов (dialogue-export). Read-only выборка переписок workspace
2
+ // по фильтрам + транскрипт одного диалога. Scope read:conversations (входит в mcp:*).
3
+ // MCP файлы НЕ пишет — данные возвращаются, вызывающий AI раскладывает по файлам.
4
+ import { z } from 'zod';
5
+ import { defineTool, uuidSchema, withInstructions } from './shared.js';
6
+ const workLogEventSchema = z.enum([
7
+ 'START',
8
+ 'IN_PROCESS',
9
+ 'FINISH_WIN',
10
+ 'FINISH_LOST',
11
+ 'FAILED',
12
+ 'OPERATOR_TAKEOVER',
13
+ 'FINISH_AUTO',
14
+ ]);
15
+ export const dialogueExportTools = [
16
+ defineTool({
17
+ name: 'besales_dialogues_query',
18
+ requiredFields: [],
19
+ defaultWorkspaceId: true,
20
+ schema: z
21
+ .object({
22
+ workspace_id: uuidSchema.optional(),
23
+ // Фильтр по каналам (ChatUser.platformId IN ...).
24
+ platform_ids: z.array(uuidSchema).optional(),
25
+ // Окно активности диалога (по сообщениям), ISO 8601.
26
+ date_from: z.string().datetime().optional(),
27
+ date_to: z.string().datetime().optional(),
28
+ // CRM лид-статусы (ChatUser.leadStatus).
29
+ lead_status: z.array(z.string().min(1)).optional(),
30
+ // Причины деактивации бота (ChatUser.deactivateReason).
31
+ deactivate_reason: z.array(z.string().min(1)).optional(),
32
+ // Фильтр по ПОСЛЕДНЕМУ событию platform_work_logs диалога.
33
+ work_log_event: z.array(workLogEventSchema).optional(),
34
+ // ChatUser.isBotActive.
35
+ ai_enabled: z.boolean().optional(),
36
+ // ChatUser.isFollowUpActive.
37
+ followups_enabled: z.boolean().optional(),
38
+ // true — только с сообщениями; false — только без.
39
+ has_messages: z.boolean().optional(),
40
+ limit: z.number().int().min(1).max(100).optional(),
41
+ cursor: z.string().optional(),
42
+ })
43
+ .strict(),
44
+ createRequest: (input) => {
45
+ const params = new URLSearchParams();
46
+ if (input.platform_ids?.length) {
47
+ params.set('platformIds', input.platform_ids.join(','));
48
+ }
49
+ if (input.date_from) {
50
+ params.set('dateFrom', input.date_from);
51
+ }
52
+ if (input.date_to) {
53
+ params.set('dateTo', input.date_to);
54
+ }
55
+ if (input.lead_status?.length) {
56
+ params.set('leadStatus', input.lead_status.join(','));
57
+ }
58
+ if (input.deactivate_reason?.length) {
59
+ params.set('deactivateReason', input.deactivate_reason.join(','));
60
+ }
61
+ if (input.work_log_event?.length) {
62
+ params.set('workLogEvent', input.work_log_event.join(','));
63
+ }
64
+ if (input.ai_enabled !== undefined) {
65
+ params.set('aiEnabled', String(input.ai_enabled));
66
+ }
67
+ if (input.followups_enabled !== undefined) {
68
+ params.set('followUpsEnabled', String(input.followups_enabled));
69
+ }
70
+ if (input.has_messages !== undefined) {
71
+ params.set('hasMessages', String(input.has_messages));
72
+ }
73
+ if (input.limit !== undefined) {
74
+ params.set('limit', String(input.limit));
75
+ }
76
+ if (input.cursor) {
77
+ params.set('cursor', input.cursor);
78
+ }
79
+ const query = params.toString();
80
+ return {
81
+ method: 'GET',
82
+ path: `/workspaces/${input.workspace_id}/dialogue-export${query ? `?${query}` : ''}`,
83
+ };
84
+ },
85
+ enhanceResponse: (response) => withInstructions(response, [
86
+ 'Манифест подходящих диалогов: { data: DialogueExportItem[], total, cursor }. total — «найдено N диалогов».',
87
+ 'Пагинация: если cursor не null — повтори тот же вызов с этим cursor, пока не соберёшь все диалоги.',
88
+ 'Тексты переписки здесь НЕ отдаются — по каждому conversationId вызывай besales_dialogue_transcript.',
89
+ 'MCP файлы не пишет: сохрани результаты локально сам (по файлу на диалог).',
90
+ ]),
91
+ }),
92
+ defineTool({
93
+ name: 'besales_dialogue_transcript',
94
+ requiredFields: ['conversation_id'],
95
+ // workspace_id только выбирает ключ нужного воркспейса (симметрия с _query на
96
+ // мульти-воркспейс хостах); в path не идёт — backend скоупит по воркспейсу ключа.
97
+ defaultWorkspaceId: true,
98
+ schema: z
99
+ .object({
100
+ workspace_id: uuidSchema.optional(),
101
+ // ID диалога = conversationId из манифеста (ChatUser.id).
102
+ conversation_id: uuidSchema,
103
+ limit: z.number().int().min(1).max(100).optional(),
104
+ cursor: z.string().optional(),
105
+ })
106
+ .strict(),
107
+ createRequest: (input) => {
108
+ const params = new URLSearchParams();
109
+ if (input.limit !== undefined) {
110
+ params.set('limit', String(input.limit));
111
+ }
112
+ if (input.cursor) {
113
+ params.set('cursor', input.cursor);
114
+ }
115
+ const query = params.toString();
116
+ return {
117
+ method: 'GET',
118
+ path: `/dialogue-export/${input.conversation_id}/transcript${query ? `?${query}` : ''}`,
119
+ };
120
+ },
121
+ enhanceResponse: (response) => withInstructions(response, [
122
+ 'Транскрипт диалога: { conversationId, channelType, contactName, turns[], transcriptMarkdown, cursor }.',
123
+ 'Сохрани transcriptMarkdown в ОТДЕЛЬНЫЙ файл на диалог, напр. dialogs/<conversationId>.md.',
124
+ 'Роли реплик: client (клиент) / agent (isAi=true → AI-агент, false → оператор/историческое).',
125
+ 'Если cursor не null — у диалога есть ещё сообщения: повтори с этим cursor. Первая страница содержит заголовок; последующие — только реплики: дописывай их в тот же файл.',
126
+ ]),
127
+ }),
128
+ ];
129
+ //# sourceMappingURL=dialogue-export.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialogue-export.js","sourceRoot":"","sources":["../../../src/tools/definitions/dialogue-export.ts"],"names":[],"mappings":"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"}