@besales/mcp 0.1.0 → 0.11.1

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 (94) hide show
  1. package/README.md +272 -17
  2. package/dist/auth/connection-store.d.ts +58 -0
  3. package/dist/auth/connection-store.js +208 -0
  4. package/dist/auth/connection-store.js.map +1 -0
  5. package/dist/auth/oauth-client.d.ts +27 -2
  6. package/dist/auth/oauth-client.js +62 -11
  7. package/dist/auth/oauth-client.js.map +1 -1
  8. package/dist/auth/session-workspace.d.ts +2 -0
  9. package/dist/auth/session-workspace.js +20 -0
  10. package/dist/auth/session-workspace.js.map +1 -0
  11. package/dist/auth/token-storage.d.ts +19 -5
  12. package/dist/auth/token-storage.js +11 -6
  13. package/dist/auth/token-storage.js.map +1 -1
  14. package/dist/cli.d.ts +2 -7
  15. package/dist/cli.js +111 -33
  16. package/dist/cli.js.map +1 -1
  17. package/dist/http/api-client.d.ts +4 -13
  18. package/dist/http/api-client.js +18 -18
  19. package/dist/http/api-client.js.map +1 -1
  20. package/dist/index.d.ts +8 -6
  21. package/dist/index.js +3 -2
  22. package/dist/index.js.map +1 -1
  23. package/dist/instructions/server-instructions.d.ts +15 -0
  24. package/dist/instructions/server-instructions.js +243 -0
  25. package/dist/instructions/server-instructions.js.map +1 -0
  26. package/dist/package-metadata.js +7 -1
  27. package/dist/package-metadata.js.map +1 -1
  28. package/dist/resources/concepts/feedback-sheets.md +77 -0
  29. package/dist/resources/concepts/sandbox.md +13 -0
  30. package/dist/resources/concepts/workbook-classification.md +241 -0
  31. package/dist/resources/docs/agent-behavior.md +393 -0
  32. package/dist/resources/docs/crm-integration.md +535 -0
  33. package/dist/resources/docs/files-and-uploads.md +295 -0
  34. package/dist/resources/docs/knowledge-base.md +521 -0
  35. package/dist/resources/docs/pipeline-builder.md +221 -0
  36. package/dist/resources/docs/pipeline-settings-deep.md +221 -0
  37. package/dist/resources/docs/platforms.md +513 -0
  38. package/dist/resources/docs/prompt-anatomy.md +298 -0
  39. package/dist/resources/docs/prompt-principles.md +289 -0
  40. package/dist/resources/registry.js +34 -12
  41. package/dist/resources/registry.js.map +1 -1
  42. package/dist/resources/workflows/compare-models.md +46 -0
  43. package/dist/resources/workflows/connect-crm-from-scratch.md +89 -0
  44. package/dist/resources/workflows/connect-datasource-from-scratch.md +92 -0
  45. package/dist/resources/workflows/extract-from-document.md +36 -0
  46. package/dist/resources/workflows/iterate-with-sandbox.md +31 -0
  47. package/dist/resources/workflows/platform-setup-from-scratch.md +113 -0
  48. package/dist/resources/workflows/production-readiness-check.md +41 -0
  49. package/dist/schemas/mcp-tools.json +2636 -182
  50. package/dist/server.js +2 -0
  51. package/dist/server.js.map +1 -1
  52. package/dist/tools/definitions/agent-design.d.ts +215 -0
  53. package/dist/tools/definitions/agent-design.js +643 -0
  54. package/dist/tools/definitions/agent-design.js.map +1 -0
  55. package/dist/tools/definitions/crm-platform.d.ts +211 -0
  56. package/dist/tools/definitions/crm-platform.js +1070 -0
  57. package/dist/tools/definitions/crm-platform.js.map +1 -0
  58. package/dist/tools/definitions/datasource.d.ts +40 -0
  59. package/dist/tools/definitions/datasource.js +196 -0
  60. package/dist/tools/definitions/datasource.js.map +1 -0
  61. package/dist/tools/definitions/knowledge.d.ts +215 -0
  62. package/dist/tools/definitions/knowledge.js +782 -0
  63. package/dist/tools/definitions/knowledge.js.map +1 -0
  64. package/dist/tools/definitions/model-comparison.d.ts +25 -0
  65. package/dist/tools/definitions/model-comparison.js +101 -0
  66. package/dist/tools/definitions/model-comparison.js.map +1 -0
  67. package/dist/tools/definitions/platform-setup.d.ts +412 -0
  68. package/dist/tools/definitions/platform-setup.js +738 -0
  69. package/dist/tools/definitions/platform-setup.js.map +1 -0
  70. package/dist/tools/definitions/session.d.ts +11 -0
  71. package/dist/tools/definitions/session.js +86 -0
  72. package/dist/tools/definitions/session.js.map +1 -0
  73. package/dist/tools/definitions/shared.d.ts +742 -0
  74. package/dist/tools/definitions/shared.js +773 -0
  75. package/dist/tools/definitions/shared.js.map +1 -0
  76. package/dist/tools/definitions.d.ts +873 -88
  77. package/dist/tools/definitions.js +14 -856
  78. package/dist/tools/definitions.js.map +1 -1
  79. package/dist/tools/registry.d.ts +3 -1
  80. package/dist/tools/registry.js +90 -11
  81. package/dist/tools/registry.js.map +1 -1
  82. package/dist/tools/result.d.ts +1 -1
  83. package/dist/tools/result.js +12 -4
  84. package/dist/tools/result.js.map +1 -1
  85. package/dist/utils/logger.js +2 -1
  86. package/dist/utils/logger.js.map +1 -1
  87. package/docs/host-setup.md +34 -15
  88. package/package.json +2 -2
  89. package/scripts/install-claude-desktop.js +89 -11
  90. package/scripts/mock-api-server.js +1 -1
  91. package/scripts/mock-credentials.js +49 -6
  92. package/dist/types/api-contract.gen.d.ts +0 -6975
  93. package/dist/types/api-contract.gen.js +0 -6
  94. package/dist/types/api-contract.gen.js.map +0 -1
@@ -0,0 +1,221 @@
1
+ # 14. Pipeline Builder и PipelineSettings
2
+
3
+ > **Источник правды:** `ai-aniomaly/docs/ai-prompt-builder/14-pipeline-builder.md` — копия для MCP doc-bridge (v1.5 Increment 6b). Синкать при изменении оригинала.
4
+ >
5
+ > ⚠️ **MCP-контекст:** это доменная справка, написанная для людей/админ-UI. В MCP-сессии ты выполняешь те же операции **через besales_* tools**, а не через UI (drag-n-drop, клики в админке). Где текст ниже говорит «клиент/админ делает X через UI» — это «что должно получиться»; механику бери из tool (напр. загрузка файла → `besales_file_upload_request`; документ в knowledge → `besales_knowledge_document_upload`; сайт → `besales_knowledge_website_add`) + из workflow-ресурсов. Внутренние ссылки `[NN-name.md]` — относительные пути исходной доки, через MCP НЕ резолвятся: ищи `besales://docs/<slug>` или concept-resource.
6
+
7
+ ## 1. Назначение
8
+
9
+ Когда платформа подключена к CRM, нужно решить две задачи:
10
+
11
+ 1. **Что делать, если в CRM уже есть сделка/контакт с тем же телефоном** (повторное обращение клиента) — `PipelineSettings.filter*`.
12
+ 2. **Куда автоматически переводить сделку** при определённых событиях: пришло сообщение от пользователя, ответил бот, ответил оператор — `PipelineSettings.*MoveToStatus`.
13
+
14
+ Обе задачи настраиваются в Pipeline Builder UI (одна страница админки). Структура — модель `PipelineSettings` (one-to-one с `Platform`).
15
+
16
+ ## 2. JSON-схема
17
+
18
+ ```json
19
+ {
20
+ "id": "uuid",
21
+ "platformId": "<platformId>",
22
+
23
+ "filterEnabled": true,
24
+ "filterByPhoneEnabled": true,
25
+ "filterByFieldEnabled": false,
26
+ "filterByContactEnabled": false,
27
+
28
+ "fieldId": null,
29
+ "fieldEntityType": null,
30
+ "valueType": null,
31
+ "value": null,
32
+
33
+ "actionOnFound": "LINK",
34
+
35
+ "createNewLead": true,
36
+ "createNewContact": true,
37
+
38
+ "incomingMoveToStatus": 142,
39
+ "incomingMoveToStatusPipelineId": 1234567,
40
+ "incomingMoveToStatusCrmId": "<Crm.id>",
41
+
42
+ "outgoingMoveToStatus": 143,
43
+ "outgoingMoveToStatusPipelineId": 1234567,
44
+ "outgoingMoveToStatusCrmId": "<Crm.id>",
45
+
46
+ "aiResponseMoveToStatus": null,
47
+ "aiResponseMoveToStatusPipelineId": null,
48
+ "aiResponseMoveToStatusCrmId": null,
49
+
50
+ "operatorSpeechMoveToStatus": 145,
51
+ "operatorSpeechMoveToStatusPipelineId": 1234567,
52
+ "operatorSpeechMoveToStatusCrmId": "<Crm.id>"
53
+ }
54
+ ```
55
+
56
+ ## 3. Фильтрация существующих сделок
57
+
58
+ ### Поля
59
+
60
+ | Поле | Тип | Описание |
61
+ |---|---|---|
62
+ | `filterEnabled` | bool | Главный выключатель — без него фильтр не работает. |
63
+ | `filterByPhoneEnabled` | bool | Искать существующую сделку/контакт по телефону клиента. |
64
+ | `filterByFieldEnabled` | bool | Искать по кастомному CRM-полю. |
65
+ | `filterByContactEnabled` | bool | Полнотекстовый поиск через CRM-контакт. |
66
+ | `fieldId` | string / null | ID кастомного поля CRM (для `filterByFieldEnabled`). |
67
+ | `fieldEntityType` | string / null | `LEAD` или `CONTACT` — где искать поле. |
68
+ | `valueType` | enum `PipelineFilterValueType` / null | `CONSTANT` (фиксированное значение) или `SYSTEM_VARIABLE` (имя системной переменной — `phone`, `telegramId` и т.п.). |
69
+ | `value` | string / null | Само значение или имя системной переменной. |
70
+ | `actionOnFound` | enum `PipelineFilterAction` / null | `IGNORE` (игнорировать обращение, `isBotActive=false`) или `LINK` (привязать к существующей сделке и продолжить). |
71
+
72
+ ### Сценарии
73
+
74
+ | Сценарий бизнеса | Настройки |
75
+ |---|---|
76
+ | «Если клиент уже у нас в работе — не дублировать сделку, а продолжить в существующей» | `filterEnabled=true`, `filterByPhoneEnabled=true`, `actionOnFound=LINK` |
77
+ | «Если есть сделка — не отвечать ботом, отдать оператору» | `filterEnabled=true`, `filterByPhoneEnabled=true`, `actionOnFound=IGNORE` |
78
+ | «Искать по нашему UTM-полю, чтобы не дублировать с лендингов» | `filterEnabled=true`, `filterByFieldEnabled=true`, `fieldId=<UTM_FIELD_ID>`, `fieldEntityType=LEAD`, `valueType=CONSTANT`, `value="lp_main_2026"`, `actionOnFound=LINK` |
79
+
80
+ ### Создание новых сущностей
81
+
82
+ | Поле | Описание |
83
+ |---|---|
84
+ | `createNewLead` | Создавать ли сделку в CRM при первом обращении клиента, если фильтр не нашёл существующую (или фильтр выключен). |
85
+ | `createNewContact` | Аналогично для контакта. |
86
+
87
+ Обычно оба `true` — иначе сделки/контакты в CRM не появятся.
88
+
89
+ ## 4. Status movement: автоматические переходы между этапами
90
+
91
+ Каждое событие в системе может автоматически двигать сделку. Настройка задаёт композитный ключ `(crmId, pipelineId, statusId)` — статус **может быть в любой воронке** активной для платформы (см. `PlatformPipeline`).
92
+
93
+ ### Все события
94
+
95
+ | Поле | Когда срабатывает | Куда уйдёт сделка |
96
+ |---|---|---|
97
+ | `incomingMoveToStatus` | Пользователь написал в бот (входящее сообщение) | На указанный статус (обычно «Новый» или «В работе») |
98
+ | `outgoingMoveToStatus` | Бот/оператор ответил пользователю | На указанный статус (обычно «Идёт диалог») |
99
+ | `aiResponseMoveToStatus` | Бот сгенерировал и отправил ответ через LLM | На указанный статус (опционально — если хочется отделить «AI ответил» от «оператор ответил») |
100
+ | `operatorSpeechMoveToStatus` | Оператор написал в карточку (через CRM Chat API) | На указанный статус (обычно «У оператора») |
101
+
102
+ Каждое поле — это **тройка**: `*MoveToStatus`, `*MoveToStatusPipelineId`, `*MoveToStatusCrmId`. Если основное поле `null` — переход не выполняется.
103
+
104
+ ### Связь со `StatusType`
105
+
106
+ В `CrmPipelineStatus.statusTypes[]` (массив enum `StatusType`) можно указать «семантику» этапа: `INCOMING`, `OUTGOING`, `MESSAGE_SENT`, `AI_TOOK_OVER`, `OPERATOR_TOOK_OVER`, `USER_BLOCKED`. Эти теги — для UI и аналитики (показывают, какие этапы что обрабатывают), но **не управляют автопереходами** — это делает `PipelineSettings.*MoveToStatus`.
107
+
108
+ ### `CrmPipelineStatus.onMessageReceivedMoveToStatus`
109
+
110
+ Альтернативный механизм автоперехода — на уровне самого статуса: `null` или ID статуса, в который перейти при получении сообщения от пользователя. Может конфликтовать с `PipelineSettings.incomingMoveToStatus` — приоритет за `PipelineSettings`.
111
+
112
+ ### `CrmPipelineStatus.isBotActive` и `isOperatorAvailable`
113
+
114
+ Не часть Pipeline Builder, но влияют на работу:
115
+ - `isBotActive=false` → бот молчит на этом этапе.
116
+ - `isOperatorAvailable=false` → оператор не может быть подключён через `CALL_OPERATOR`.
117
+
118
+ ## 5. PlatformPipeline — связь платформы с воронками
119
+
120
+ Платформа может работать с **несколькими воронками** одной CRM (например, одна для лидов, другая для текущих клиентов):
121
+
122
+ ```json
123
+ {
124
+ "platformId": "<platformId>",
125
+ "pipelineCrmId": "<Crm.id>",
126
+ "pipelineId": 1234567,
127
+ "isActive": true
128
+ }
129
+ ```
130
+
131
+ Это many-to-many связь. PipelineSettings может ссылаться на статусы из любой активной воронки.
132
+
133
+ ## 6. Inline-пример: типичный e-commerce setup
134
+
135
+ ```json
136
+ {
137
+ "platformId": "<platformId>",
138
+
139
+ "filterEnabled": true,
140
+ "filterByPhoneEnabled": true,
141
+ "filterByFieldEnabled": false,
142
+ "filterByContactEnabled": false,
143
+ "actionOnFound": "LINK",
144
+
145
+ "createNewLead": true,
146
+ "createNewContact": true,
147
+
148
+ "incomingMoveToStatus": 142,
149
+ "incomingMoveToStatusPipelineId": 1234567,
150
+ "incomingMoveToStatusCrmId": "<Crm.id>",
151
+
152
+ "outgoingMoveToStatus": 143,
153
+ "outgoingMoveToStatusPipelineId": 1234567,
154
+ "outgoingMoveToStatusCrmId": "<Crm.id>",
155
+
156
+ "operatorSpeechMoveToStatus": 145,
157
+ "operatorSpeechMoveToStatusPipelineId": 1234567,
158
+ "operatorSpeechMoveToStatusCrmId": "<Crm.id>"
159
+ }
160
+ ```
161
+
162
+ Логика:
163
+ - При первом сообщении — ищем по телефону, если уже есть — привязываем; если нет — создаём новую сделку в этапе 142.
164
+ - При первом ответе — переводим в 143 («Идёт диалог»).
165
+ - Когда оператор подключился — 145 («У оператора»).
166
+
167
+ ## 7. Inline-пример: фильтр по UTM (B2B-лендинг)
168
+
169
+ Для разных лендингов — разные воронки:
170
+
171
+ ```json
172
+ {
173
+ "filterEnabled": true,
174
+ "filterByFieldEnabled": true,
175
+ "fieldId": "987",
176
+ "fieldEntityType": "LEAD",
177
+ "valueType": "SYSTEM_VARIABLE",
178
+ "value": "telegramId",
179
+ "actionOnFound": "LINK",
180
+ "createNewLead": true,
181
+ "createNewContact": true
182
+ }
183
+ ```
184
+
185
+ Логика: ищем сделку по полю с ID `987` (например, «Telegram User ID» в LEAD), значение которого совпадает с `{telegramId}` текущего пользователя. Если найдено — привязываем; иначе создаём новую.
186
+
187
+ ## 8. Граничные случаи и валидация
188
+
189
+ | Правило | Что AI-сервису помнить |
190
+ |---|---|
191
+ | `PipelineSettings` — one-to-one с Platform | Для каждой платформы максимум одна запись. |
192
+ | `filterEnabled=true` без `filterBy*` | Бесполезно — система не знает, по чему искать. |
193
+ | `filterByPhoneEnabled=true` для платформ без телефона (Web Widget без UTM) | Может не найти, но бот всё равно сработает (фильтр пропустится). |
194
+ | `actionOnFound=IGNORE` | `ChatUser.isBotActive=false` — бот не отвечает. Подходит для «после ручного открытия сделки оператор сам отвечает». |
195
+ | `actionOnFound=LINK` | `ChatUser` привязывается к существующей `leadId/bitrixDealId`. Хорошо для непрерывности. |
196
+ | `*MoveToStatus` без всех трёх ID | Если нет `*MoveToStatusPipelineId`/`*MoveToStatusCrmId` — переход не сработает. |
197
+ | Конфликт `aiResponseMoveToStatus` и `outgoingMoveToStatus` | Оба сработают по очереди (сначала AI, потом outgoing). Часто хватает только `outgoing`, чтобы не перегружать. |
198
+ | Composite key статуса | Все три поля (`MoveToStatus`, `MoveToStatusPipelineId`, `MoveToStatusCrmId`) или ни одного. |
199
+ | Удаление воронки/статуса | Если ID удалится в CRM — переход просто не выполнится (без ошибки). Регулярно проверять синхронизацию. |
200
+ | Pipeline Builder не управляет триггерами | Это два разных механизма перевода. Триггер `CRM_MOVE_STAGE` — точечный, по условию. PipelineSettings — автоматически по событию. |
201
+
202
+ ## 9. Что AI-сервис должен спрашивать у клиента
203
+
204
+ Перед генерацией PipelineSettings:
205
+
206
+ 1. «Если клиент уже есть в CRM — продолжить в существующей сделке или создать новую?» (`actionOnFound`).
207
+ 2. «Куда переводить сделку, когда клиент написал?» (`incomingMoveToStatus`).
208
+ 3. «Куда переводить, когда вы ответили?» (`outgoingMoveToStatus`).
209
+ 4. «Куда переводить, когда подключился оператор?» (`operatorSpeechMoveToStatus`).
210
+ 5. «Создавать ли новые сделки/контакты автоматически?» (`createNewLead`/`createNewContact`).
211
+ 6. «По какому ключу искать дубликаты — телефон, кастомное поле, что-то ещё?» (`filterBy*`).
212
+
213
+ ## 10. Связь с другими подсистемами
214
+
215
+ | Подсистема | Связь |
216
+ |---|---|
217
+ | **Платформы** ([`11-platforms.md`](11-platforms.md)) | `PipelineSettings` принадлежит платформе (one-to-one). |
218
+ | **CRM** ([`12-crm-integration.md`](12-crm-integration.md)) | Использует `CrmPipelineStatus` (composite key). `StatusType` теги влияют на UI/аналитику. |
219
+ | **Триггеры** ([`04-triggers-and-actions.md`](04-triggers-and-actions.md)) | Триггер `CRM_MOVE_STAGE` — независимый механизм перевода (точечный, по условию). Совместимы — могут работать параллельно. |
220
+ | **Маршрутизация** ([`06-routing.md`](06-routing.md)) | `LEAD_STATUS` стратегия router-а опирается на `ChatUser.leadStatusId`, который обновляется автоматически после переходов через PipelineSettings. |
221
+ | **AgentBehavior** ([`10-agent-behavior.md`](10-agent-behavior.md)) | `aiErrorTargetStageId` — куда перевести при ошибке LLM (читает напрямую `pipelineId` текущей воронки, если `null`). |
@@ -0,0 +1,221 @@
1
+ # 24. Pipeline Settings — глубокая конфигурация
2
+
3
+ > **Источник правды:** `ai-aniomaly/docs/ai-prompt-builder/24-pipeline-settings-deep.md` — копия для MCP doc-bridge (v1.5 Increment 6b). Синкать при изменении оригинала.
4
+ >
5
+ > ⚠️ **MCP-контекст:** это доменная справка, написанная для людей/админ-UI. В MCP-сессии ты выполняешь те же операции **через besales_* tools**, а не через UI (drag-n-drop, клики в админке). Где текст ниже говорит «клиент/админ делает X через UI» — это «что должно получиться»; механику бери из tool (напр. загрузка файла → `besales_file_upload_request`; документ в knowledge → `besales_knowledge_document_upload`; сайт → `besales_knowledge_website_add`) + из workflow-ресурсов. Внутренние ссылки `[NN-name.md]` — относительные пути исходной доки, через MCP НЕ резолвятся: ищи `besales://docs/<slug>` или concept-resource.
6
+
7
+ ## 1. Назначение
8
+
9
+ `PipelineSettings` — расширение [14-pipeline-builder.md](14-pipeline-builder.md). Настройки 1:1 с `Platform`, отвечают за две группы поведения:
10
+
11
+ 1. **Фильтр поиска существующих сделок** — когда новый клиент пишет, нужно ли искать его уже-имеющуюся сделку и как с ней поступить.
12
+ 2. **Автопереход статусов** — 6 разных триггеров системного перемещения сделки по воронке.
13
+
14
+ Базовый файл [14-pipeline-builder.md](14-pipeline-builder.md) описывал часть полей; здесь — **полная JSON-схема** со всеми 30+ полями.
15
+
16
+ ## 2. Полная JSON-схема `PipelineSettings`
17
+
18
+ ```json
19
+ {
20
+ "id": "uuid",
21
+ "platformId": "<Platform.id>",
22
+
23
+ // === FILTER SEARCH (поиск существующих сделок) ===
24
+ "filterEnabled": true,
25
+ "filterByPhoneEnabled": true,
26
+ "filterByFieldEnabled": false,
27
+ "filterByContactEnabled": false,
28
+ "fieldId": null,
29
+ "fieldEntityType": null,
30
+ "valueType": null,
31
+ "value": null,
32
+ "actionOnFound": "LINK",
33
+
34
+ // === CREATION FLAGS ===
35
+ "createNewLead": true,
36
+ "createNewContact": true,
37
+
38
+ // === STATUS MOVEMENT (6 триггеров) ===
39
+ "aiResponseMoveToStatus": 142,
40
+ "aiResponseMoveToStatusPipelineId": 1234567,
41
+ "aiResponseMoveToStatusCrmId": "<Crm.id>",
42
+
43
+ "operatorSpeechMoveToStatus": 145,
44
+ "operatorSpeechMoveToStatusPipelineId": 1234567,
45
+ "operatorSpeechMoveToStatusCrmId": "<Crm.id>",
46
+
47
+ "incomingMoveToStatus": 140,
48
+ "incomingMoveToStatusPipelineId": 1234567,
49
+ "incomingMoveToStatusCrmId": "<Crm.id>",
50
+
51
+ "outgoingMoveToStatus": 141,
52
+ "outgoingMoveToStatusPipelineId": 1234567,
53
+ "outgoingMoveToStatusCrmId": "<Crm.id>",
54
+
55
+ "messageSentMoveToStatus": null,
56
+ "messageSentMoveToStatusPipelineId": null,
57
+ "messageSentMoveToStatusCrmId": null,
58
+
59
+ "userBlockedMoveToStatus": 199,
60
+ "userBlockedMoveToStatusPipelineId": 1234567,
61
+ "userBlockedMoveToStatusCrmId": "<Crm.id>",
62
+
63
+ // === GLOBAL ===
64
+ "alwaysMoveToAiStatus": false,
65
+ "preventBackwardMovement": true
66
+ }
67
+ ```
68
+
69
+ ## 3. Группа: Фильтр поиска существующих сделок
70
+
71
+ ### 3.1. Master switch
72
+
73
+ `filterEnabled: true` — без этого ни один из под-флагов не работает.
74
+
75
+ ### 3.2. Три режима поиска (взаимоисключающие, AI-сервис выбирает один)
76
+
77
+ | Флаг | Поведение | Когда использовать |
78
+ |---|---|---|
79
+ | `filterByPhoneEnabled` | Искать сделку по полю `phone` контакта **И** по кастомным полям сделки, в которых есть телефон | По умолчанию для всех платформ с телефоном (Wazzup, Avito, SMS) |
80
+ | `filterByFieldEnabled` | Искать по конкретному кастомному полю CRM | Когда у клиента CRM-поле «UTM» или «email», и нужно дедуплицировать по нему |
81
+ | `filterByContactEnabled` | Полнотекстовый поиск через **контакт** CRM (AmoCRM Search Contact API) | Когда у клиента нет фиксированных полей, но нужен fuzzy-матч |
82
+
83
+ ### 3.3. Параметры для `filterByFieldEnabled`
84
+
85
+ ```json
86
+ {
87
+ "filterByFieldEnabled": true,
88
+ "fieldId": "12345",
89
+ "fieldEntityType": "CONTACT",
90
+ "valueType": "SYSTEM_VARIABLE",
91
+ "value": "phone"
92
+ }
93
+ ```
94
+
95
+ | Поле | Тип | Описание |
96
+ |---|---|---|
97
+ | `fieldId` | string | ID кастомного поля в CRM (числовой ID) |
98
+ | `fieldEntityType` | `'LEAD'` / `'CONTACT'` | Где искать поле — у сделки или у контакта |
99
+ | `valueType` | enum `PipelineFilterValueType` | `CONSTANT` (искать по фиксированной строке) / `SYSTEM_VARIABLE` (брать значение из системной переменной типа `{phone}`) |
100
+ | `value` | string | Либо константа, либо имя переменной (`phone`, `telegramId`, etc.) |
101
+
102
+ ### 3.4. Действие при нахождении сделки
103
+
104
+ `actionOnFound` — enum `PipelineFilterAction`:
105
+
106
+ | Значение | Поведение |
107
+ |---|---|
108
+ | `LINK` | Привязать `ChatUser` к найденной сделке. Бот будет общаться с этой сделкой |
109
+ | `IGNORE` | Игнорировать существующую сделку, создать новую (если `createNewLead=true`) |
110
+
111
+ ### 3.5. Флаги создания
112
+
113
+ | Флаг | Поведение | Default |
114
+ |---|---|---|
115
+ | `createNewLead` | Создать новую сделку, если ничего не найдено или `actionOnFound=IGNORE` | true |
116
+ | `createNewContact` | Создать новый контакт, если ничего не найдено | true |
117
+
118
+ > Если `createNewLead=false` и сделка не найдена → бот будет общаться без CRM-сделки. CRM-actions триггеров (`CRM_MOVE_STAGE`) silently не сработают.
119
+
120
+ ## 4. Группа: Автопереход статусов
121
+
122
+ `PipelineSettings` определяет **6 триггеров** автоматического перевода сделки по воронке. Каждый триггер — composite-FK на `CrmPipelineStatus` (3 поля: `<X>MoveToStatus`, `<X>MoveToStatusPipelineId`, `<X>MoveToStatusCrmId`).
123
+
124
+ | Триггер | Когда срабатывает | Назначение |
125
+ |---|---|---|
126
+ | `aiResponseMoveToStatus` | После каждого ответа AI боту | Маркировка «бот говорил с клиентом» |
127
+ | `operatorSpeechMoveToStatus` | После каждого сообщения от оператора-человека | Маркировка «оператор уже подключился» |
128
+ | `incomingMoveToStatus` | При первом входящем сообщении от пользователя | Маркировка «появился новый запрос» |
129
+ | `outgoingMoveToStatus` | При первом исходящем сообщении (например, follow-up) | Маркировка «бот написал первым» |
130
+ | `messageSentMoveToStatus` | После любого исходящего сообщения (повторяется) | Аналитика «сколько касаний с клиентом» |
131
+ | `userBlockedMoveToStatus` | Когда пользователь блокирует бота / пишет «STOP» | Архив для очистки воронки |
132
+
133
+ ### 4.1. Composite-FK
134
+
135
+ В CRM статус идентифицируется тройкой: `(crmId, pipelineId, statusId)`. AI-сервис всегда заполняет все три поля или все три оставляет null. Заполнить только `<X>MoveToStatus` без `<X>MoveToStatusPipelineId` нельзя — runtime не поймёт, в какую воронку.
136
+
137
+ ### 4.2. Конфликты и приоритеты
138
+
139
+ | Конфликт | Поведение |
140
+ |---|---|
141
+ | Триггер `CRM_MOVE_STAGE` сработал ПЕРЕД ответом ИИ | После ответа ИИ применится `aiResponseMoveToStatus` (если задан) — может перезаписать только что выставленный статус |
142
+ | `incomingMoveToStatus` И `aiResponseMoveToStatus` оба заданы | На входящем сначала переведётся в incoming-статус, потом — после ответа — в ai-response статус. Клиент видит две движухи в воронке |
143
+ | `alwaysMoveToAiStatus=true` | Любой ai-response игнорирует текущую колонку и форсит переход в `aiResponseMoveToStatus` (даже если бот уже там) |
144
+
145
+ ### 4.3. Связь с типами статусов (`CrmPipelineStatus.statusTypes[]`)
146
+
147
+ | Триггер | Соответствующий `StatusType` |
148
+ |---|---|
149
+ | `incomingMoveToStatus` | `INCOMING` |
150
+ | `outgoingMoveToStatus` | `OUTGOING` |
151
+ | `messageSentMoveToStatus` | `MESSAGE_SENT` |
152
+
153
+ > `CrmPipelineStatus.statusTypes` — массив, **может содержать несколько типов**. Например, статус «Бот общается» может быть и `INCOMING`, и `MESSAGE_SENT`. UI Pipeline Builder использует это для отметки колонок.
154
+
155
+ ## 5. Глобальные правила
156
+
157
+ ### `alwaysMoveToAiStatus`
158
+
159
+ Если `true`: после **любого** ответа AI сделка форсированно переходит в `aiResponseMoveToStatus`, даже если она уже в колонке с включённым `isBotActive`. Это полезно, когда у клиента 5-10 «AI-колонок» и важно сводить всё в одну для аналитики.
160
+
161
+ ### `preventBackwardMovement`
162
+
163
+ Если `true`: автоматический move-to-status **не сработает**, если целевой статус имеет `sort` меньше текущего. Защита от бесконечного «маятника» (бот пишет → ai-response двигает назад → потом снова движение).
164
+
165
+ > Работает per-pipeline (учитывается только в пределах одной воронки).
166
+
167
+ ## 6. `PlatformPipeline` (M2M связь)
168
+
169
+ Платформа может работать с **несколькими активными воронками одновременно**:
170
+
171
+ ```json
172
+ {
173
+ "id": "uuid",
174
+ "platformId": "<Platform.id>",
175
+ "pipelineId": 1234567,
176
+ "pipelineCrmId": "<Crm.id>",
177
+ "sort": 0
178
+ }
179
+ ```
180
+
181
+ Используется когда:
182
+ - Один Telegram-бот ведёт лидов и в основную воронку, и в воронку «холодных».
183
+ - Несколько воронок Bitrix24 (для разных продуктов).
184
+
185
+ Без хотя бы одной записи `PlatformPipeline` все CRM-actions триггеров silently не сработают (платформа «не знает» в какую воронку кладть сделки).
186
+
187
+ ## 7. Что AI-сервис описывает клиенту
188
+
189
+ Стандартный workflow настройки Pipeline Settings:
190
+
191
+ 1. **Подключите Crm** (AmoCRM/Bitrix24/GetCourse) — см. [12-crm-integration.md](12-crm-integration.md).
192
+ 2. **Создайте `PlatformPipeline`** для каждой воронки, в которой бот должен работать.
193
+ 3. **Включите `filterEnabled`** и выберите режим поиска (`filterByPhoneEnabled` для Wazzup/SMS, `filterByContactEnabled` для AmoCRM).
194
+ 4. **Задайте `actionOnFound`** — обычно `LINK` (продолжить с существующей сделкой).
195
+ 5. **Настройте 1-3 автоперехода**, которые вам реально нужны:
196
+ - Минимум: `aiResponseMoveToStatus` для трекинга «бот ответил».
197
+ - Часто: `incomingMoveToStatus` + `outgoingMoveToStatus` для разделения первичной воронки.
198
+ - Реже: `userBlockedMoveToStatus` для архивирования.
199
+ 6. **Не включайте всё подряд** — больше автопереходов = больше движений в воронке = операторы запутаются.
200
+ 7. **`preventBackwardMovement=true`** — почти всегда нужен, защищает от глюков.
201
+
202
+ ## 8. Граничные случаи
203
+
204
+ | Ситуация | Поведение |
205
+ |---|---|
206
+ | `<X>MoveToStatus` указан, но не существует в CRM | Runtime логирует ошибку, переход не происходит |
207
+ | `<X>MoveToStatusPipelineId` не совпадает с активной `PlatformPipeline` | Runtime отклонит переход (статус не из «своей» воронки) |
208
+ | `filterByPhoneEnabled=true`, но у платформы нет телефона (Telegram Bot без shareContact) | Поиск не выполняется, ведёт себя как `filterEnabled=false` |
209
+ | `actionOnFound=IGNORE` И `createNewLead=false` | Сделка не создаётся, бот общается «в воздух» — CRM-actions не сработают |
210
+ | Несколько матчей в `filterByContactEnabled` | Берётся самая свежая сделка (по `updatedAt`) |
211
+ | Клиент сменил `filterByFieldEnabled` на лету | Существующие связи `ChatUser ↔ Deal` не пересчитываются — только для новых |
212
+
213
+ ## 9. Связь с другими подсистемами
214
+
215
+ | Подсистема | Связь |
216
+ |---|---|
217
+ | **Pipeline Builder** ([14-pipeline-builder.md](14-pipeline-builder.md)) | Базовая часть — UI и `CrmPipelineStatus.statusTypes` |
218
+ | **CRM** ([12-crm-integration.md](12-crm-integration.md)) | `Crm`, `CrmPipeline`, `CrmPipelineStatus`, поля контакта |
219
+ | **Платформы** ([11-platforms.md](11-platforms.md)) | `Platform.dealMarkerType`, `responsibleAssigneeStrategy`, `sendToCrm` |
220
+ | **Триггеры** ([04-triggers-and-actions.md](04-triggers-and-actions.md)) | `CRM_MOVE_STAGE` action — ручное переключение, может конфликтовать с автопереходами |
221
+ | **Routing** ([06-routing.md](06-routing.md)) | `LEAD_STATUS` маршрутизация работает поверх той же `CrmPipelineStatus` модели |