@besales/mcp 0.48.0 → 1.0.0-beta.4

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 (604) hide show
  1. package/README.md +117 -163
  2. package/README.onprem.ru.md +35 -0
  3. package/bin/besales-mcp-http.js +9 -0
  4. package/bin/besales-onprem.mjs +19 -0
  5. package/dist/auth/connection-store.d.ts +3 -1
  6. package/dist/auth/connection-store.js +9 -3
  7. package/dist/auth/connection-store.js.map +1 -1
  8. package/dist/auth/oauth-client.d.ts +20 -27
  9. package/dist/auth/oauth-client.js +31 -23
  10. package/dist/auth/oauth-client.js.map +1 -1
  11. package/dist/auth/session-workspace.d.ts +1 -0
  12. package/dist/auth/session-workspace.js +10 -1
  13. package/dist/auth/session-workspace.js.map +1 -1
  14. package/dist/auth/token-storage.js +4 -1
  15. package/dist/auth/token-storage.js.map +1 -1
  16. package/dist/cli.d.ts +1 -0
  17. package/dist/cli.js +80 -19
  18. package/dist/cli.js.map +1 -1
  19. package/dist/constants/crm-catalog.d.ts +2 -0
  20. package/dist/constants/crm-catalog.js +3 -0
  21. package/dist/constants/crm-catalog.js.map +1 -0
  22. package/dist/http/api-client.d.ts +54 -7
  23. package/dist/http/api-client.js +132 -66
  24. package/dist/http/api-client.js.map +1 -1
  25. package/dist/http/feedback-document-error.d.ts +26 -0
  26. package/dist/http/feedback-document-error.js +22 -0
  27. package/dist/http/feedback-document-error.js.map +1 -0
  28. package/dist/http-server.d.ts +15 -0
  29. package/dist/http-server.js +378 -0
  30. package/dist/http-server.js.map +1 -0
  31. package/dist/instructions/server-instructions.d.ts +1 -1
  32. package/dist/instructions/server-instructions.js +6 -3
  33. package/dist/instructions/server-instructions.js.map +1 -1
  34. package/dist/network/constants.d.ts +25 -0
  35. package/dist/network/constants.js +26 -0
  36. package/dist/network/constants.js.map +1 -0
  37. package/dist/network/failed-verifications.d.ts +10 -0
  38. package/dist/network/failed-verifications.js +37 -0
  39. package/dist/network/failed-verifications.js.map +1 -0
  40. package/dist/network/http-test-fixture.d.ts +3 -0
  41. package/dist/network/http-test-fixture.js +86 -0
  42. package/dist/network/http-test-fixture.js.map +1 -0
  43. package/dist/network/known-keys.d.ts +10 -0
  44. package/dist/network/known-keys.js +38 -0
  45. package/dist/network/known-keys.js.map +1 -0
  46. package/dist/network/memory-credentials.d.ts +11 -0
  47. package/dist/network/memory-credentials.js +24 -0
  48. package/dist/network/memory-credentials.js.map +1 -0
  49. package/dist/network/rate-limit.d.ts +23 -0
  50. package/dist/network/rate-limit.js +62 -0
  51. package/dist/network/rate-limit.js.map +1 -0
  52. package/dist/network/rejected-keys.d.ts +9 -0
  53. package/dist/network/rejected-keys.js +39 -0
  54. package/dist/network/rejected-keys.js.map +1 -0
  55. package/dist/network/request-work.d.ts +8 -0
  56. package/dist/network/request-work.js +20 -0
  57. package/dist/network/request-work.js.map +1 -0
  58. package/dist/network/successful-keys.d.ts +17 -0
  59. package/dist/network/successful-keys.js +47 -0
  60. package/dist/network/successful-keys.js.map +1 -0
  61. package/dist/resources/concepts/external-execution.md +3 -1
  62. package/dist/resources/concepts/feedback-sheets.md +21 -12
  63. package/dist/resources/concepts/icp.md +8 -1
  64. package/dist/resources/concepts/sandbox.md +15 -6
  65. package/dist/resources/concepts/triggers.md +43 -0
  66. package/dist/resources/concepts/workbook-classification.md +104 -50
  67. package/dist/resources/docs/agent-behavior.md +51 -382
  68. package/dist/resources/docs/campaigns.md +9 -16
  69. package/dist/resources/docs/crm-integration.md +39 -523
  70. package/dist/resources/docs/files-and-uploads.md +23 -286
  71. package/dist/resources/docs/follow-up.md +47 -12
  72. package/dist/resources/docs/knowledge-base.md +52 -521
  73. package/dist/resources/docs/pipeline-builder.md +26 -216
  74. package/dist/resources/docs/pipeline-settings-deep.md +27 -220
  75. package/dist/resources/docs/platforms.md +149 -519
  76. package/dist/resources/docs/prompt-anatomy.md +39 -285
  77. package/dist/resources/docs/routing.md +8 -16
  78. package/dist/resources/docs/trigger-actions.md +40 -24
  79. package/dist/resources/docs/variables.md +8 -12
  80. package/dist/resources/operator-edit.contract.d.ts +7 -0
  81. package/dist/resources/operator-edit.contract.js +8 -0
  82. package/dist/resources/operator-edit.contract.js.map +1 -0
  83. package/dist/resources/registry.js +20 -4
  84. package/dist/resources/registry.js.map +1 -1
  85. package/dist/resources/workflows/audit-dialogues.md +23 -12
  86. package/dist/resources/workflows/compare-models.md +6 -6
  87. package/dist/resources/workflows/connect-crm-from-scratch.md +15 -12
  88. package/dist/resources/workflows/connect-datasource-from-scratch.md +5 -4
  89. package/dist/resources/workflows/extract-from-document.md +8 -6
  90. package/dist/resources/workflows/operator-edit.md +15 -0
  91. package/dist/resources/workflows/platform-setup-from-scratch.md +59 -70
  92. package/dist/resources/workflows/production-readiness-check.md +25 -15
  93. package/dist/schemas/mcp-tools.json +12389 -6657
  94. package/dist/schemas/voice-actions-api.contract.json +248 -0
  95. package/dist/shared/constants/broadcast-reason.constants.d.ts +2 -0
  96. package/dist/shared/constants/broadcast-reason.constants.js +47 -0
  97. package/dist/shared/constants/broadcast-reason.constants.js.map +1 -0
  98. package/dist/shared/constants/crm-stage-pool.constants.d.ts +2 -0
  99. package/dist/shared/constants/crm-stage-pool.constants.js +3 -0
  100. package/dist/shared/constants/crm-stage-pool.constants.js.map +1 -0
  101. package/dist/shared/constants/followup-note.constants.d.ts +3 -0
  102. package/dist/shared/constants/followup-note.constants.js +4 -0
  103. package/dist/shared/constants/followup-note.constants.js.map +1 -0
  104. package/dist/shared/constants/followup-session.constants.d.ts +2 -0
  105. package/dist/shared/constants/followup-session.constants.js +3 -0
  106. package/dist/shared/constants/followup-session.constants.js.map +1 -0
  107. package/dist/shared/constants/funnel-analytics.constants.d.ts +2 -0
  108. package/dist/shared/constants/funnel-analytics.constants.js +3 -0
  109. package/dist/shared/constants/funnel-analytics.constants.js.map +1 -0
  110. package/dist/shared/constants/message-filter.constants.d.ts +7 -0
  111. package/dist/shared/constants/message-filter.constants.js +8 -0
  112. package/dist/shared/constants/message-filter.constants.js.map +1 -0
  113. package/dist/shared/utils/trigger-repeat-mapping.d.ts +72 -0
  114. package/dist/shared/utils/trigger-repeat-mapping.js +53 -0
  115. package/dist/shared/utils/trigger-repeat-mapping.js.map +1 -0
  116. package/dist/skills/besales-dialogue-audit/SKILL.md +31 -20
  117. package/dist/tasks/task-state-store.js +5 -0
  118. package/dist/tasks/task-state-store.js.map +1 -1
  119. package/dist/tools/annotations.js +4 -3
  120. package/dist/tools/annotations.js.map +1 -1
  121. package/dist/tools/definitions/agent-design.d.ts +81 -24
  122. package/dist/tools/definitions/agent-design.js +246 -90
  123. package/dist/tools/definitions/agent-design.js.map +1 -1
  124. package/dist/tools/definitions/agent-model-notice.d.ts +5 -0
  125. package/dist/tools/definitions/agent-model-notice.js +31 -0
  126. package/dist/tools/definitions/agent-model-notice.js.map +1 -0
  127. package/dist/tools/definitions/agent-models.d.ts +5 -0
  128. package/dist/tools/definitions/agent-models.js +26 -0
  129. package/dist/tools/definitions/agent-models.js.map +1 -0
  130. package/dist/tools/definitions/agent-settings.d.ts +85 -0
  131. package/dist/tools/definitions/agent-settings.js +187 -0
  132. package/dist/tools/definitions/agent-settings.js.map +1 -0
  133. package/dist/tools/definitions/ai-costs.d.ts +12 -0
  134. package/dist/tools/definitions/ai-costs.js +142 -0
  135. package/dist/tools/definitions/ai-costs.js.map +1 -0
  136. package/dist/tools/definitions/api-tool-request.d.ts +15 -0
  137. package/dist/tools/definitions/api-tool-request.js +2 -0
  138. package/dist/tools/definitions/api-tool-request.js.map +1 -0
  139. package/dist/tools/definitions/behavior-shipped-form.d.ts +182 -0
  140. package/dist/tools/definitions/behavior-shipped-form.js +185 -0
  141. package/dist/tools/definitions/behavior-shipped-form.js.map +1 -0
  142. package/dist/tools/definitions/broadcast-alerts.d.ts +6 -0
  143. package/dist/tools/definitions/broadcast-alerts.js +72 -0
  144. package/dist/tools/definitions/broadcast-alerts.js.map +1 -0
  145. package/dist/tools/definitions/broadcast-analytics.d.ts +102 -18
  146. package/dist/tools/definitions/broadcast-analytics.js +732 -431
  147. package/dist/tools/definitions/broadcast-analytics.js.map +1 -1
  148. package/dist/tools/definitions/broadcast-scheduler.d.ts +10 -13
  149. package/dist/tools/definitions/broadcast-scheduler.js +196 -154
  150. package/dist/tools/definitions/broadcast-scheduler.js.map +1 -1
  151. package/dist/tools/definitions/call-operator-task.schema.d.ts +11 -0
  152. package/dist/tools/definitions/call-operator-task.schema.js +30 -0
  153. package/dist/tools/definitions/call-operator-task.schema.js.map +1 -0
  154. package/dist/tools/definitions/contacts.d.ts +3 -0
  155. package/dist/tools/definitions/contacts.js +57 -31
  156. package/dist/tools/definitions/contacts.js.map +1 -1
  157. package/dist/tools/definitions/crm-platform.d.ts +29 -39
  158. package/dist/tools/definitions/crm-platform.js +249 -352
  159. package/dist/tools/definitions/crm-platform.js.map +1 -1
  160. package/dist/tools/definitions/crm-stage-pool.messages.d.ts +39 -0
  161. package/dist/tools/definitions/crm-stage-pool.messages.js +43 -0
  162. package/dist/tools/definitions/crm-stage-pool.messages.js.map +1 -0
  163. package/dist/tools/definitions/crm-stage-pool.schema.d.ts +64 -0
  164. package/dist/tools/definitions/crm-stage-pool.schema.js +109 -0
  165. package/dist/tools/definitions/crm-stage-pool.schema.js.map +1 -0
  166. package/dist/tools/definitions/custom-reports.d.ts +12 -0
  167. package/dist/tools/definitions/custom-reports.js +68 -0
  168. package/dist/tools/definitions/custom-reports.js.map +1 -0
  169. package/dist/tools/definitions/datasource.d.ts +2 -1
  170. package/dist/tools/definitions/datasource.js +28 -13
  171. package/dist/tools/definitions/datasource.js.map +1 -1
  172. package/dist/tools/definitions/dialog-review-findings-response.d.ts +14 -0
  173. package/dist/tools/definitions/dialog-review-findings-response.js +23 -0
  174. package/dist/tools/definitions/dialog-review-findings-response.js.map +1 -0
  175. package/dist/tools/definitions/dialog-review-safe-response.d.ts +5 -0
  176. package/dist/tools/definitions/dialog-review-safe-response.js +152 -0
  177. package/dist/tools/definitions/dialog-review-safe-response.js.map +1 -0
  178. package/dist/tools/definitions/dialog-review.d.ts +49 -0
  179. package/dist/tools/definitions/dialog-review.js +383 -0
  180. package/dist/tools/definitions/dialog-review.js.map +1 -0
  181. package/dist/tools/definitions/dialogue-analysis.d.ts +46 -0
  182. package/dist/tools/definitions/dialogue-analysis.js +170 -0
  183. package/dist/tools/definitions/dialogue-analysis.js.map +1 -0
  184. package/dist/tools/definitions/dialogue-audit-agent-scope.d.ts +7 -0
  185. package/dist/tools/definitions/dialogue-audit-agent-scope.js +41 -0
  186. package/dist/tools/definitions/dialogue-audit-agent-scope.js.map +1 -0
  187. package/dist/tools/definitions/dialogue-audit-alias.d.ts +67 -0
  188. package/dist/tools/definitions/dialogue-audit-alias.js +559 -0
  189. package/dist/tools/definitions/dialogue-audit-alias.js.map +1 -0
  190. package/dist/tools/definitions/dialogue-export.d.ts +7 -1
  191. package/dist/tools/definitions/dialogue-export.js +89 -32
  192. package/dist/tools/definitions/dialogue-export.js.map +1 -1
  193. package/dist/tools/definitions/dialogue-followup-state.d.ts +9 -0
  194. package/dist/tools/definitions/dialogue-followup-state.js +69 -0
  195. package/dist/tools/definitions/dialogue-followup-state.js.map +1 -0
  196. package/dist/tools/definitions/dialogue-reprocess.d.ts +5 -0
  197. package/dist/tools/definitions/dialogue-reprocess.js +107 -0
  198. package/dist/tools/definitions/dialogue-reprocess.js.map +1 -0
  199. package/dist/tools/definitions/edit-version-transport.test-fixtures.d.ts +13 -0
  200. package/dist/tools/definitions/edit-version-transport.test-fixtures.js +292 -0
  201. package/dist/tools/definitions/edit-version-transport.test-fixtures.js.map +1 -0
  202. package/dist/tools/definitions/edit-version.d.ts +5 -0
  203. package/dist/tools/definitions/edit-version.js +20 -0
  204. package/dist/tools/definitions/edit-version.js.map +1 -0
  205. package/dist/tools/definitions/external-api-channels.js +17 -15
  206. package/dist/tools/definitions/external-api-channels.js.map +1 -1
  207. package/dist/tools/definitions/feedback-document-access.d.ts +16 -0
  208. package/dist/tools/definitions/feedback-document-access.js +63 -0
  209. package/dist/tools/definitions/feedback-document-access.js.map +1 -0
  210. package/dist/tools/definitions/fixtures/shipped-setup-status.d.ts +3 -0
  211. package/dist/tools/definitions/fixtures/shipped-setup-status.js +9 -0
  212. package/dist/tools/definitions/fixtures/shipped-setup-status.js.map +1 -0
  213. package/dist/tools/definitions/follow-up-config.d.ts +79 -0
  214. package/dist/tools/definitions/follow-up-config.js +258 -0
  215. package/dist/tools/definitions/follow-up-config.js.map +1 -0
  216. package/dist/tools/definitions/follow-up-funnel-test.d.ts +4 -3
  217. package/dist/tools/definitions/follow-up-funnel-test.js +65 -52
  218. package/dist/tools/definitions/follow-up-funnel-test.js.map +1 -1
  219. package/dist/tools/definitions/follow-up-promotion-instructions.d.ts +2 -0
  220. package/dist/tools/definitions/follow-up-promotion-instructions.js +7 -0
  221. package/dist/tools/definitions/follow-up-promotion-instructions.js.map +1 -0
  222. package/dist/tools/definitions/follow-up-setup.d.ts +4 -28
  223. package/dist/tools/definitions/follow-up-setup.js +20 -87
  224. package/dist/tools/definitions/follow-up-setup.js.map +1 -1
  225. package/dist/tools/definitions/follow-up.d.ts +20 -122
  226. package/dist/tools/definitions/follow-up.js +200 -357
  227. package/dist/tools/definitions/follow-up.js.map +1 -1
  228. package/dist/tools/definitions/followup-getcourse-compat.d.ts +29 -0
  229. package/dist/tools/definitions/followup-getcourse-compat.js +62 -0
  230. package/dist/tools/definitions/followup-getcourse-compat.js.map +1 -0
  231. package/dist/tools/definitions/followup-sequence-config.schema.d.ts +10 -0
  232. package/dist/tools/definitions/followup-sequence-config.schema.js +14 -0
  233. package/dist/tools/definitions/followup-sequence-config.schema.js.map +1 -0
  234. package/dist/tools/definitions/funnel-analytics.d.ts +18 -0
  235. package/dist/tools/definitions/funnel-analytics.js +150 -0
  236. package/dist/tools/definitions/funnel-analytics.js.map +1 -0
  237. package/dist/tools/definitions/getcourse-anchor-refresh.d.ts +5 -9
  238. package/dist/tools/definitions/getcourse-anchor-refresh.js +26 -31
  239. package/dist/tools/definitions/getcourse-anchor-refresh.js.map +1 -1
  240. package/dist/tools/definitions/getcourse-cold-outbound-settings.d.ts +46 -0
  241. package/dist/tools/definitions/getcourse-cold-outbound-settings.js +37 -0
  242. package/dist/tools/definitions/getcourse-cold-outbound-settings.js.map +1 -0
  243. package/dist/tools/definitions/getcourse-cold-welcome.d.ts +14 -29
  244. package/dist/tools/definitions/getcourse-cold-welcome.js +190 -253
  245. package/dist/tools/definitions/getcourse-cold-welcome.js.map +1 -1
  246. package/dist/tools/definitions/getcourse-wave-welcome.messages.d.ts +10 -0
  247. package/dist/tools/definitions/getcourse-wave-welcome.messages.js +17 -0
  248. package/dist/tools/definitions/getcourse-wave-welcome.messages.js.map +1 -0
  249. package/dist/tools/definitions/help.js.map +1 -1
  250. package/dist/tools/definitions/icp-import-source.d.ts +75 -0
  251. package/dist/tools/definitions/icp-import-source.js +88 -0
  252. package/dist/tools/definitions/icp-import-source.js.map +1 -0
  253. package/dist/tools/definitions/inbound-analytics.d.ts +13 -0
  254. package/dist/tools/definitions/inbound-analytics.js +55 -0
  255. package/dist/tools/definitions/inbound-analytics.js.map +1 -0
  256. package/dist/tools/definitions/instagram-comment.templates.d.ts +19 -0
  257. package/dist/tools/definitions/instagram-comment.templates.js +34 -0
  258. package/dist/tools/definitions/instagram-comment.templates.js.map +1 -0
  259. package/dist/tools/definitions/instagram-settings-compat.d.ts +33 -0
  260. package/dist/tools/definitions/instagram-settings-compat.js +7 -0
  261. package/dist/tools/definitions/instagram-settings-compat.js.map +1 -0
  262. package/dist/tools/definitions/knowledge-chunking.d.ts +42 -0
  263. package/dist/tools/definitions/knowledge-chunking.js +83 -0
  264. package/dist/tools/definitions/knowledge-chunking.js.map +1 -0
  265. package/dist/tools/definitions/knowledge-space-search-fields.d.ts +12 -0
  266. package/dist/tools/definitions/knowledge-space-search-fields.js +30 -0
  267. package/dist/tools/definitions/knowledge-space-search-fields.js.map +1 -0
  268. package/dist/tools/definitions/knowledge.d.ts +24 -14
  269. package/dist/tools/definitions/knowledge.js +342 -233
  270. package/dist/tools/definitions/knowledge.js.map +1 -1
  271. package/dist/tools/definitions/legacy-trigger-repeat.d.ts +17 -0
  272. package/dist/tools/definitions/legacy-trigger-repeat.js +53 -0
  273. package/dist/tools/definitions/legacy-trigger-repeat.js.map +1 -0
  274. package/dist/tools/definitions/link-tracking.d.ts +8 -0
  275. package/dist/tools/definitions/link-tracking.js +141 -38
  276. package/dist/tools/definitions/link-tracking.js.map +1 -1
  277. package/dist/tools/definitions/mailing-variant-content.d.ts +75 -0
  278. package/dist/tools/definitions/mailing-variant-content.js +73 -0
  279. package/dist/tools/definitions/mailing-variant-content.js.map +1 -0
  280. package/dist/tools/definitions/model-comparison.d.ts +2 -1
  281. package/dist/tools/definitions/model-comparison.js +4 -5
  282. package/dist/tools/definitions/model-comparison.js.map +1 -1
  283. package/dist/tools/definitions/money-currency.d.ts +5 -0
  284. package/dist/tools/definitions/money-currency.js +10 -0
  285. package/dist/tools/definitions/money-currency.js.map +1 -0
  286. package/dist/tools/definitions/operator-workflow.d.ts +5 -5
  287. package/dist/tools/definitions/operator-workflow.js +80 -27
  288. package/dist/tools/definitions/operator-workflow.js.map +1 -1
  289. package/dist/tools/definitions/platform-funnel-analytics.d.ts +42 -0
  290. package/dist/tools/definitions/platform-funnel-analytics.js +72 -0
  291. package/dist/tools/definitions/platform-funnel-analytics.js.map +1 -0
  292. package/dist/tools/definitions/platform-getcourse-wave-field-bindings.d.ts +42 -0
  293. package/dist/tools/definitions/platform-getcourse-wave-field-bindings.js +51 -0
  294. package/dist/tools/definitions/platform-getcourse-wave-field-bindings.js.map +1 -0
  295. package/dist/tools/definitions/platform-settings-limits.d.ts +5 -0
  296. package/dist/tools/definitions/platform-settings-limits.js +6 -0
  297. package/dist/tools/definitions/platform-settings-limits.js.map +1 -0
  298. package/dist/tools/definitions/platform-settings-shipped-form.d.ts +37 -0
  299. package/dist/tools/definitions/platform-settings-shipped-form.js +310 -0
  300. package/dist/tools/definitions/platform-settings-shipped-form.js.map +1 -0
  301. package/dist/tools/definitions/platform-settings-update.d.ts +300 -0
  302. package/dist/tools/definitions/platform-settings-update.js +134 -0
  303. package/dist/tools/definitions/platform-settings-update.js.map +1 -0
  304. package/dist/tools/definitions/{getcourse-wave.d.ts → platform-setup-wave.d.ts} +75 -6
  305. package/dist/tools/definitions/platform-setup-wave.js +1079 -0
  306. package/dist/tools/definitions/platform-setup-wave.js.map +1 -0
  307. package/dist/tools/definitions/platform-setup.d.ts +697 -224
  308. package/dist/tools/definitions/platform-setup.js +313 -752
  309. package/dist/tools/definitions/platform-setup.js.map +1 -1
  310. package/dist/tools/definitions/platform-widget-page-rules.schema.d.ts +10 -0
  311. package/dist/tools/definitions/platform-widget-page-rules.schema.js +7 -0
  312. package/dist/tools/definitions/platform-widget-page-rules.schema.js.map +1 -0
  313. package/dist/tools/definitions/prompt-authoring.constants.d.ts +2 -0
  314. package/dist/tools/definitions/prompt-authoring.constants.js +3 -0
  315. package/dist/tools/definitions/prompt-authoring.constants.js.map +1 -0
  316. package/dist/tools/definitions/prompt-batch-controls.d.ts +5 -0
  317. package/dist/tools/definitions/prompt-batch-controls.js +40 -0
  318. package/dist/tools/definitions/prompt-batch-controls.js.map +1 -0
  319. package/dist/tools/definitions/prompt-draft-authoring.d.ts +5 -0
  320. package/dist/tools/definitions/prompt-draft-authoring.js +24 -0
  321. package/dist/tools/definitions/prompt-draft-authoring.js.map +1 -0
  322. package/dist/tools/definitions/prompt-text-projection.d.ts +11 -0
  323. package/dist/tools/definitions/prompt-text-projection.js +62 -0
  324. package/dist/tools/definitions/prompt-text-projection.js.map +1 -0
  325. package/dist/tools/definitions/reviewer-paid.d.ts +19 -0
  326. package/dist/tools/definitions/reviewer-paid.js +115 -0
  327. package/dist/tools/definitions/reviewer-paid.js.map +1 -0
  328. package/dist/tools/definitions/routing-condition-projection.d.ts +31 -0
  329. package/dist/tools/definitions/routing-condition-projection.js +97 -0
  330. package/dist/tools/definitions/routing-condition-projection.js.map +1 -0
  331. package/dist/tools/definitions/schedule-selection-hints.d.ts +3 -0
  332. package/dist/tools/definitions/schedule-selection-hints.js +42 -0
  333. package/dist/tools/definitions/schedule-selection-hints.js.map +1 -0
  334. package/dist/tools/definitions/{scheduled-tasks.d.ts → scheduled-task-alias.d.ts} +12 -10
  335. package/dist/tools/definitions/scheduled-task-alias.js +207 -0
  336. package/dist/tools/definitions/scheduled-task-alias.js.map +1 -0
  337. package/dist/tools/definitions/session.js +3 -3
  338. package/dist/tools/definitions/session.js.map +1 -1
  339. package/dist/tools/definitions/setup-init-operation.schema.d.ts +2 -0
  340. package/dist/tools/definitions/setup-init-operation.schema.js +6 -0
  341. package/dist/tools/definitions/setup-init-operation.schema.js.map +1 -0
  342. package/dist/tools/definitions/shared-input.helpers.d.ts +51 -0
  343. package/dist/tools/definitions/shared-input.helpers.js +323 -0
  344. package/dist/tools/definitions/shared-input.helpers.js.map +1 -0
  345. package/dist/tools/definitions/shared.d.ts +401 -307
  346. package/dist/tools/definitions/shared.js +430 -652
  347. package/dist/tools/definitions/shared.js.map +1 -1
  348. package/dist/tools/definitions/shipped-output-names.d.ts +19 -0
  349. package/dist/tools/definitions/shipped-output-names.js +123 -0
  350. package/dist/tools/definitions/shipped-output-names.js.map +1 -0
  351. package/dist/tools/definitions/task-state.d.ts +4 -4
  352. package/dist/tools/definitions/task-state.js +2 -1
  353. package/dist/tools/definitions/task-state.js.map +1 -1
  354. package/dist/tools/definitions/telegram-personal-capacity.d.ts +4 -0
  355. package/dist/tools/definitions/telegram-personal-capacity.js +18 -0
  356. package/dist/tools/definitions/telegram-personal-capacity.js.map +1 -0
  357. package/dist/tools/definitions/telegram-personal-management.d.ts +34 -0
  358. package/dist/tools/definitions/telegram-personal-management.js +139 -0
  359. package/dist/tools/definitions/telegram-personal-management.js.map +1 -0
  360. package/dist/tools/definitions/telegram-personal-restrictions.d.ts +19 -0
  361. package/dist/tools/definitions/telegram-personal-restrictions.js +109 -0
  362. package/dist/tools/definitions/telegram-personal-restrictions.js.map +1 -0
  363. package/dist/tools/definitions/telegram-pool-policy.d.ts +8 -0
  364. package/dist/tools/definitions/telegram-pool-policy.js +56 -0
  365. package/dist/tools/definitions/telegram-pool-policy.js.map +1 -0
  366. package/dist/tools/definitions/touch-history-alias.d.ts +4 -0
  367. package/dist/tools/definitions/touch-history-alias.js +99 -0
  368. package/dist/tools/definitions/touch-history-alias.js.map +1 -0
  369. package/dist/tools/definitions/trigger-action-item.schema.d.ts +254 -0
  370. package/dist/tools/definitions/trigger-action-item.schema.js +121 -0
  371. package/dist/tools/definitions/trigger-action-item.schema.js.map +1 -0
  372. package/dist/tools/definitions/trigger-argument.schema.d.ts +14 -0
  373. package/dist/tools/definitions/trigger-argument.schema.js +23 -0
  374. package/dist/tools/definitions/trigger-argument.schema.js.map +1 -0
  375. package/dist/tools/definitions/trigger-claim-alias.d.ts +10 -0
  376. package/dist/tools/definitions/trigger-claim-alias.js +90 -0
  377. package/dist/tools/definitions/trigger-claim-alias.js.map +1 -0
  378. package/dist/tools/definitions/trigger-echo-projection.d.ts +1 -0
  379. package/dist/tools/definitions/trigger-echo-projection.js +41 -0
  380. package/dist/tools/definitions/trigger-echo-projection.js.map +1 -0
  381. package/dist/tools/definitions/trigger-upsert.tool.d.ts +41 -0
  382. package/dist/tools/definitions/trigger-upsert.tool.js +159 -0
  383. package/dist/tools/definitions/trigger-upsert.tool.js.map +1 -0
  384. package/dist/tools/definitions/v15-port-descriptions.d.ts +11 -0
  385. package/dist/tools/definitions/v15-port-descriptions.js +30 -0
  386. package/dist/tools/definitions/v15-port-descriptions.js.map +1 -0
  387. package/dist/tools/definitions/variables.d.ts +44 -0
  388. package/dist/tools/definitions/variables.js +310 -0
  389. package/dist/tools/definitions/variables.js.map +1 -0
  390. package/dist/tools/definitions/voice-action-input.d.ts +9 -0
  391. package/dist/tools/definitions/voice-action-input.js +141 -0
  392. package/dist/tools/definitions/voice-action-input.js.map +1 -0
  393. package/dist/tools/definitions/widget-customization-compat.d.ts +51 -0
  394. package/dist/tools/definitions/widget-customization-compat.js +23 -0
  395. package/dist/tools/definitions/widget-customization-compat.js.map +1 -0
  396. package/dist/tools/definitions.d.ts +1134 -617
  397. package/dist/tools/definitions.js +63 -27
  398. package/dist/tools/definitions.js.map +1 -1
  399. package/dist/tools/description-localization.js +3 -2
  400. package/dist/tools/description-localization.js.map +1 -1
  401. package/dist/tools/follow-up-request.d.ts +19 -0
  402. package/dist/tools/follow-up-request.js +81 -0
  403. package/dist/tools/follow-up-request.js.map +1 -0
  404. package/dist/tools/followup-revision.d.ts +4 -0
  405. package/dist/tools/followup-revision.js +29 -0
  406. package/dist/tools/followup-revision.js.map +1 -0
  407. package/dist/tools/incomplete-list.js +4 -0
  408. package/dist/tools/incomplete-list.js.map +1 -1
  409. package/dist/tools/inventory.js +47 -11
  410. package/dist/tools/inventory.js.map +1 -1
  411. package/dist/tools/operation-recovery.d.ts +4 -0
  412. package/dist/tools/operation-recovery.js +33 -0
  413. package/dist/tools/operation-recovery.js.map +1 -0
  414. package/dist/tools/registry-mapping-primary.cases.d.ts +2 -0
  415. package/dist/tools/registry-mapping-primary.cases.js +765 -0
  416. package/dist/tools/registry-mapping-primary.cases.js.map +1 -0
  417. package/dist/tools/registry-mapping-secondary.cases.d.ts +2 -0
  418. package/dist/tools/registry-mapping-secondary.cases.js +626 -0
  419. package/dist/tools/registry-mapping-secondary.cases.js.map +1 -0
  420. package/dist/tools/registry-mapping.cases.d.ts +8 -0
  421. package/dist/tools/registry-mapping.cases.js +170 -0
  422. package/dist/tools/registry-mapping.cases.js.map +1 -0
  423. package/dist/tools/registry-test-fixtures.d.ts +50 -0
  424. package/dist/tools/registry-test-fixtures.js +120 -0
  425. package/dist/tools/registry-test-fixtures.js.map +1 -0
  426. package/dist/tools/registry.d.ts +2 -0
  427. package/dist/tools/registry.js +84 -52
  428. package/dist/tools/registry.js.map +1 -1
  429. package/dist/tools/result.d.ts +1 -0
  430. package/dist/tools/result.js +90 -22
  431. package/dist/tools/result.js.map +1 -1
  432. package/dist/tools/toolsets.d.ts +16 -0
  433. package/dist/tools/toolsets.js +25 -1
  434. package/dist/tools/toolsets.js.map +1 -1
  435. package/dist/types/api-contract.gen.d.ts +131892 -22453
  436. package/dist/utils/credential-signatures.d.ts +3 -0
  437. package/dist/utils/credential-signatures.js +10 -0
  438. package/dist/utils/credential-signatures.js.map +1 -0
  439. package/dist/utils/data-source-column-name.d.ts +10 -0
  440. package/dist/utils/data-source-column-name.js +36 -0
  441. package/dist/utils/data-source-column-name.js.map +1 -0
  442. package/dist/utils/logger.d.ts +1 -0
  443. package/dist/utils/logger.js +32 -8
  444. package/dist/utils/logger.js.map +1 -1
  445. package/dist/utils/secret-read-boundary.d.ts +2 -0
  446. package/dist/utils/secret-read-boundary.js +88 -0
  447. package/dist/utils/secret-read-boundary.js.map +1 -0
  448. package/dist/utils/setup-secrets.d.ts +4 -0
  449. package/dist/utils/setup-secrets.js +40 -0
  450. package/dist/utils/setup-secrets.js.map +1 -0
  451. package/docs/agent-models.md +9 -0
  452. package/docs/agent-usage-guide.full.ru.md +1 -4
  453. package/docs/analytics-contract.md +67 -0
  454. package/docs/async-operation-lifecycle.md +10 -3
  455. package/docs/connect.ru.md +144 -4
  456. package/docs/contact-company-profile.md +17 -0
  457. package/docs/contact-recommendations.md +7 -0
  458. package/docs/crm-stage-pool.md +73 -0
  459. package/docs/customer-data.md +61 -0
  460. package/docs/dialogue-reprocess.md +37 -0
  461. package/docs/edit-versions.md +36 -0
  462. package/docs/failure-semantics.md +17 -9
  463. package/docs/help-navigation.md +15 -42
  464. package/docs/host-compatibility-matrix.md +72 -93
  465. package/docs/host-setup.md +33 -35
  466. package/docs/invariants/public-api-and-contracts.md +16 -0
  467. package/docs/legacy-action-settings.md +21 -0
  468. package/docs/main-transfer-dependencies.md +38 -0
  469. package/docs/mcp2-parity.md +50 -0
  470. package/docs/modules/analytics.md +79 -0
  471. package/docs/modules/besales-mcp.md +144 -0
  472. package/docs/modules/dialog-review-report.md +38 -0
  473. package/docs/modules/dialogue-followup-adapters.md +39 -0
  474. package/docs/modules/message-reactions.md +17 -0
  475. package/docs/modules/network-mode.md +284 -0
  476. package/docs/modules/prompt-batch-and-dialogue-controls.md +69 -0
  477. package/docs/pii-policy.md +21 -16
  478. package/docs/response-size-budget.md +197 -7
  479. package/docs/secret-read-boundary.md +102 -0
  480. package/docs/task-state.md +7 -2
  481. package/docs/telegram-personal-management.md +50 -0
  482. package/docs/telegram-personal-restrictions.md +87 -0
  483. package/docs/telegram-pool-policy.md +19 -0
  484. package/docs/tool-effects.md +15 -9
  485. package/docs/tool-surface.md +205 -4
  486. package/docs/voice-actions-and-inbox.md +95 -0
  487. package/docs/wazzup-instagram-comments.md +26 -0
  488. package/package.json +17 -10
  489. package/scripts/api-source-loader.spec.mjs +40 -0
  490. package/scripts/audit-bridge-controls.mjs +129 -0
  491. package/scripts/audit-revert-mutants.mjs +107 -0
  492. package/scripts/bridge-api-input.spec.mjs +98 -0
  493. package/scripts/bridge-controls-mutants.mjs +130 -0
  494. package/scripts/brx3-shipped-refusals.spec.mjs +149 -0
  495. package/scripts/brx4-ledger-payload.spec.mjs +127 -0
  496. package/scripts/build-onprem.mjs +108 -0
  497. package/scripts/connect-clients.sh +141 -0
  498. package/scripts/connect-scopes.mjs +166 -0
  499. package/scripts/connect-scopes.spec.mjs +83 -0
  500. package/scripts/emit-personal-link-carrier.mjs +31 -0
  501. package/scripts/emit-setup-init-schemas.mjs +24 -0
  502. package/scripts/emit-tool-routes.mjs +878 -0
  503. package/scripts/emit-tool-routes.spec.mjs +300 -0
  504. package/scripts/followup-registry.spec.mjs +107 -0
  505. package/scripts/generate-crm-rotation-contract.mjs +98 -0
  506. package/scripts/generate-followup-session-contract.mjs +16 -0
  507. package/scripts/getcourse-text-source.spec.mjs +110 -0
  508. package/scripts/icp-import-verification/mutate.cjs +70 -0
  509. package/scripts/import-secret-read-contract.mjs +53 -0
  510. package/scripts/install-claude-desktop.js +1 -1
  511. package/scripts/lag-decisions.mjs +37 -0
  512. package/scripts/lag-decisions.spec.mjs +87 -0
  513. package/scripts/lag-open-task-owners.mjs +46 -0
  514. package/scripts/lag-open-task-owners.spec.mjs +78 -0
  515. package/scripts/lag-payload.mjs +10 -0
  516. package/scripts/legacy-action-lag.spec.mjs +22 -0
  517. package/scripts/legacy-field-refusal.shipped.spec.mjs +99 -0
  518. package/scripts/mcpcompat-handler.mjs +34 -0
  519. package/scripts/mcpcompat-loader.mjs +36 -0
  520. package/scripts/mcpcompat-typescript.mjs +3 -0
  521. package/scripts/platform-settings-shipped-parity.spec.mjs +89 -0
  522. package/scripts/prbr-round1-recheck.spec.mjs +605 -0
  523. package/scripts/prbr-shipped-compatibility.spec.mjs +276 -0
  524. package/scripts/publish-mcp-registry.mjs +98 -0
  525. package/scripts/publish-mcp-registry.spec.mjs +68 -0
  526. package/scripts/record-scheduled-task-call.mjs +26 -0
  527. package/scripts/refusal-probe.mjs +17 -0
  528. package/scripts/refusal-probe.spec.mjs +29 -0
  529. package/scripts/registry-diff.mjs +502 -0
  530. package/scripts/registry-diff.spec.mjs +787 -0
  531. package/scripts/request-query.mjs +43 -0
  532. package/scripts/request-query.spec.mjs +82 -0
  533. package/scripts/route-lag.mjs +23 -0
  534. package/scripts/route-lag.spec.mjs +30 -0
  535. package/scripts/sample-args.mjs +174 -0
  536. package/scripts/sample-args.spec.mjs +160 -0
  537. package/scripts/shipped-definitions.mjs +197 -0
  538. package/scripts/shipped-definitions.spec.mjs +57 -0
  539. package/scripts/shipped-inputs.spec.mjs +136 -0
  540. package/scripts/shipped-recordings.spec.mjs +19 -0
  541. package/scripts/shipped-ref.d.mts +5 -0
  542. package/scripts/shipped-ref.mjs +20 -0
  543. package/scripts/shipped-ref.spec.mjs +59 -0
  544. package/scripts/shipped-registry.mjs +107 -0
  545. package/scripts/shipped-sample-candidates.mjs +69 -0
  546. package/scripts/shipped-sample-candidates.spec.mjs +117 -0
  547. package/scripts/shipped-sample.mjs +871 -0
  548. package/scripts/shipped-sample.spec.mjs +451 -0
  549. package/scripts/sync-agent-model-schemas.mjs +72 -0
  550. package/scripts/sync-api-contracts.mjs +85 -0
  551. package/scripts/sync-api-contracts.spec.mjs +144 -0
  552. package/scripts/sync-contract-ledger-schemas.mjs +161 -0
  553. package/scripts/sync-crm-stage-pool-schema.mjs +85 -0
  554. package/scripts/sync-prompt-draft-schema.mjs +92 -0
  555. package/scripts/sync-route-map.mjs +94 -0
  556. package/scripts/sync-schema-descriptions.mjs +25 -0
  557. package/scripts/sync-tool-inventory.mjs +27 -0
  558. package/scripts/sync-trigger-argument-schema.mjs +97 -0
  559. package/scripts/sync-trigger-argument-schema.spec.mjs +91 -0
  560. package/scripts/sync-trigger-event-schema.mjs +20 -0
  561. package/scripts/sync-trigger-repeat-mapping.mjs +12 -0
  562. package/scripts/sync-trigger-task-schema.mjs +45 -0
  563. package/scripts/sync-voice-actions-contract.mjs +67 -0
  564. package/scripts/sync-voice-actions-tools.mjs +97 -0
  565. package/scripts/sync-wave-id-schemas.mjs +55 -0
  566. package/scripts/telegram-personal-confirmation-mutants.mjs +74 -0
  567. package/scripts/tool-input-schemas.mjs +46 -0
  568. package/scripts/tool-input-schemas.spec.mjs +50 -0
  569. package/scripts/verify-followup-adapters.mjs +235 -0
  570. package/scripts/verify-secret-read-import.mjs +84 -0
  571. package/scripts/verify-secret-read-mutants.mjs +143 -0
  572. package/dist/tools/definitions/analytics.d.ts +0 -40
  573. package/dist/tools/definitions/analytics.js +0 -233
  574. package/dist/tools/definitions/analytics.js.map +0 -1
  575. package/dist/tools/definitions/broadcast-mailing-variants.d.ts +0 -41
  576. package/dist/tools/definitions/broadcast-mailing-variants.js +0 -194
  577. package/dist/tools/definitions/broadcast-mailing-variants.js.map +0 -1
  578. package/dist/tools/definitions/dialogue-audit.d.ts +0 -38
  579. package/dist/tools/definitions/dialogue-audit.js +0 -292
  580. package/dist/tools/definitions/dialogue-audit.js.map +0 -1
  581. package/dist/tools/definitions/getcourse-wave.js +0 -519
  582. package/dist/tools/definitions/getcourse-wave.js.map +0 -1
  583. package/dist/tools/definitions/operational-overview.d.ts +0 -3
  584. package/dist/tools/definitions/operational-overview.js +0 -23
  585. package/dist/tools/definitions/operational-overview.js.map +0 -1
  586. package/dist/tools/definitions/scheduled-tasks.js +0 -173
  587. package/dist/tools/definitions/scheduled-tasks.js.map +0 -1
  588. package/dist/tools/definitions/trigger-contact-claims.d.ts +0 -7
  589. package/dist/tools/definitions/trigger-contact-claims.js +0 -60
  590. package/dist/tools/definitions/trigger-contact-claims.js.map +0 -1
  591. package/dist/tools/registry-mapping-cases-core.d.ts +0 -38
  592. package/dist/tools/registry-mapping-cases-core.js +0 -963
  593. package/dist/tools/registry-mapping-cases-core.js.map +0 -1
  594. package/dist/tools/registry-mapping-cases-platform.d.ts +0 -2
  595. package/dist/tools/registry-mapping-cases-platform.js +0 -1052
  596. package/dist/tools/registry-mapping-cases-platform.js.map +0 -1
  597. package/dist/tools/trigger-action-config.d.ts +0 -49
  598. package/dist/tools/trigger-action-config.js +0 -171
  599. package/dist/tools/trigger-action-config.js.map +0 -1
  600. package/docs/evals/model-behavior-scorecard.md +0 -80
  601. package/docs/operation-coverage.md +0 -114
  602. package/scripts/mock-credentials.js +0 -77
  603. package/scripts/model-eval-server.js +0 -213
  604. package/scripts/run-model-eval.js +0 -447
@@ -0,0 +1,1079 @@
1
+ import { withAgentModelNotice } from './agent-model-notice.js';
2
+ import { z } from 'zod';
3
+ import { assertCompleteList } from '../incomplete-list.js';
4
+ import { withProjectedRoutingRules } from './routing-condition-projection.js';
5
+ import { CHANNEL_AGENTS_MAX_PAGE_SIZE, compactObject, defineTool, uuidSchema, withInstructions, } from './shared.js';
6
+ import { WAVE_TRANSPORTS } from './follow-up.js';
7
+ import { WAVE_RUN_WELCOME_PAUSE_INSTRUCTION, WAVE_WELCOME_PAUSE_INSTRUCTION, } from './getcourse-wave-welcome.messages.js';
8
+ // Помощники долгих прогонов волны GetCourse. Перенесены ДОСЛОВНО из отгружаемой линии пакета
9
+ // (`src/tools/definitions/getcourse-wave.ts`): инструменты уже стоят у клиентов, и любое
10
+ // расхождение формы здесь — молчаливое расхождение контракта.
11
+ const waveFunnelSchema = z.string().max(200).describe('Передавай код школьной воронки fNN либо метку с этим кодом (f11 — ДБО). Без кода название образует отдельный ключ воронки. Не подставляй когорту, сегмент или подпись запуска: для них segment и cohort_label; продукт — product_code.');
12
+ const normalizedWaveTransports = new Set(WAVE_TRANSPORTS.map((name) => name.toUpperCase()));
13
+ const waveChannelsSchema = z.array(z.string()).min(1).superRefine((values, ctx) => {
14
+ // Как в API: проверяем нормализованное множество, отправляем исходное написание.
15
+ const normalized = new Set(values.map((value) => value.trim().toUpperCase()));
16
+ if (normalized.size > WAVE_TRANSPORTS.length) {
17
+ ctx.addIssue({ code: 'custom', message: 'Не больше 10 различных транспортов после нормализации' });
18
+ }
19
+ values.forEach((value, index) => {
20
+ if (!normalizedWaveTransports.has(value.trim().toUpperCase())) {
21
+ ctx.addIssue({ code: 'custom', path: [index], message: 'Неизвестный транспорт GetCourse' });
22
+ }
23
+ });
24
+ }).describe('Полный перечень транспортов: Site, Email, SMS, Telegram, Facebook, VK, Chatium, WhatsApp, Viber, MAX. Проверяется множество после trim, приведения регистра и удаления дублей: от 1 до 10 транспортов. В API уходит исходное написание; API нормализует сам.');
25
+ const BROADCAST_SEGMENT_DESCRIPTION_MAX_LENGTH = 4000;
26
+ /** Предел поиска имени на серверной границе. */
27
+ const CHANNEL_AGENT_QUERY_MAX_LENGTH = 120;
28
+ const UNCERTAIN_WAVE_OUTCOME_INSTRUCTION = 'UNKNOWN и FAILED с неопределённым внешним исходом жёстко блокируются между волнами: repeat-режим и dedup_window_days=0 их не обходят. Повтор допустим только для FAILED, который подтверждённо возник до внешней отправки; остальные случаи требуют отдельного ручного разбора исхода.';
29
+ const getCourseWaveContactSchema = z.object({
30
+ contact_id: z.string(),
31
+ deal_id: z.string().optional(),
32
+ transport: z.string().optional(),
33
+ dialog_id: z.string().optional(),
34
+ email: z.string().email().max(320).optional(),
35
+ phone: z.string().max(64).optional(),
36
+ first_name: z.string().max(200).optional(),
37
+ last_name: z.string().max(200).optional(),
38
+ lead_last_active_at: z.string().datetime({ offset: true }).optional(),
39
+ segment_entered_at: z.string().datetime({ offset: true }).optional(),
40
+ });
41
+ /**
42
+ * Два идентификатора человека в строке прогона — разные вещи, и выдача прогона — основание для переотправки. Карточка
43
+ * в поле номера ученика не пройдёт построчную проверку цифр: человеку ничего не уйдёт, а шаг станет отказом.
44
+ */
45
+ export const WAVE_RUN_CONTACT_IDS_INSTRUCTION = 'contactId — карточка контакта Besales (UUID), getcourseUserId — номер ученика GetCourse. Для повторной отправки в contact_id подставляй getcourseUserId, не contactId.';
46
+ const mapGetCourseWaveContact = (contact) => compactObject({
47
+ getcourseUserId: contact.contact_id,
48
+ dealId: contact.deal_id,
49
+ transport: contact.transport,
50
+ dialogId: contact.dialog_id,
51
+ email: contact.email,
52
+ phone: contact.phone,
53
+ firstName: contact.first_name,
54
+ lastName: contact.last_name,
55
+ leadLastActiveAt: contact.lead_last_active_at,
56
+ segmentEnteredAt: contact.segment_entered_at,
57
+ });
58
+ export const platformSetupWaveTools = [
59
+ defineTool({
60
+ name: 'besales_workspace_overview',
61
+ sideEffect: 'none',
62
+ closedWorld: true,
63
+ requiredFields: [],
64
+ defaultWorkspaceId: true,
65
+ schema: z.object({
66
+ workspace_id: uuidSchema.optional(),
67
+ }),
68
+ createRequest: (input) => ({
69
+ method: 'GET',
70
+ path: `/workspaces/${input.workspace_id}/overview`,
71
+ }),
72
+ enhanceResponse: (response) => withInstructions(response, [
73
+ 'Single-shot snapshot всего workspace: platforms (с agentsCount), ICP profiles (с segments/samples counts), feedback sheets, MCP connections summary.',
74
+ 'Используй ПЕРВЫМ когда user задаёт open-ended вопросы вида "что у меня есть?" / "покажи мою настройку" — заменяет N round-trips.',
75
+ '`setupCompleteness` — readiness-сигнал «что недоделано»: channelsConnected/Pending, crmConnected, hasAnchorAgent, hasKnowledge, knowledgeLinkedToChannel, hasTriggers/Followups, hasIcp, lastSandboxRunAt, suggestedNextPhase. Используй suggestedNextPhase как мягкий hint следующего фокуса (не приказ); продолжай настройку с него, а не с нуля.',
76
+ 'После overview перейди на drill-down: `besales_platform_context_get` для конкретной платформы, `besales_agent_list` для агентов внутри неё.',
77
+ ]),
78
+ }),
79
+ defineTool({
80
+ name: 'besales_workspace_navigate',
81
+ sideEffect: 'none',
82
+ closedWorld: true,
83
+ requiredFields: ['intent'],
84
+ defaultWorkspaceId: true,
85
+ resultPolicy: {
86
+ neverTruncate: true,
87
+ narrowingHint: 'Передайте известные platform_id, agent_id, contact_id или task_id; если requiredUserInput непуст, уточните перечисленные поля у пользователя.',
88
+ },
89
+ schema: z
90
+ .object({
91
+ workspace_id: uuidSchema.optional(),
92
+ intent: z.enum([
93
+ 'CREATE_BOT',
94
+ 'IMPROVE_AGENT',
95
+ 'DIAGNOSE_CONTACT',
96
+ 'AUDIT_DIALOGUES',
97
+ 'CAMPAIGN_REPORT',
98
+ 'CONNECT_CRM',
99
+ 'CONNECT_DATASOURCE',
100
+ 'CONFIGURE_FOLLOWUP',
101
+ 'CONTINUE_TASK',
102
+ 'FREEFORM',
103
+ ]),
104
+ bot_type: z.enum(['FAQ', 'SALES', 'BOOKING', 'OUTBOUND', 'SUPPORT', 'UNKNOWN']).optional(),
105
+ platform_id: uuidSchema.optional(),
106
+ agent_id: uuidSchema.optional(),
107
+ contact_id: uuidSchema.optional(),
108
+ task_id: uuidSchema.optional(),
109
+ question: z.string().trim().min(2).max(500).optional(),
110
+ })
111
+ .strict(),
112
+ createRequest: (input) => ({
113
+ method: 'POST',
114
+ path: `/workspaces/${input.workspace_id}/navigation`,
115
+ body: compactObject({
116
+ intent: input.intent,
117
+ botType: input.bot_type,
118
+ channelId: input.platform_id,
119
+ agentId: input.agent_id,
120
+ contactId: input.contact_id,
121
+ taskId: input.task_id,
122
+ question: input.question,
123
+ }),
124
+ }),
125
+ enhanceResponse: (response) => withInstructions(response, [
126
+ 'next.tool и next.arguments — один адресный следующий шаг; placeholders перечислены в requiredUserInput и не должны угадываться.',
127
+ 'Сохранённый overview — evidence только на момент ответа. Перед записью перечитай выбранный объект, а после записи выполни next.verifyWith.',
128
+ 'CREATE_BOT учитывает bot_type: FAQ не требует ICP/CRM автоматически, SALES/BOOKING/OUTBOUND требуют ICP и условий до доказательства готовности.',
129
+ 'FREEFORM возвращает help route и не запускает обязательный шестишаговый мастер.',
130
+ ]),
131
+ }),
132
+ defineTool({
133
+ name: 'besales_platform_context_get',
134
+ sideEffect: 'none',
135
+ requiredFields: ['platform_id'],
136
+ // Снимок для замещающей записи: из него берут текущий набор действий триггера перед
137
+ // besales_trigger_upsert(replace_actions), а неполный снимок молча удалит непрочитанное.
138
+ // Системные промпты агентов выбрасываются первыми — на крупном канале это до 72 % веса,
139
+ // и читаются они отдельными инструментами besales_prompt_*.
140
+ resultPolicy: {
141
+ neverTruncate: true,
142
+ droppablePaths: ['agents[].baseSystemPrompt'],
143
+ narrowingHint: 'Снимок канала отдаётся целиком и на канале с десятками агентов не помещается в ответ. Работайте по одному агенту: besales_agent_list для перечня, besales_prompt_patch_get для промпта.',
144
+ },
145
+ schema: z.object({
146
+ platform_id: uuidSchema,
147
+ // Срез снимка: без него на канале с десятками агентов ответ не помещается вовсе.
148
+ // `agent_summaries` отдаёт агентов БЕЗ промптов, `agents_full` — целиком.
149
+ sections: z
150
+ .array(z.enum([
151
+ 'platform',
152
+ 'agent_summaries',
153
+ 'agents_full',
154
+ 'router',
155
+ 'behavior',
156
+ 'follow_up',
157
+ 'pipeline_settings',
158
+ 'readiness',
159
+ ]))
160
+ .min(1)
161
+ .optional(),
162
+ }),
163
+ createRequest: (input) => {
164
+ const suffix = input.sections === undefined
165
+ ? ''
166
+ : `?sections=${encodeURIComponent(input.sections.join(','))}`;
167
+ return {
168
+ method: 'GET',
169
+ path: `/channels/${input.platform_id}/setup/context${suffix}`,
170
+ };
171
+ },
172
+ enhanceResponse: (response) => withInstructions(withProjectedRoutingRules(response), [
173
+ 'Use this sanitized context first for Platform Setup Bridge flows.',
174
+ 'Read readiness flags, then call targeted mutation tools. Do not ask the user for workspace_id.',
175
+ 'Prompt content changes still go through besales_prompt_* tools and besales_prompt_finalize.',
176
+ 'У правил роутинга «по значению переменной» рядом с сырым condition лежат condition_logic и conditions[] — именно их принимает besales_routing_rule_upsert. Внутри условия набор значений (variable_values) и его первое значение (variable_value) описывают ОДНО и то же: правь набор, а одиночное значение приводи в соответствие либо не присылай вовсе.',
177
+ ]),
178
+ }),
179
+ defineTool({
180
+ name: 'besales_trigger_action_catalog_get',
181
+ sideEffect: 'none',
182
+ closedWorld: true,
183
+ requiredFields: ['platform_id'],
184
+ resultPolicy: {
185
+ neverTruncate: true,
186
+ narrowingHint: 'Полный каталог не помещается в ответ. Повторите вызов с action_type для одного действия.',
187
+ },
188
+ schema: z.object({
189
+ platform_id: uuidSchema,
190
+ action_type: z.string().trim().min(1).optional(),
191
+ }),
192
+ createRequest: (input) => ({
193
+ method: 'GET',
194
+ path: `/channels/${input.platform_id}/setup/trigger-action-catalog` +
195
+ (input.action_type ? `?actionType=${encodeURIComponent(input.action_type)}` : ''),
196
+ }),
197
+ enhanceResponse: (response) => withInstructions(response, [
198
+ 'Каталог построен из канонического Zod union, карты wire-имён и реестра runtime handlers.',
199
+ 'runtimeAvailable=false означает реальный блокер исполнения; не создавай такой шаг до появления handler.',
200
+ 'Для записи используй minimalExample и scope мутации из ответа, затем выполни verification.',
201
+ 'Строка в Google-таблицу (GOOGLE_SHEETS_APPEND_ROW): попроси человека открыть таблицу на редактирование адресу ' +
202
+ 'serviceAccount.email из этого пункта; serviceAccount.configured=false — действие исполниться не сможет.',
203
+ ]),
204
+ }),
205
+ defineTool({
206
+ name: 'besales_agent_list',
207
+ sideEffect: 'none',
208
+ closedWorld: true,
209
+ requiredFields: ['platform_id'],
210
+ // Перечень парка, по которому запускают массовую правку промптов. Усечённый список здесь —
211
+ // не «неполный ответ», а раскатка на ЧАСТЬ агентов с отчётом об успехе.
212
+ resultPolicy: { neverTruncate: true },
213
+ schema: z.object({
214
+ platform_id: uuidSchema,
215
+ // ⚠️ Без умолчания: без этих ручек отказ по размеру был бы отказом БЕЗ ВЫХОДА — сузить
216
+ // перечень было нечем. Умолчание же превратило бы отказ в тихую раскатку на часть парка.
217
+ limit: z.number().int().min(1).max(CHANNEL_AGENTS_MAX_PAGE_SIZE).optional(),
218
+ cursor: z.string().min(1).optional(),
219
+ // Поиск без учёта регистра по имени или отображаемому имени агента.
220
+ query: z.string().trim().min(1).max(CHANNEL_AGENT_QUERY_MAX_LENGTH).optional(),
221
+ }),
222
+ createRequest: (input) => {
223
+ const query = new URLSearchParams();
224
+ if (input.limit !== undefined)
225
+ query.set('limit', String(input.limit));
226
+ if (input.cursor)
227
+ query.set('cursor', input.cursor);
228
+ if (input.query)
229
+ query.set('query', input.query);
230
+ const suffix = query.size > 0 ? `?${query.toString()}` : '';
231
+ return {
232
+ method: 'GET',
233
+ path: `/channels/${input.platform_id}/agents${suffix}`,
234
+ };
235
+ },
236
+ enhanceResponse: (response, input) => withAgentModelNotice(assertCompleteList(response, {
237
+ pageRequested: input.limit !== undefined || input.cursor !== undefined,
238
+ toolName: 'besales_agent_list',
239
+ howToPage: `Позовите снова с limit (до ${CHANNEL_AGENTS_MAX_PAGE_SIZE}) и продолжайте по nextCursor, пока он не станет пустым.`,
240
+ })),
241
+ }),
242
+ defineTool({
243
+ name: 'besales_agent_get',
244
+ enhanceResponse: withAgentModelNotice,
245
+ sideEffect: 'none',
246
+ requiredFields: ['agent_id'],
247
+ // Ответ — снимок агента для замещающей правки: ПОЛНЫЙ базовый промпт (до 100 000 символов, то есть до ~200 КБ
248
+ // кириллицей) и конфигурации. Сузить его нечем, а общий порог отказа снимков отвечал бы отказом на штатном
249
+ // агенте с длинным промптом.
250
+ resultPolicy: {
251
+ neverTruncate: true,
252
+ maxResultBytes: 400_000,
253
+ narrowingHint: 'Параметров для сужения у этого снимка нет: промпт и конфигурации агента читаются целиком. Если он не помещается даже в свой потолок, сократите базовый промпт агента.',
254
+ },
255
+ schema: z.object({
256
+ agent_id: uuidSchema,
257
+ }),
258
+ createRequest: (input) => ({
259
+ method: 'GET',
260
+ path: `/agents/${input.agent_id}`,
261
+ }),
262
+ }),
263
+ defineTool({
264
+ name: 'besales_agent_readiness_get',
265
+ sideEffect: 'none',
266
+ closedWorld: true,
267
+ requiredFields: ['agent_id'],
268
+ // Вердикт считается по КАЖДОЙ привязке, и усечённый список привязок выдал бы непроверенные каналы за
269
+ // проверенные. Сузить ответ нечем: размер растёт с числом каналов агента (десятки привязок с адресными
270
+ // пунктами missing[] по-русски), поэтому потолок свой и заведомо выше общего.
271
+ resultPolicy: {
272
+ neverTruncate: true,
273
+ maxResultBytes: 400_000,
274
+ narrowingHint: 'Параметров для сужения у проверки готовности нет: она считается по всем привязкам агента сразу. Если ответ не помещается даже в свой потолок, у агента аномально много привязок к каналам — уберите лишние.',
275
+ },
276
+ schema: z.object({ agent_id: uuidSchema }).strict(),
277
+ createRequest: (input) => ({
278
+ method: 'GET',
279
+ path: `/agents/${input.agent_id}/readiness`,
280
+ }),
281
+ enhanceResponse: (response) => withInstructions(response, [
282
+ 'Сейчас готовность всегда отвечает NOT_PROVEN: API не фиксирует версии зависимых настроек (поведение канала, маршрутизация, триггеры, навыки, содержимое знаний, ICP, переменные, пост-обработчики, настройки привязки агента к каналу), и повтор песочницы этого не снимает. Не обещай пользователю READY.',
283
+ 'Ответ двухуровневый. Верхний missing — все шаги: сначала пункты агента (промпт, ICP, версия, пространства знаний), затем пункты каждой привязки с названием канала; bindings[].missing — те же шаги одного канала. Верхние gates — сводка по каналу песочницы (sandboxChannelId), а готовность считается по КАЖДОЙ привязке. Выполни адресные пункты обоих уровней и перечитай; promptAndAgentCurrent=true означает только актуальность промпта и записи агента.',
284
+ 'passRate не является quality. Нужны COMPLETED, свежая конфигурация, findings и qualityRate не ниже policy.minQualityRate; прогон по кандидату промпта готовность не доказывает. Проверки ожиданий сценариев — отдельная ось: checksRate — доля пройденных проверок (сумма по сценариям прогона), пусто — у реплик нет машинных ожиданий; нужно не ниже policy.minChecksRate. Пункт о проверках закрывается ожиданиями, но добавление expectations вердикт не меняет.',
285
+ 'crmAuthorization=NOT_EVALUATED не подтверждает живой доступ к CRM; проверяй его отдельной проверкой подключения, если CRM нужна сценарию.',
286
+ ]),
287
+ }),
288
+ defineTool({
289
+ name: 'besales_agent_variable_overrides_get',
290
+ sideEffect: 'none',
291
+ requiredFields: ['platform_id', 'agent_id'],
292
+ // Снимок для замещающей записи: besales_agent_update меняет набор перезаписей ЦЕЛИКОМ.
293
+ //
294
+ // ⛔ Сузить его НЕЧЕМ — набор неделим по построению, и отказ здесь был единственным
295
+ // отказом во всём мосту БЕЗ ВЫХОДА: не помещается — значит агента нельзя безопасно
296
+ // обновить вовсе. Поэтому свой потолок вместо общего плюс сказанное вслух, что делать.
297
+ resultPolicy: {
298
+ neverTruncate: true,
299
+ maxResultBytes: 400_000,
300
+ narrowingHint: 'Этот снимок неделим: он вход замещающей записи besales_agent_update. Если он не помещается даже в свой потолок, у агента аномально много перезаписей — сократите их набор, иначе безопасное обновление агента невозможно.',
301
+ },
302
+ schema: z.object({
303
+ platform_id: uuidSchema,
304
+ agent_id: uuidSchema,
305
+ }),
306
+ createRequest: (input) => ({
307
+ method: 'GET',
308
+ path: `/agents/${input.agent_id}/variable-overrides`,
309
+ }),
310
+ enhanceResponse: (response) => withInstructions(response, [
311
+ 'Read-only: текущие unified variable overrides v2-агента.',
312
+ 'Читай ПЕРЕД besales_agent_update: поля agent_workspace_variable_overrides / agent_variable_overrides заменяют набор ЦЕЛИКОМ (delete отсутствующих + upsert присланных). Чтобы не затереть уже выставленную перезапись (напр. registration_link), переотправь её вместе с новыми.',
313
+ 'Пустой набор означает, что агент наследует defaultValue переменных.',
314
+ ]),
315
+ }),
316
+ defineTool({
317
+ name: 'besales_getcourse_departments_list',
318
+ sideEffect: 'none',
319
+ requiredFields: ['platform_id'],
320
+ schema: z.object({
321
+ platform_id: uuidSchema,
322
+ }),
323
+ createRequest: (input) => ({
324
+ method: 'GET',
325
+ path: `/channels/${input.platform_id}/getcourse/departments`,
326
+ }),
327
+ enhanceResponse: (response) => withInstructions(response, [
328
+ 'Живой список отделов GetCourse Inbox школы ({ id, title }): спрашивается у школы в момент вызова.',
329
+ 'department.id — значение target_department_id для маршрутизации диалогов по отделам.',
330
+ 'Записать выбранный отдел: besales_platform_settings_update с getcourse_settings.target_department_id. Запись идёт слиянием и соседние настройки не трогает; null выключает маршрутизацию.',
331
+ 'Отдел ЖИВЫХ ЛЮДЕЙ — отдельная настройка getcourse_settings.operator_department_id: туда уезжает диалог при передаче человеку, null выключает передачу. Отделы обязаны различаться: одинаковые значения бэкенд отвергает, иначе авто-маршрутизация клала бы диалог прямо в очередь сотрудников.',
332
+ 'Пустой массив означает, что отделов у школы нет. Сбой обращения к школе приходит ошибкой 502, пустым списком он не притворяется.',
333
+ ]),
334
+ }),
335
+ defineTool({
336
+ name: 'besales_getcourse_wave_import',
337
+ sideEffect: 'spend',
338
+ requiredFields: ['platform_id', 'crm_id', 'channels', 'contacts'],
339
+ defaultWorkspaceId: true,
340
+ schema: z
341
+ .object({
342
+ workspace_id: uuidSchema.optional(),
343
+ platform_id: uuidSchema,
344
+ crm_id: uuidSchema,
345
+ launch_type: z.enum(['status', 'board', 'group']).optional(),
346
+ channels: waveChannelsSchema,
347
+ funnel: waveFunnelSchema.optional(),
348
+ segment: z.string().max(200).optional(),
349
+ // Предел 4000 совпадает с сервером и опубликованной схемой инструмента.
350
+ segment_description: z.string().max(BROADCAST_SEGMENT_DESCRIPTION_MAX_LENGTH).optional(),
351
+ cohort_label: z.string().max(200).optional(),
352
+ mailing_variant_id: uuidSchema.optional(),
353
+ message_variant: z.string().max(120).optional(),
354
+ found_count: z.number().int().min(0).max(10_000_000).optional(),
355
+ product_code: z
356
+ .string()
357
+ .trim()
358
+ .max(100)
359
+ .regex(/^[^\s/]+$/, 'product_code: без пробелов и «/»')
360
+ .optional(),
361
+ product_name: z.string().max(200).optional(),
362
+ deal_fields: z
363
+ .record(z.string(), z.union([z.string(), z.number(), z.boolean(), z.null()]))
364
+ .optional(),
365
+ pipeline_id: z.number().int().nonnegative().optional(),
366
+ status_id: z.number().int().nonnegative().optional(),
367
+ group_id: z.number().int().nonnegative().optional(),
368
+ contacts: z
369
+ .array(z.object({
370
+ contact_id: z.string(),
371
+ deal_id: z.string().optional(),
372
+ transport: z.string().optional(),
373
+ dialog_id: z.string().optional(),
374
+ email: z.string().email().max(320).optional(),
375
+ phone: z.string().max(64).optional(),
376
+ first_name: z.string().max(200).optional(),
377
+ last_name: z.string().max(200).optional(),
378
+ lead_last_active_at: z.string().datetime({ offset: true }).optional(),
379
+ segment_entered_at: z.string().datetime({ offset: true }).optional(),
380
+ }))
381
+ .min(1)
382
+ .max(500),
383
+ confirm: z.boolean().optional(),
384
+ check_messenger_presence: z.boolean().optional(),
385
+ dedup_window_days: z.number().int().min(0).max(365).optional(),
386
+ dedup_scope: z.enum(['any', 'same_funnel']).optional(),
387
+ preflight: z
388
+ .object({
389
+ enabled: z.boolean().optional(),
390
+ product_title: z.string().max(500).optional(),
391
+ product_match_key: z.string().max(500).optional(),
392
+ offer_id: z.number().int().nonnegative().optional(),
393
+ other_product_active_deal_mode: z.enum(['warn', 'block', 'ignore']).optional(),
394
+ })
395
+ .optional(),
396
+ })
397
+ .refine((input) => input.segment_description === undefined || Boolean(input.segment?.trim()), {
398
+ message: 'segment_description требует непустой segment',
399
+ path: ['segment_description'],
400
+ }),
401
+ createRequest: (input) => ({
402
+ method: 'POST',
403
+ path: `/workspaces/${input.workspace_id}/getcourse-waves/import`,
404
+ body: compactObject({
405
+ channelId: input.platform_id,
406
+ crmId: input.crm_id,
407
+ launchType: input.launch_type,
408
+ transports: input.channels,
409
+ funnel: input.funnel,
410
+ segment: input.segment,
411
+ segmentDescription: input.segment_description,
412
+ cohortLabel: input.cohort_label,
413
+ mailingVariantId: input.mailing_variant_id,
414
+ messageVariant: input.message_variant,
415
+ foundCount: input.found_count,
416
+ productCode: input.product_code,
417
+ productName: input.product_name,
418
+ dealFields: input.deal_fields,
419
+ pipelineId: input.pipeline_id,
420
+ statusId: input.status_id,
421
+ groupId: input.group_id,
422
+ dedupWindowDays: input.dedup_window_days,
423
+ dedupScope: input.dedup_scope,
424
+ contacts: input.contacts?.map((c) => compactObject({
425
+ getcourseUserId: c.contact_id,
426
+ dealId: c.deal_id,
427
+ transport: c.transport,
428
+ dialogId: c.dialog_id,
429
+ email: c.email,
430
+ phone: c.phone,
431
+ firstName: c.first_name,
432
+ lastName: c.last_name,
433
+ leadLastActiveAt: c.lead_last_active_at,
434
+ segmentEnteredAt: c.segment_entered_at,
435
+ })),
436
+ confirm: input.confirm,
437
+ checkMessengerPresence: input.check_messenger_presence,
438
+ preflight: input.preflight
439
+ ? compactObject({
440
+ enabled: input.preflight.enabled,
441
+ productTitle: input.preflight.product_title,
442
+ productMatchKey: input.preflight.product_match_key,
443
+ offerId: input.preflight.offer_id,
444
+ otherProductActiveDealMode: input.preflight.other_product_active_deal_mode,
445
+ })
446
+ : undefined,
447
+ }),
448
+ }),
449
+ enhanceResponse: (response) => withInstructions(response, [
450
+ 'Запуск outbound-волны GetCourse: Шаг 1 (импорт контактов + пин сделок) → Шаг 2 (отправка). По умолчанию launch_type=status (простановка полей → «Процесс» GetCourse шлёт welcome).',
451
+ 'БЕЗ confirm=true — DRY-RUN: вернётся { kind: "PREVIEW", totalContacts, withDeal, mode, channels, parseErrors, sample }, НИЧЕГО не отправляется и не пишется. Покажи summary пользователю и запускай реально только с confirm=true.',
452
+ 'channels — значения GetCourseTransport: "VK" | "Telegram" | "MAX" | "SMS" | "Email" | "Site" (для аналитики «Канал волны»).',
453
+ 'funnel — канонический ключ/название БИЗНЕС-воронки. Не передавай сюда имя файла, batch/resend или дату запуска. cohort_label — человекочитаемое имя конкретного запуска волны.',
454
+ 'contacts[].segment_entered_at — точный ISO-момент попадания контакта в сегмент. contacts[].lead_last_active_at — только legacy/последняя активность; не используй её как замену segment_entered_at.',
455
+ 'launch_type=status требует deal_fields = { "<числовой fieldId GetCourse>": значение } (одинаковые на всю волну). board требует pipeline_id+status_id, group — group_id.',
456
+ 'preflight (default enabled=true) — pre-send гейт перед каждой строкой Шага 2: блокирует контакт, если уже есть активная/оплаченная сделка по этому продукту или проверка GetCourse упала. Передавай product_title/product_match_key или funnel; если контекста продукта нет вообще, backend отклонит запуск до создания job.',
457
+ 'confirm=true ставит волну в очередь и СРАЗУ возвращает { kind: "STARTED", waveJobId, mode, totalContacts, parseErrors }: HTTP не ждёт ни одной фазы. Опрашивай waveJobId через besales_getcourse_wave_status до ТЕРМИНАЛЬНОГО статуса — COMPLETED или FAILED (оба терминальны; на FAILED прекращай поллинг, повторный опрос ничего не изменит).',
458
+ 'check_messenger_presence (default TRUE): отсеивает контакты без мессенджера (Telegram/Max/VK) — они не импортируются и не уходят в волну. Проверка = API-вызов на каждый контакт (кроме тех, о ком свежий ответ школы уже есть), но идёт ВНУТРИ джоба, а не в HTTP-запросе, поэтому большая когорта запуск не роняет по таймауту; число отсеянных читается из preflight.noMessengerSkipped в статусе. Выключать её стоит только когда заранее прогнан backfill POST /api/v2/.../chat-users/import/getcourse-refresh-messengers.',
459
+ 'dedup_window_days (default 30, 0–365) и dedup_scope (default any | same_funnel): any учитывает любую прошлую волну, same_funnel — ту же каноническую бизнес-воронку (legacy batch/resend-суффиксы игнорируются). 0 = выключить дедуп полностью — только для осознанного повторного запуска.',
460
+ WAVE_WELCOME_PAUSE_INSTRUCTION,
461
+ 'В dry-run поле ledByOtherSchoolChannel.count считает людей, чей живой адрес ведёт другой канал той же школы. Адрес на канале загрузки для них не создаётся. Строка получателя и новая строка сделки записываются за ведущим каналом человека. Существующая строка сделки остаётся за каналом первой волны; деньги и зачёт оплаты читают канал строки сделки.',
462
+ ]),
463
+ }),
464
+ defineTool({
465
+ name: 'besales_getcourse_wave_status',
466
+ sideEffect: 'none',
467
+ requiredFields: ['job_id'],
468
+ defaultWorkspaceId: true,
469
+ schema: z.object({
470
+ workspace_id: uuidSchema.optional(),
471
+ job_id: uuidSchema,
472
+ }),
473
+ createRequest: (input) => ({
474
+ method: 'GET',
475
+ path: `/workspaces/${input.workspace_id}/getcourse-waves/${input.job_id}`,
476
+ }),
477
+ enhanceResponse: (response) => withInstructions(response, [
478
+ 'Статус ОДНОГО durable-джоба волны: status (PROCESSING | COMPLETED | FAILED), phase (PREFLIGHT | CONTACT_IMPORT | DEAL_MOVE — фаза, на которой джоб сейчас), mode (STATUS | BOARD | GROUP), счётчики total/processed/success/failed/blocked/skipped, errorReasons (первые построчные причины), metadata (created/unverified/welcomed/welcomeSkipped/welcomeSkippedAgentPaused/welcomeFailed/segmentWriteFailed), welcomeSkippedAgentPaused (count/labels/rows) и preflight-агрегаты.',
479
+ WAVE_WELCOME_PAUSE_INSTRUCTION,
480
+ 'Терминальных статусов ДВА: COMPLETED и FAILED. FAILED — волна остановлена целиком (сорвался предохранитель по доле ошибок либо джоб упал), остаток строк НЕ обработан; поллинг прекращай, автоматически не перезапускай — часть внешних эффектов уже произведена.',
481
+ 'blocked — это НЕ ошибка: контакт намеренно не тронут (обычно уже есть активная или оплаченная сделка по тому же продукту). Считать blocked неудачей — вводить оператора в заблуждение; неудачи только в failed + errorReasons (нет сделки, поле вне «Предложения» заказа и т.п.).',
482
+ 'phase объясняет, на чём стоит волна: PREFLIGHT — проверки до отправки, CONTACT_IMPORT — импорт контактов и пин сделок, DEAL_MOVE — собственно Шаг 2. Долгий PROCESSING в DEAL_MOVE нормален: между строками стоят паузы.',
483
+ 'preflight.blocked/errors — контакты, которым НЕ отправляли; BroadcastRecipient для них не создаётся, поэтому received в аналитике не раздувается. preflight.warnings — число ПРОВЕРОК, несущих хотя бы одно предупреждение, а не число предупреждений.',
484
+ 'ledByOtherSchoolChannel.count — число людей, которых ведёт другой канал той же школы. Строка получателя и новая строка сделки записываются за ведущим каналом человека. Существующая строка сделки остаётся за каналом первой волны; деньги и зачёт оплаты читают канал строки сделки.',
485
+ ]),
486
+ }),
487
+ defineTool({
488
+ name: 'besales_getcourse_contacts_import',
489
+ sideEffect: 'write',
490
+ // Имя проверки не выводится из имени запуска: у статуса другой корень (`contact_import`).
491
+ verifyWithToolName: 'besales_getcourse_contact_import_status',
492
+ requiredFields: ['platform_id', 'contacts'],
493
+ defaultWorkspaceId: true,
494
+ schema: z
495
+ .object({
496
+ workspace_id: uuidSchema.optional(),
497
+ platform_id: uuidSchema,
498
+ // ⛔ У строки — только размер. Смысл (цифры в номерах, адрес почты, известный транспорт)
499
+ // сервер проверяет ПОСТРОЧНО и возвращает в parseErrors; проверка смысла здесь отвергала
500
+ // весь список из-за одной ячейки, и годные контакты не заводились вовсе.
501
+ contacts: z
502
+ .array(z
503
+ .object({
504
+ contact_id: z.string().min(1).max(20),
505
+ deal_id: z.string().max(20).optional(),
506
+ dialog_id: z.string().max(20).optional(),
507
+ transport: z.string().max(64).optional(),
508
+ email: z.string().max(320).optional(),
509
+ phone: z.string().max(64).optional(),
510
+ first_name: z.string().max(200).optional(),
511
+ last_name: z.string().max(200).optional(),
512
+ })
513
+ .strict())
514
+ .min(1)
515
+ .max(5000),
516
+ check_messenger_presence: z.boolean().optional(),
517
+ // Значения проверяет сервер (регистр не значим); здесь — только размер: транспортов десять.
518
+ channels: z.array(z.string().min(1).max(64)).max(WAVE_TRANSPORTS.length).optional(),
519
+ })
520
+ .strict(),
521
+ createRequest: (input) => ({
522
+ method: 'POST',
523
+ path: `/workspaces/${input.workspace_id}/getcourse-contact-imports`,
524
+ body: compactObject({
525
+ channelId: input.platform_id,
526
+ contacts: input.contacts.map((contact) => compactObject({
527
+ getcourseUserId: contact.contact_id,
528
+ dealId: contact.deal_id,
529
+ dialogId: contact.dialog_id,
530
+ transport: contact.transport,
531
+ email: contact.email,
532
+ phone: contact.phone,
533
+ firstName: contact.first_name,
534
+ lastName: contact.last_name,
535
+ })),
536
+ checkMessengerPresence: input.check_messenger_presence,
537
+ transports: input.channels,
538
+ }),
539
+ }),
540
+ enhanceResponse: (response) => withInstructions(response, [
541
+ 'Импорт НИЧЕГО не отправляет: он только заводит или обновляет контакты школы в базе платформы, чтобы по ним можно было запускать отправку.',
542
+ 'Импорт идёт фоном. Ответ: jobId — ключ опроса; queuedContacts — сколько контактов поставлено; parseErrors — строки, отбракованные разбором (position — номер контакта в присланном списке, message — причина: номер не цифрами, неверная почта, неизвестный транспорт, дубль). Одна битая строка НЕ отвергает список — остальные ставятся; пустая ячейка — это «значения нет», а не ошибка. Опрашивай besales_getcourse_contact_import_status с этим jobId до терминального статуса.',
543
+ 'Уже существующий контакт не дублируется — опознание идёт по ключам склейки. Повторный импорт того же списка безопасен.',
544
+ 'check_messenger_presence (по умолчанию true): контакт, до которого не достать, не заводится. Проверка — обращение к школе на каждый контакт, кроме тех, о ком свежий ответ школы уже есть: ответ о человеке без карточки помнится (срок — настройка канала reachability_ttl_days) и повторно не спрашивается. На тысячах строк импорт идёт заметно дольше.',
545
+ 'channels — каналы, которыми запуск собирается писать (SITE, EMAIL, SMS, TELEGRAM, FACEBOOK, VK, CHATIUM, WHATSAPP, VIBER, MAX; регистр не значим). Судятся только каналы, включённые у канала школы. Названы — человек не заводится, лишь если недостижим НИ ОДНИМ из них: мессенджеры по привязкам школы, почта по адресу у школы, SMS и WhatsApp по номеру; транспорт, указанный в строке контакта, считается доказанным. Не названы — нужен хотя бы один привязанный мессенджер Telegram/MAX/VK. Названы, но ни один не включён у канала школы — импорт отвергается (422 LAUNCH_CHANNELS_NOT_AVAILABLE): этим подключением никого не достать.',
546
+ '409 — в воркспейсе уже идёт другой импорт или волна (гейт «один активный импорт» общий), это не сбой: дождись завершения и повтори. 400 — ни одной годной строки, причины в parseErrors.',
547
+ ]),
548
+ }),
549
+ defineTool({
550
+ name: 'besales_getcourse_contact_import_status',
551
+ sideEffect: 'none',
552
+ requiredFields: ['job_id'],
553
+ defaultWorkspaceId: true,
554
+ schema: z
555
+ .object({
556
+ workspace_id: uuidSchema.optional(),
557
+ job_id: uuidSchema,
558
+ })
559
+ .strict(),
560
+ createRequest: (input) => ({
561
+ method: 'GET',
562
+ path: `/workspaces/${input.workspace_id}/getcourse-contact-imports/${input.job_id}`,
563
+ }),
564
+ enhanceResponse: (response) => withInstructions(response, [
565
+ 'status: PROCESSING — идёт; COMPLETED — все строки обработаны без отказов; PARTIAL — завершён, но у части строк отказ (сверь failedCount и errors); FAILED — импорт остановлен аварией, строки, до которых он не дошёл, не обработаны; CANCELLED — остановлен. Терминальны все, кроме PROCESSING.',
566
+ 'successCount — заведено или обновлено; skippedNoMessenger — не заведены, потому что у контакта нет мессенджера (это не ошибка); failedCount — отказы строк, включая отбракованные разбором; errors — первые причины отказов.',
567
+ 'Инструмент читает только задачи импорта контактов: номер задачи другого вида даёт 404. Статус волны — besales_getcourse_wave_status.',
568
+ ]),
569
+ }),
570
+ defineTool({
571
+ name: 'besales_getcourse_wave_run_start',
572
+ sideEffect: 'spend',
573
+ requiredFields: ['platform_id', 'crm_id', 'channels', 'contacts'],
574
+ defaultWorkspaceId: true,
575
+ schema: z
576
+ .object({
577
+ workspace_id: uuidSchema.optional(),
578
+ platform_id: uuidSchema,
579
+ crm_id: uuidSchema,
580
+ launch_key: uuidSchema.optional(),
581
+ dry_run_hash: z
582
+ .string()
583
+ .regex(/^[0-9a-f]{64}$/)
584
+ .optional(),
585
+ launch_type: z.literal('status').optional(),
586
+ channels: waveChannelsSchema,
587
+ funnel: waveFunnelSchema.optional(),
588
+ segment: z.string().max(200).optional(),
589
+ segment_description: z.string().max(BROADCAST_SEGMENT_DESCRIPTION_MAX_LENGTH).optional(),
590
+ cohort_label: z.string().max(200).optional(),
591
+ mailing_variant_id: uuidSchema.optional(),
592
+ message_variant: z.string().max(120).optional(),
593
+ found_count: z.number().int().min(0).max(10_000_000).optional(),
594
+ product_code: z
595
+ .string()
596
+ .trim()
597
+ .max(100)
598
+ .regex(/^[^\s/]+$/, 'product_code: без пробелов и «/»')
599
+ .optional(),
600
+ product_name: z.string().max(200).optional(),
601
+ deal_fields: z
602
+ .record(z.string(), z.union([z.string(), z.number(), z.boolean(), z.null()]))
603
+ .optional(),
604
+ contacts: z.array(getCourseWaveContactSchema).min(1).max(5000),
605
+ confirm: z.boolean().optional(),
606
+ check_messenger_presence: z.boolean().optional(),
607
+ dedup_window_days: z.number().int().min(0).max(365).optional(),
608
+ dedup_scope: z.enum(['any', 'same_funnel']).optional(),
609
+ preflight: z
610
+ .object({
611
+ enabled: z.boolean().optional(),
612
+ product_title: z.string().max(500).optional(),
613
+ product_match_key: z.string().max(500).optional(),
614
+ offer_id: z.number().int().nonnegative().optional(),
615
+ other_product_active_deal_mode: z.enum(['warn', 'block', 'ignore']).optional(),
616
+ })
617
+ .optional(),
618
+ chunk_size: z.number().int().min(1).max(500).optional(),
619
+ explicit_confirmation: z.literal(true).optional(),
620
+ confirmation_reason: z.string().optional(),
621
+ })
622
+ .refine((input) => input.segment_description === undefined || Boolean(input.segment?.trim()), {
623
+ message: 'segment_description требует непустой segment',
624
+ path: ['segment_description'],
625
+ })
626
+ .refine((input) => !input.confirm || input.explicit_confirmation === true, {
627
+ message: 'Реальный запуск требует explicit_confirmation=true: отправка волны необратима',
628
+ path: ['explicit_confirmation'],
629
+ })
630
+ .refine((input) => !input.confirm || Boolean(input.launch_key), {
631
+ message: 'confirm=true требует стабильный launch_key UUID',
632
+ path: ['launch_key'],
633
+ })
634
+ .refine((input) => !input.confirm || Boolean(input.dry_run_hash), {
635
+ message: 'confirm=true требует dry_run_hash из предыдущего dry-run',
636
+ path: ['dry_run_hash'],
637
+ }),
638
+ createRequest: (input) => ({
639
+ method: 'POST',
640
+ path: `/workspaces/${input.workspace_id}/getcourse-wave-runs`,
641
+ body: compactObject({
642
+ channelId: input.platform_id,
643
+ crmId: input.crm_id,
644
+ launchKey: input.launch_key,
645
+ dryRunHash: input.dry_run_hash,
646
+ launchType: input.launch_type,
647
+ transports: input.channels,
648
+ funnel: input.funnel,
649
+ segment: input.segment,
650
+ segmentDescription: input.segment_description,
651
+ cohortLabel: input.cohort_label,
652
+ mailingVariantId: input.mailing_variant_id,
653
+ messageVariant: input.message_variant,
654
+ foundCount: input.found_count,
655
+ productCode: input.product_code,
656
+ productName: input.product_name,
657
+ dealFields: input.deal_fields,
658
+ contacts: input.contacts?.map(mapGetCourseWaveContact),
659
+ confirm: input.confirm,
660
+ checkMessengerPresence: input.check_messenger_presence,
661
+ dedupWindowDays: input.dedup_window_days,
662
+ dedupScope: input.dedup_scope,
663
+ preflight: input.preflight
664
+ ? compactObject({
665
+ enabled: input.preflight.enabled,
666
+ productTitle: input.preflight.product_title,
667
+ productMatchKey: input.preflight.product_match_key,
668
+ offerId: input.preflight.offer_id,
669
+ otherProductActiveDealMode: input.preflight.other_product_active_deal_mode,
670
+ })
671
+ : undefined,
672
+ chunkSize: input.chunk_size,
673
+ }),
674
+ }),
675
+ enhanceResponse: (response) => withInstructions(response, [
676
+ 'Durable-прогон сам режет когорту на куски и отправляет их последовательно, сам ждёт освобождения лока импорта и переживает рестарт сервиса. Опрашивай ОДИН прогон через besales_getcourse_wave_run_status, не собирай волну вручную.',
677
+ WAVE_RUN_WELCOME_PAUSE_INSTRUCTION,
678
+ 'mailing_variant_id — ID именованного «Варианта рассылки» из besales_broadcast_mailing_variant_list/create. Backend tenant-scoped проверяет активность и сохраняет связь для фильтра аналитики.',
679
+ 'БЕЗ confirm=true — DRY-RUN: возвращается проверка, но прогон НЕ создаётся и ничего не отправляется.',
680
+ 'Реальный запуск требует confirm=true, explicit_confirmation=true, launch_key и dry_run_hash из последнего dry-run. Сгенерируй launch_key один раз на намерение и повторяй ТОТ ЖЕ ключ при сетевом ретрае.',
681
+ 'rowsWithoutSegmentEntry — сколько СТРОК присланного списка пришли без contacts[].segment_entered_at: это строки, а не люди. Таким строкам прогон ставит момент своего запуска, и аналитика по дате входа в сегмент положит их в день запуска, а не в день настоящего входа. Если в dry-run число больше нуля — ОСТАНОВИСЬ: назови его человеку и спроси, запускать так или сначала дополнить даты. null — число не считалось (прогон создан до появления счётчика).',
682
+ 'Поддерживается ТОЛЬКО launch_type=status. Для board/group используй старый besales_getcourse_wave_import.',
683
+ 'HTTP 409 означает, что на платформе уже идёт активный прогон: в ответе есть его runId — не создавай новую волну, опрашивай этот runId.',
684
+ 'Статус прогона отдаёт только агрегаты. Полный построчный разбор получай через besales_getcourse_wave_run_contacts с курсорной пагинацией.',
685
+ 'Dry-run показывает ledByOtherSchoolChannel.count: столько людей ведёт другой канал той же школы. Адрес на канале загрузки не создаётся. Строка получателя и новая строка сделки записываются за ведущим каналом человека. Существующая строка сделки остаётся за каналом первой волны; деньги и зачёт оплаты читают канал строки сделки.',
686
+ 'Исходы контактов: SENT — отправлено; SKIPPED — дедуп, нет мессенджера или preflight заблокировал; FAILED — построчная ошибка; UNKNOWN — исход НЕИЗВЕСТЕН: кусок был прерван, но отправка могла состояться.',
687
+ UNCERTAIN_WAVE_OUTCOME_INSTRUCTION,
688
+ ]),
689
+ }),
690
+ defineTool({
691
+ name: 'besales_getcourse_wave_run_status',
692
+ sideEffect: 'none',
693
+ requiredFields: ['run_id'],
694
+ defaultWorkspaceId: true,
695
+ schema: z.object({
696
+ workspace_id: uuidSchema.optional(),
697
+ run_id: uuidSchema,
698
+ }),
699
+ createRequest: (input) => ({
700
+ method: 'GET',
701
+ path: `/workspaces/${input.workspace_id}/getcourse-wave-runs/${input.run_id}`,
702
+ }),
703
+ enhanceResponse: (response) => withInstructions(response, [
704
+ 'Это единый агрегированный статус durable-прогона: он сам последовательно обрабатывает куски, ждёт лок импорта и продолжает после рестарта сервиса. Не собирай и не запускай куски вручную.',
705
+ WAVE_RUN_WELCOME_PAUSE_INSTRUCTION,
706
+ 'Статус содержит только агрегаты. Для полного построчного разбора используй besales_getcourse_wave_run_contacts с курсорной пагинацией.',
707
+ 'ledByOtherSchoolChannel.count — число людей, которых ведёт другой канал той же школы. Строка получателя и новая строка сделки записываются за ведущим каналом человека. Существующая строка сделки остаётся за каналом первой волны; деньги и зачёт оплаты читают канал строки сделки.',
708
+ 'SENT — отправлено; SKIPPED — дедуп, нет мессенджера или блокировка preflight; FAILED — построчная ошибка; UNKNOWN — исход НЕИЗВЕСТЕН после прерванного куска, отправка могла состояться.',
709
+ UNCERTAIN_WAVE_OUTCOME_INSTRUCTION,
710
+ ]),
711
+ }),
712
+ defineTool({
713
+ name: 'besales_getcourse_wave_run_contacts',
714
+ carriesCustomerData: true,
715
+ sideEffect: 'none',
716
+ requiredFields: ['run_id'],
717
+ defaultWorkspaceId: true,
718
+ // Курсорная выдача уже работает; вдобавок построчный разбор волны — основание для
719
+ // переотправки, и усечённый он привёл бы к повторной отправке живым людям.
720
+ resultPolicy: {
721
+ neverTruncate: true,
722
+ narrowingHint: 'Уменьшите limit, фильтруйте по status и идите по страницам через cursor.',
723
+ },
724
+ schema: z.object({
725
+ workspace_id: uuidSchema.optional(),
726
+ run_id: uuidSchema,
727
+ status: z.enum(['PENDING', 'CLAIMED', 'SENT', 'SKIPPED', 'FAILED', 'UNKNOWN']).optional(),
728
+ cursor: uuidSchema.optional(),
729
+ limit: z.number().int().min(1).max(500).optional(),
730
+ }),
731
+ createRequest: (input) => {
732
+ const params = new URLSearchParams();
733
+ if (input.status)
734
+ params.set('status', input.status);
735
+ if (input.cursor)
736
+ params.set('cursor', input.cursor);
737
+ if (input.limit !== undefined)
738
+ params.set('limit', String(input.limit));
739
+ const query = params.toString();
740
+ return {
741
+ method: 'GET',
742
+ path: `/workspaces/${input.workspace_id}/getcourse-wave-runs/${input.run_id}/contacts${query ? `?${query}` : ''}`,
743
+ };
744
+ },
745
+ enhanceResponse: (response) => withInstructions(response, [
746
+ 'Полный построчный разбор durable-прогона. Продолжай курсорную пагинацию по nextCursor до конца; агрегированный besales_getcourse_wave_run_status строк контактов не возвращает.',
747
+ 'Фильтр status: PENDING | CLAIMED | SENT | SKIPPED | FAILED | UNKNOWN. SKIPPED означает дедуп, отсутствие мессенджера или блокировку preflight; FAILED — построчную ошибку.',
748
+ 'UNKNOWN означает, что исход НЕИЗВЕСТЕН: кусок был прерван, но отправка могла состояться.',
749
+ WAVE_RUN_CONTACT_IDS_INSTRUCTION,
750
+ UNCERTAIN_WAVE_OUTCOME_INSTRUCTION,
751
+ ]),
752
+ }),
753
+ defineTool({
754
+ name: 'besales_getcourse_wave_run_cancel',
755
+ sideEffect: 'write',
756
+ requiredFields: ['run_id'],
757
+ defaultWorkspaceId: true,
758
+ schema: z.object({
759
+ workspace_id: uuidSchema.optional(),
760
+ run_id: uuidSchema,
761
+ }),
762
+ createRequest: (input) => ({
763
+ method: 'POST',
764
+ path: `/workspaces/${input.workspace_id}/getcourse-wave-runs/${input.run_id}/cancel`,
765
+ }),
766
+ enhanceResponse: (response) => withInstructions(response, [
767
+ 'Отмена запрещает прогону заявлять новые куски И останавливает идущий кусок: внешних вызовов он больше не делает, оставшиеся в куске люди сообщение не получают. Уже отправленное вернуть нельзя. После отмены опроси besales_getcourse_wave_run_status.',
768
+ 'Для полного построчного результата используй besales_getcourse_wave_run_contacts с курсорной пагинацией; статус прогона отдаёт только агрегаты.',
769
+ 'UNKNOWN — исход НЕИЗВЕСТЕН после прерванного куска, отправка могла состояться.',
770
+ UNCERTAIN_WAVE_OUTCOME_INSTRUCTION,
771
+ ]),
772
+ }),
773
+ defineTool({
774
+ name: 'besales_getcourse_wave_run_batch_start',
775
+ sideEffect: 'spend',
776
+ requiredFields: ['batch_key', 'platform_id', 'crm_id', 'segments'],
777
+ defaultWorkspaceId: true,
778
+ schema: z
779
+ .object({
780
+ workspace_id: uuidSchema.optional(),
781
+ batch_key: z.string(),
782
+ platform_id: uuidSchema,
783
+ crm_id: uuidSchema,
784
+ segments: z
785
+ .array(z
786
+ .object({
787
+ // Имя поля обязано совпадать с WaveLaunchParamsDto.launchType: у бэкенда
788
+ // включён forbidNonWhitelisted, поэтому лишний ключ роняет весь батч в 400.
789
+ launch_type: z.literal('status').optional(),
790
+ contacts: z.array(getCourseWaveContactSchema).min(1).max(2000),
791
+ channels: waveChannelsSchema,
792
+ deal_fields: z.record(z.string(), z.union([z.string(), z.number(), z.boolean(), z.null()])),
793
+ funnel: waveFunnelSchema.optional(),
794
+ segment: z.string().max(200).optional(),
795
+ segment_description: z
796
+ .string()
797
+ .max(BROADCAST_SEGMENT_DESCRIPTION_MAX_LENGTH)
798
+ .optional(),
799
+ cohort_label: z.string().max(200).optional(),
800
+ mailing_variant_id: uuidSchema.optional(),
801
+ message_variant: z.string().max(120).optional(),
802
+ found_count: z.number().int().min(0).max(10_000_000).optional(),
803
+ product_code: z
804
+ .string()
805
+ .trim()
806
+ .max(100)
807
+ .regex(/^[^\s/]+$/, 'product_code: без пробелов и «/»')
808
+ .optional(),
809
+ product_name: z.string().max(200).optional(),
810
+ preflight: z
811
+ .object({
812
+ enabled: z.boolean().optional(),
813
+ product_title: z.string().max(500).optional(),
814
+ product_match_key: z.string().max(500).optional(),
815
+ offer_id: z.number().int().nonnegative().optional(),
816
+ other_product_active_deal_mode: z.enum(['warn', 'block', 'ignore']).optional(),
817
+ })
818
+ .optional(),
819
+ check_messenger_presence: z.boolean().optional(),
820
+ dedup_window_days: z.number().int().min(1).max(365).optional(),
821
+ dedup_scope: z.enum(['any', 'same_funnel']).optional(),
822
+ chunk_size: z.number().int().min(1).max(500).optional(),
823
+ })
824
+ .strict()
825
+ .refine((segment) => segment.segment_description === undefined || Boolean(segment.segment?.trim()), {
826
+ message: 'segment_description требует непустой segment',
827
+ path: ['segment_description'],
828
+ }))
829
+ .min(1)
830
+ .max(10)
831
+ .refine((segments) => segments.reduce((total, segment) => total + segment.contacts.length, 0) <= 5000, {
832
+ message: 'В батче может быть не более 5000 контактов суммарно',
833
+ }),
834
+ confirm: z.boolean().optional(),
835
+ explicit_confirmation: z.literal(true).optional(),
836
+ confirmation_reason: z.string().optional(),
837
+ })
838
+ .refine((input) => !input.confirm || input.explicit_confirmation === true, {
839
+ message: 'Реальный запуск требует explicit_confirmation=true: отправка волны необратима',
840
+ path: ['explicit_confirmation'],
841
+ }),
842
+ createRequest: (input) => ({
843
+ method: 'POST',
844
+ path: `/workspaces/${input.workspace_id}/getcourse-wave-runs/batch`,
845
+ body: compactObject({
846
+ batchKey: input.batch_key,
847
+ channelId: input.platform_id,
848
+ crmId: input.crm_id,
849
+ segments: input.segments.map((segment) => compactObject({
850
+ launchType: segment.launch_type,
851
+ contacts: segment.contacts.map(mapGetCourseWaveContact),
852
+ transports: segment.channels,
853
+ dealFields: segment.deal_fields,
854
+ funnel: segment.funnel,
855
+ segment: segment.segment,
856
+ segmentDescription: segment.segment_description,
857
+ cohortLabel: segment.cohort_label,
858
+ mailingVariantId: segment.mailing_variant_id,
859
+ messageVariant: segment.message_variant,
860
+ foundCount: segment.found_count,
861
+ productCode: segment.product_code,
862
+ productName: segment.product_name,
863
+ preflight: segment.preflight
864
+ ? compactObject({
865
+ enabled: segment.preflight.enabled,
866
+ productTitle: segment.preflight.product_title,
867
+ productMatchKey: segment.preflight.product_match_key,
868
+ offerId: segment.preflight.offer_id,
869
+ otherProductActiveDealMode: segment.preflight.other_product_active_deal_mode,
870
+ })
871
+ : undefined,
872
+ checkMessengerPresence: segment.check_messenger_presence,
873
+ dedupWindowDays: segment.dedup_window_days,
874
+ dedupScope: segment.dedup_scope,
875
+ chunkSize: segment.chunk_size,
876
+ })),
877
+ confirm: input.confirm,
878
+ }),
879
+ }),
880
+ enhanceResponse: (response) => withInstructions(response, [
881
+ 'Очередь исполняет сегменты ПОСЛЕДОВАТЕЛЬНО: в один момент на платформе идёт только один прогон. Опрашивай ОДИН besales_getcourse_wave_run_batch_status, не собирай сегменты и их статусы вручную.',
882
+ WAVE_RUN_WELCOME_PAUSE_INSTRUCTION,
883
+ 'Для каждого сегмента mailing_variant_id — ID именованного «Варианта рассылки» из besales_broadcast_mailing_variant_list/create. Перед запуском backend повторно tenant-scoped проверяет активность всех вариантов под транзакционным lock.',
884
+ 'batch_key сгенерируй ОДИН раз на попытку и ПЕРЕИСПОЛЬЗУЙ при ретрае: это ключ идемпотентности. Повтор с тем же ключом вернёт существующий батч; новый ключ создаст ВТОРУЮ копию кампании, которую дедуп не остановит.',
885
+ 'Если повторить тот же batch_key с ИЗМЕНЁННЫМ составом сегментов, сервер вернёт HTTP 409 «ключ занят другим составом». Не меняй состав при ретрае — сначала прочитай статус существующего батча.',
886
+ 'HTTP 400 отклоняет батч до запуска, если контакт есть в двух сегментах, пересекается с незавершённым прогоном платформы или ему уже отправляли за последние 30 дней. В ответе будут пары contactId + сегменты либо конфликтующий runId; исправь состав, а не обходи проверку новым ключом.',
887
+ 'Два подряд отказавших сегмента ставят батч на паузу. Продолжай только через besales_getcourse_wave_run_batch_resume после проверки причин отказов.',
888
+ 'rowsWithoutSegmentEntry — сколько СТРОК пришли без contacts[].segment_entered_at: это строки, а не люди. В dry-run число стоит у каждого сегмента, в ответе запуска — сумма и разбивка segments[] по сегментам. Таким строкам прогон ставит момент своего запуска, и аналитика по дате входа в сегмент положит их в день запуска. Если хоть у одного сегмента число больше нуля — ОСТАНОВИСЬ: назови сегменты и числа человеку и спроси, запускать так или сначала дополнить даты. null — число не считалось (прогон создан до появления счётчика).',
889
+ UNCERTAIN_WAVE_OUTCOME_INSTRUCTION,
890
+ 'БЕЗ confirm=true — DRY-RUN: батч не создаётся и ничего не отправляется. Реальный запуск дополнительно требует MCP-only explicit_confirmation=true; это поле в HTTP-запрос не передаётся.',
891
+ ]),
892
+ }),
893
+ defineTool({
894
+ name: 'besales_getcourse_wave_run_batch_status',
895
+ sideEffect: 'none',
896
+ requiredFields: ['batch_id'],
897
+ defaultWorkspaceId: true,
898
+ schema: z.object({
899
+ workspace_id: uuidSchema.optional(),
900
+ batch_id: uuidSchema,
901
+ }),
902
+ createRequest: (input) => ({
903
+ method: 'GET',
904
+ path: `/workspaces/${input.workspace_id}/getcourse-wave-runs/batches/${input.batch_id}`,
905
+ }),
906
+ enhanceResponse: (response) => withInstructions(response, [
907
+ 'Это ЕДИНСТВЕННЫЙ агрегированный статус очереди: она запускает сегменты последовательно и держит только один прогон на платформу. Опрашивай этот batch-status, а не статусы сегментов вручную.',
908
+ WAVE_RUN_WELCOME_PAUSE_INSTRUCTION,
909
+ 'Если батч на паузе после двух подряд отказавших сегментов, сначала проверь причины, затем снимай паузу только besales_getcourse_wave_run_batch_resume.',
910
+ UNCERTAIN_WAVE_OUTCOME_INSTRUCTION,
911
+ ]),
912
+ }),
913
+ defineTool({
914
+ name: 'besales_getcourse_wave_run_batch_cancel',
915
+ sideEffect: 'write',
916
+ requiredFields: ['batch_id'],
917
+ defaultWorkspaceId: true,
918
+ schema: z.object({
919
+ workspace_id: uuidSchema.optional(),
920
+ batch_id: uuidSchema,
921
+ }),
922
+ createRequest: (input) => ({
923
+ method: 'POST',
924
+ path: `/workspaces/${input.workspace_id}/getcourse-wave-runs/batches/${input.batch_id}/cancel`,
925
+ }),
926
+ enhanceResponse: (response) => withInstructions(response, [
927
+ 'Отмена останавливает очередь батча; после неё опроси ОДИН besales_getcourse_wave_run_batch_status, не собирая сегменты вручную.',
928
+ UNCERTAIN_WAVE_OUTCOME_INSTRUCTION,
929
+ ]),
930
+ }),
931
+ defineTool({
932
+ name: 'besales_getcourse_wave_run_batch_resume',
933
+ sideEffect: 'spend',
934
+ requiredFields: ['batch_id'],
935
+ defaultWorkspaceId: true,
936
+ schema: z.object({
937
+ workspace_id: uuidSchema.optional(),
938
+ batch_id: uuidSchema,
939
+ }),
940
+ createRequest: (input) => ({
941
+ method: 'POST',
942
+ path: `/workspaces/${input.workspace_id}/getcourse-wave-runs/batches/${input.batch_id}/resume`,
943
+ }),
944
+ enhanceResponse: (response) => withInstructions(response, [
945
+ 'Снимает паузу батча, которую вызывают два подряд отказавших сегмента. После resume опрашивай ОДИН besales_getcourse_wave_run_batch_status: очередь продолжит сегменты последовательно, один прогон на платформу.',
946
+ UNCERTAIN_WAVE_OUTCOME_INSTRUCTION,
947
+ ]),
948
+ }),
949
+ defineTool({
950
+ name: 'besales_wazzup_templates_list',
951
+ sideEffect: 'none',
952
+ requiredFields: ['platform_id'],
953
+ defaultWorkspaceId: true,
954
+ schema: z.object({
955
+ workspace_id: uuidSchema.optional(),
956
+ platform_id: uuidSchema,
957
+ }),
958
+ createRequest: (input) => ({
959
+ method: 'GET',
960
+ path: `/workspaces/${input.workspace_id}/wazzup-waves/templates/${input.platform_id}`,
961
+ }),
962
+ enhanceResponse: (response) => withInstructions(response, [
963
+ 'Шаблоны WABA платформы Wazzup: templateGuid, title, status модерации Meta, bodyText, variableCount (сколько значений переменных нужно), usable + unsupportedReasons.',
964
+ 'В волне используй ТОЛЬКО шаблоны с usable=true. Остальные либо не одобрены Meta, либо содержат медиа-заголовок / переменную в URL-кнопке — там значения ушли бы не в те слоты.',
965
+ 'variableCount считается по МАКСИМАЛЬНОМУ индексу {{n}}: «{{1}} … {{2}} … {{1}}» — это 2 значения, а не 3.',
966
+ ]),
967
+ }),
968
+ defineTool({
969
+ name: 'besales_wazzup_wave_import',
970
+ sideEffect: 'spend',
971
+ requiredFields: ['platform_id', 'template_guid', 'contacts'],
972
+ defaultWorkspaceId: true,
973
+ schema: z
974
+ .object({
975
+ workspace_id: uuidSchema.optional(),
976
+ platform_id: uuidSchema,
977
+ template_guid: z.string().trim().max(100),
978
+ template_values: z.array(z.string()).max(20).optional(),
979
+ contacts: z
980
+ .array(z.object({
981
+ phone: z.string().trim().max(32),
982
+ name: z.string().max(200).optional(),
983
+ }))
984
+ .min(1)
985
+ .max(500),
986
+ funnel: z.string().max(200).optional(),
987
+ segment: z.string().max(200).optional(),
988
+ product_code: z
989
+ .string()
990
+ .trim()
991
+ .max(100)
992
+ .regex(/^[^\s/]+$/, 'product_code: без пробелов и «/»')
993
+ .optional(),
994
+ product_name: z.string().max(200).optional(),
995
+ cohort_label: z.string().max(200).optional(),
996
+ confirm: z.boolean().optional(),
997
+ /**
998
+ * Обязателен ВМЕСТЕ с confirm=true: волна необратима и платна (каждый WABA-шаблон
999
+ * тарифицируется Meta). Dry-run (без confirm) подтверждения не требует.
1000
+ *
1001
+ * ⚠️ Это гейт СЛОЯ МОСТА, а не серверного контракта: на сервер он не уходит вовсе.
1002
+ * Его назначение — не дать AI-оператору запустить платную рассылку мимоходом, одним
1003
+ * флагом. Тот же приём защищает удаление одной строки знаний; рассылка пятисот
1004
+ * платных шаблонов не может быть защищена слабее.
1005
+ */
1006
+ explicit_confirmation: z.literal(true).optional(),
1007
+ confirmation_reason: z.string().optional(),
1008
+ })
1009
+ .refine((input) => !input.confirm || input.explicit_confirmation === true, {
1010
+ message: 'Реальный запуск волны требует explicit_confirmation=true: сообщения платные и необратимые',
1011
+ path: ['explicit_confirmation'],
1012
+ }),
1013
+ createRequest: (input) => ({
1014
+ method: 'POST',
1015
+ path: `/workspaces/${input.workspace_id}/wazzup-waves/import`,
1016
+ body: compactObject({
1017
+ channelId: input.platform_id,
1018
+ templateGuid: input.template_guid,
1019
+ templateValues: input.template_values,
1020
+ funnel: input.funnel,
1021
+ segment: input.segment,
1022
+ productCode: input.product_code,
1023
+ productName: input.product_name,
1024
+ cohortLabel: input.cohort_label,
1025
+ contacts: input.contacts?.map((c) => compactObject({ phone: c.phone, name: c.name })),
1026
+ confirm: input.confirm,
1027
+ }),
1028
+ }),
1029
+ enhanceResponse: (response) => withInstructions(response, [
1030
+ 'Волна WhatsApp через Wazzup: одобренный WABA-шаблон уходит по когорте телефонов. Ответы получателей обрабатывает AI-агент платформы, волна видна в «Аналитике рассылок» (канал WhatsApp).',
1031
+ 'БЕЗ confirm=true — DRY-RUN: прогоняется ПОЛНЫЙ preflight (платформа, живой WABA-канал, шаблон, число значений) и возвращается previewText — НИ ОДНО сообщение не отправляется. Покажи previewText пользователю и запускай реально только с confirm=true.',
1032
+ 'Реальный запуск требует ОДНОВРЕМЕННО confirm=true и explicit_confirmation=true — второе ставится только после явного согласия пользователя на отправку платных сообщений.',
1033
+ 'КАЖДОЕ маркетинговое сообщение WhatsApp ПЛАТНОЕ, а Meta ограничивает число уникальных получателей в сутки (на старте 250, дальше растёт после верификации бизнеса). Не запускай большие когорты без явного согласия пользователя.',
1034
+ 'template_guid бери из besales_wazzup_templates_list (только usable=true). Длина template_values обязана совпасть с variableCount шаблона, иначе запуск отклонят.',
1035
+ 'Повторная отправка ТОГО ЖЕ шаблона тому же контакту в течение суток пропускается автоматически (защита от двойного списания). Другой шаблон той же когорте не блокируется.',
1036
+ 'Пропускаются контакты, остановившие бота, ждущие оператора и заблокированные — они попадут в skipped, а не в failed.',
1037
+ 'В ответе confirm-запуска — jobId. Опрашивай его через besales_wazzup_wave_status до status=COMPLETED (отправки идут с паузами).',
1038
+ ]),
1039
+ }),
1040
+ defineTool({
1041
+ name: 'besales_wazzup_wave_status',
1042
+ sideEffect: 'none',
1043
+ requiredFields: ['job_id'],
1044
+ defaultWorkspaceId: true,
1045
+ schema: z.object({
1046
+ workspace_id: uuidSchema.optional(),
1047
+ job_id: uuidSchema,
1048
+ }),
1049
+ createRequest: (input) => ({
1050
+ method: 'GET',
1051
+ path: `/workspaces/${input.workspace_id}/wazzup-waves/${input.job_id}`,
1052
+ }),
1053
+ enhanceResponse: (response) => withInstructions(response, [
1054
+ 'Статус волны WhatsApp: status (PROCESSING|COMPLETED), processedRows, sent/failed/skipped, errors (построчные причины).',
1055
+ 'skipped — сознательные пропуски, а unknownOutcome — отдельный неопределённый исход. Для завершённой волны сумма sent+failed+skipped+unknownOutcome равна числу обработанных строк.',
1056
+ 'Доставка приходит вебхуками WhatsApp и живёт ОТДЕЛЬНО от исхода отправки: delivered/read/deliveryFailed + deliveryErrors (коды). Докладывай их вместе с sent — «отправлено» означает лишь, что Wazzup принял сообщение.',
1057
+ 'delivered и deliveryFailed взаимоисключающи; read — подмножество delivered. unknownOutcome также включает ответ успеха без message ID; автоматически такие строки НЕ переотправляются. Диапазон возможных расходов: sent .. sent+unknownOutcome.',
1058
+ ]),
1059
+ }),
1060
+ defineTool({
1061
+ name: 'besales_wazzup_wave_cancel',
1062
+ sideEffect: 'write',
1063
+ requiredFields: ['job_id'],
1064
+ defaultWorkspaceId: true,
1065
+ schema: z.object({
1066
+ workspace_id: uuidSchema.optional(),
1067
+ job_id: uuidSchema,
1068
+ }),
1069
+ createRequest: (input) => ({
1070
+ method: 'POST',
1071
+ path: `/workspaces/${input.workspace_id}/wazzup-waves/${input.job_id}/cancel`,
1072
+ }),
1073
+ enhanceResponse: (response) => withInstructions(response, [
1074
+ 'Отмена скоупится воркспейсом и идемпотентна. cancelled=true означает, что остановка была запрошена; уже завершённая волна не откатывает отправленные сообщения.',
1075
+ 'После отмены опрашивай besales_wazzup_wave_status до status=COMPLETED.',
1076
+ ]),
1077
+ }),
1078
+ ];
1079
+ //# sourceMappingURL=platform-setup-wave.js.map