@besales/mcp 0.48.0 → 1.0.0-beta.2

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 (587) hide show
  1. package/README.md +109 -164
  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 +52 -7
  23. package/dist/http/api-client.js +131 -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 +8 -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 +106 -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 +9902 -4734
  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 +78 -21
  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 +28 -41
  158. package/dist/tools/definitions/crm-platform.js +235 -368
  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/edit-version-transport.test-fixtures.d.ts +13 -0
  197. package/dist/tools/definitions/edit-version-transport.test-fixtures.js +292 -0
  198. package/dist/tools/definitions/edit-version-transport.test-fixtures.js.map +1 -0
  199. package/dist/tools/definitions/edit-version.d.ts +5 -0
  200. package/dist/tools/definitions/edit-version.js +20 -0
  201. package/dist/tools/definitions/edit-version.js.map +1 -0
  202. package/dist/tools/definitions/external-api-channels.js +17 -15
  203. package/dist/tools/definitions/external-api-channels.js.map +1 -1
  204. package/dist/tools/definitions/feedback-document-access.d.ts +16 -0
  205. package/dist/tools/definitions/feedback-document-access.js +63 -0
  206. package/dist/tools/definitions/feedback-document-access.js.map +1 -0
  207. package/dist/tools/definitions/fixtures/shipped-setup-status.d.ts +3 -0
  208. package/dist/tools/definitions/fixtures/shipped-setup-status.js +9 -0
  209. package/dist/tools/definitions/fixtures/shipped-setup-status.js.map +1 -0
  210. package/dist/tools/definitions/follow-up-config.d.ts +79 -0
  211. package/dist/tools/definitions/follow-up-config.js +258 -0
  212. package/dist/tools/definitions/follow-up-config.js.map +1 -0
  213. package/dist/tools/definitions/follow-up-funnel-test.d.ts +4 -3
  214. package/dist/tools/definitions/follow-up-funnel-test.js +65 -52
  215. package/dist/tools/definitions/follow-up-funnel-test.js.map +1 -1
  216. package/dist/tools/definitions/follow-up-promotion-instructions.d.ts +2 -0
  217. package/dist/tools/definitions/follow-up-promotion-instructions.js +7 -0
  218. package/dist/tools/definitions/follow-up-promotion-instructions.js.map +1 -0
  219. package/dist/tools/definitions/follow-up-setup.d.ts +4 -28
  220. package/dist/tools/definitions/follow-up-setup.js +20 -87
  221. package/dist/tools/definitions/follow-up-setup.js.map +1 -1
  222. package/dist/tools/definitions/follow-up.d.ts +20 -122
  223. package/dist/tools/definitions/follow-up.js +200 -357
  224. package/dist/tools/definitions/follow-up.js.map +1 -1
  225. package/dist/tools/definitions/followup-getcourse-compat.d.ts +29 -0
  226. package/dist/tools/definitions/followup-getcourse-compat.js +62 -0
  227. package/dist/tools/definitions/followup-getcourse-compat.js.map +1 -0
  228. package/dist/tools/definitions/followup-sequence-config.schema.d.ts +10 -0
  229. package/dist/tools/definitions/followup-sequence-config.schema.js +14 -0
  230. package/dist/tools/definitions/followup-sequence-config.schema.js.map +1 -0
  231. package/dist/tools/definitions/funnel-analytics.d.ts +18 -0
  232. package/dist/tools/definitions/funnel-analytics.js +150 -0
  233. package/dist/tools/definitions/funnel-analytics.js.map +1 -0
  234. package/dist/tools/definitions/getcourse-anchor-refresh.d.ts +5 -9
  235. package/dist/tools/definitions/getcourse-anchor-refresh.js +26 -31
  236. package/dist/tools/definitions/getcourse-anchor-refresh.js.map +1 -1
  237. package/dist/tools/definitions/getcourse-cold-outbound-settings.d.ts +46 -0
  238. package/dist/tools/definitions/getcourse-cold-outbound-settings.js +37 -0
  239. package/dist/tools/definitions/getcourse-cold-outbound-settings.js.map +1 -0
  240. package/dist/tools/definitions/getcourse-cold-welcome.d.ts +14 -29
  241. package/dist/tools/definitions/getcourse-cold-welcome.js +180 -254
  242. package/dist/tools/definitions/getcourse-cold-welcome.js.map +1 -1
  243. package/dist/tools/definitions/getcourse-wave-welcome.messages.d.ts +10 -0
  244. package/dist/tools/definitions/getcourse-wave-welcome.messages.js +17 -0
  245. package/dist/tools/definitions/getcourse-wave-welcome.messages.js.map +1 -0
  246. package/dist/tools/definitions/help.js.map +1 -1
  247. package/dist/tools/definitions/icp-import-source.d.ts +75 -0
  248. package/dist/tools/definitions/icp-import-source.js +88 -0
  249. package/dist/tools/definitions/icp-import-source.js.map +1 -0
  250. package/dist/tools/definitions/inbound-analytics.d.ts +13 -0
  251. package/dist/tools/definitions/inbound-analytics.js +55 -0
  252. package/dist/tools/definitions/inbound-analytics.js.map +1 -0
  253. package/dist/tools/definitions/instagram-comment.templates.d.ts +19 -0
  254. package/dist/tools/definitions/instagram-comment.templates.js +34 -0
  255. package/dist/tools/definitions/instagram-comment.templates.js.map +1 -0
  256. package/dist/tools/definitions/instagram-settings-compat.d.ts +33 -0
  257. package/dist/tools/definitions/instagram-settings-compat.js +7 -0
  258. package/dist/tools/definitions/instagram-settings-compat.js.map +1 -0
  259. package/dist/tools/definitions/knowledge-chunking.d.ts +42 -0
  260. package/dist/tools/definitions/knowledge-chunking.js +83 -0
  261. package/dist/tools/definitions/knowledge-chunking.js.map +1 -0
  262. package/dist/tools/definitions/knowledge-space-search-fields.d.ts +12 -0
  263. package/dist/tools/definitions/knowledge-space-search-fields.js +30 -0
  264. package/dist/tools/definitions/knowledge-space-search-fields.js.map +1 -0
  265. package/dist/tools/definitions/knowledge.d.ts +24 -14
  266. package/dist/tools/definitions/knowledge.js +342 -233
  267. package/dist/tools/definitions/knowledge.js.map +1 -1
  268. package/dist/tools/definitions/legacy-trigger-repeat.d.ts +17 -0
  269. package/dist/tools/definitions/legacy-trigger-repeat.js +53 -0
  270. package/dist/tools/definitions/legacy-trigger-repeat.js.map +1 -0
  271. package/dist/tools/definitions/link-tracking.d.ts +8 -0
  272. package/dist/tools/definitions/link-tracking.js +141 -38
  273. package/dist/tools/definitions/link-tracking.js.map +1 -1
  274. package/dist/tools/definitions/mailing-variant-content.d.ts +75 -0
  275. package/dist/tools/definitions/mailing-variant-content.js +73 -0
  276. package/dist/tools/definitions/mailing-variant-content.js.map +1 -0
  277. package/dist/tools/definitions/model-comparison.d.ts +1 -0
  278. package/dist/tools/definitions/model-comparison.js +4 -5
  279. package/dist/tools/definitions/model-comparison.js.map +1 -1
  280. package/dist/tools/definitions/money-currency.d.ts +5 -0
  281. package/dist/tools/definitions/money-currency.js +10 -0
  282. package/dist/tools/definitions/money-currency.js.map +1 -0
  283. package/dist/tools/definitions/operator-workflow.d.ts +5 -5
  284. package/dist/tools/definitions/operator-workflow.js +80 -27
  285. package/dist/tools/definitions/operator-workflow.js.map +1 -1
  286. package/dist/tools/definitions/platform-funnel-analytics.d.ts +42 -0
  287. package/dist/tools/definitions/platform-funnel-analytics.js +72 -0
  288. package/dist/tools/definitions/platform-funnel-analytics.js.map +1 -0
  289. package/dist/tools/definitions/platform-getcourse-wave-field-bindings.d.ts +42 -0
  290. package/dist/tools/definitions/platform-getcourse-wave-field-bindings.js +51 -0
  291. package/dist/tools/definitions/platform-getcourse-wave-field-bindings.js.map +1 -0
  292. package/dist/tools/definitions/platform-settings-limits.d.ts +5 -0
  293. package/dist/tools/definitions/platform-settings-limits.js +6 -0
  294. package/dist/tools/definitions/platform-settings-limits.js.map +1 -0
  295. package/dist/tools/definitions/platform-settings-shipped-form.d.ts +37 -0
  296. package/dist/tools/definitions/platform-settings-shipped-form.js +310 -0
  297. package/dist/tools/definitions/platform-settings-shipped-form.js.map +1 -0
  298. package/dist/tools/definitions/platform-settings-update.d.ts +300 -0
  299. package/dist/tools/definitions/platform-settings-update.js +134 -0
  300. package/dist/tools/definitions/platform-settings-update.js.map +1 -0
  301. package/dist/tools/definitions/{getcourse-wave.d.ts → platform-setup-wave.d.ts} +75 -6
  302. package/dist/tools/definitions/platform-setup-wave.js +1079 -0
  303. package/dist/tools/definitions/platform-setup-wave.js.map +1 -0
  304. package/dist/tools/definitions/platform-setup.d.ts +686 -224
  305. package/dist/tools/definitions/platform-setup.js +313 -752
  306. package/dist/tools/definitions/platform-setup.js.map +1 -1
  307. package/dist/tools/definitions/platform-widget-page-rules.schema.d.ts +10 -0
  308. package/dist/tools/definitions/platform-widget-page-rules.schema.js +7 -0
  309. package/dist/tools/definitions/platform-widget-page-rules.schema.js.map +1 -0
  310. package/dist/tools/definitions/prompt-authoring.constants.d.ts +2 -0
  311. package/dist/tools/definitions/prompt-authoring.constants.js +3 -0
  312. package/dist/tools/definitions/prompt-authoring.constants.js.map +1 -0
  313. package/dist/tools/definitions/prompt-batch-controls.d.ts +5 -0
  314. package/dist/tools/definitions/prompt-batch-controls.js +40 -0
  315. package/dist/tools/definitions/prompt-batch-controls.js.map +1 -0
  316. package/dist/tools/definitions/prompt-draft-authoring.d.ts +5 -0
  317. package/dist/tools/definitions/prompt-draft-authoring.js +24 -0
  318. package/dist/tools/definitions/prompt-draft-authoring.js.map +1 -0
  319. package/dist/tools/definitions/prompt-text-projection.d.ts +11 -0
  320. package/dist/tools/definitions/prompt-text-projection.js +62 -0
  321. package/dist/tools/definitions/prompt-text-projection.js.map +1 -0
  322. package/dist/tools/definitions/reviewer-paid.d.ts +19 -0
  323. package/dist/tools/definitions/reviewer-paid.js +115 -0
  324. package/dist/tools/definitions/reviewer-paid.js.map +1 -0
  325. package/dist/tools/definitions/routing-condition-projection.d.ts +31 -0
  326. package/dist/tools/definitions/routing-condition-projection.js +97 -0
  327. package/dist/tools/definitions/routing-condition-projection.js.map +1 -0
  328. package/dist/tools/definitions/schedule-selection-hints.d.ts +3 -0
  329. package/dist/tools/definitions/schedule-selection-hints.js +42 -0
  330. package/dist/tools/definitions/schedule-selection-hints.js.map +1 -0
  331. package/dist/tools/definitions/{scheduled-tasks.d.ts → scheduled-task-alias.d.ts} +12 -10
  332. package/dist/tools/definitions/scheduled-task-alias.js +207 -0
  333. package/dist/tools/definitions/scheduled-task-alias.js.map +1 -0
  334. package/dist/tools/definitions/session.js +3 -3
  335. package/dist/tools/definitions/session.js.map +1 -1
  336. package/dist/tools/definitions/setup-init-operation.schema.d.ts +2 -0
  337. package/dist/tools/definitions/setup-init-operation.schema.js +6 -0
  338. package/dist/tools/definitions/setup-init-operation.schema.js.map +1 -0
  339. package/dist/tools/definitions/shared-input.helpers.d.ts +51 -0
  340. package/dist/tools/definitions/shared-input.helpers.js +323 -0
  341. package/dist/tools/definitions/shared-input.helpers.js.map +1 -0
  342. package/dist/tools/definitions/shared.d.ts +396 -307
  343. package/dist/tools/definitions/shared.js +430 -652
  344. package/dist/tools/definitions/shared.js.map +1 -1
  345. package/dist/tools/definitions/shipped-output-names.d.ts +19 -0
  346. package/dist/tools/definitions/shipped-output-names.js +123 -0
  347. package/dist/tools/definitions/shipped-output-names.js.map +1 -0
  348. package/dist/tools/definitions/task-state.d.ts +5 -5
  349. package/dist/tools/definitions/task-state.js +2 -1
  350. package/dist/tools/definitions/task-state.js.map +1 -1
  351. package/dist/tools/definitions/telegram-personal-capacity.d.ts +4 -0
  352. package/dist/tools/definitions/telegram-personal-capacity.js +18 -0
  353. package/dist/tools/definitions/telegram-personal-capacity.js.map +1 -0
  354. package/dist/tools/definitions/telegram-pool-policy.d.ts +8 -0
  355. package/dist/tools/definitions/telegram-pool-policy.js +56 -0
  356. package/dist/tools/definitions/telegram-pool-policy.js.map +1 -0
  357. package/dist/tools/definitions/touch-history-alias.d.ts +4 -0
  358. package/dist/tools/definitions/touch-history-alias.js +99 -0
  359. package/dist/tools/definitions/touch-history-alias.js.map +1 -0
  360. package/dist/tools/definitions/trigger-action-item.schema.d.ts +254 -0
  361. package/dist/tools/definitions/trigger-action-item.schema.js +121 -0
  362. package/dist/tools/definitions/trigger-action-item.schema.js.map +1 -0
  363. package/dist/tools/definitions/trigger-argument.schema.d.ts +14 -0
  364. package/dist/tools/definitions/trigger-argument.schema.js +23 -0
  365. package/dist/tools/definitions/trigger-argument.schema.js.map +1 -0
  366. package/dist/tools/definitions/trigger-claim-alias.d.ts +10 -0
  367. package/dist/tools/definitions/trigger-claim-alias.js +90 -0
  368. package/dist/tools/definitions/trigger-claim-alias.js.map +1 -0
  369. package/dist/tools/definitions/trigger-echo-projection.d.ts +1 -0
  370. package/dist/tools/definitions/trigger-echo-projection.js +41 -0
  371. package/dist/tools/definitions/trigger-echo-projection.js.map +1 -0
  372. package/dist/tools/definitions/trigger-upsert.tool.d.ts +29 -0
  373. package/dist/tools/definitions/trigger-upsert.tool.js +122 -0
  374. package/dist/tools/definitions/trigger-upsert.tool.js.map +1 -0
  375. package/dist/tools/definitions/v15-port-descriptions.d.ts +11 -0
  376. package/dist/tools/definitions/v15-port-descriptions.js +30 -0
  377. package/dist/tools/definitions/v15-port-descriptions.js.map +1 -0
  378. package/dist/tools/definitions/variables.d.ts +44 -0
  379. package/dist/tools/definitions/variables.js +310 -0
  380. package/dist/tools/definitions/variables.js.map +1 -0
  381. package/dist/tools/definitions/voice-action-input.d.ts +9 -0
  382. package/dist/tools/definitions/voice-action-input.js +141 -0
  383. package/dist/tools/definitions/voice-action-input.js.map +1 -0
  384. package/dist/tools/definitions/widget-customization-compat.d.ts +51 -0
  385. package/dist/tools/definitions/widget-customization-compat.js +23 -0
  386. package/dist/tools/definitions/widget-customization-compat.js.map +1 -0
  387. package/dist/tools/definitions.d.ts +1089 -636
  388. package/dist/tools/definitions.js +57 -27
  389. package/dist/tools/definitions.js.map +1 -1
  390. package/dist/tools/description-localization.js +3 -2
  391. package/dist/tools/description-localization.js.map +1 -1
  392. package/dist/tools/follow-up-request.d.ts +19 -0
  393. package/dist/tools/follow-up-request.js +81 -0
  394. package/dist/tools/follow-up-request.js.map +1 -0
  395. package/dist/tools/followup-revision.d.ts +4 -0
  396. package/dist/tools/followup-revision.js +29 -0
  397. package/dist/tools/followup-revision.js.map +1 -0
  398. package/dist/tools/incomplete-list.js +4 -0
  399. package/dist/tools/incomplete-list.js.map +1 -1
  400. package/dist/tools/inventory.js +47 -11
  401. package/dist/tools/inventory.js.map +1 -1
  402. package/dist/tools/operation-recovery.d.ts +4 -0
  403. package/dist/tools/operation-recovery.js +33 -0
  404. package/dist/tools/operation-recovery.js.map +1 -0
  405. package/dist/tools/registry-mapping-primary.cases.d.ts +2 -0
  406. package/dist/tools/registry-mapping-primary.cases.js +753 -0
  407. package/dist/tools/registry-mapping-primary.cases.js.map +1 -0
  408. package/dist/tools/registry-mapping-secondary.cases.d.ts +2 -0
  409. package/dist/tools/registry-mapping-secondary.cases.js +626 -0
  410. package/dist/tools/registry-mapping-secondary.cases.js.map +1 -0
  411. package/dist/tools/registry-mapping.cases.d.ts +8 -0
  412. package/dist/tools/registry-mapping.cases.js +13 -0
  413. package/dist/tools/registry-mapping.cases.js.map +1 -0
  414. package/dist/tools/registry-test-fixtures.d.ts +50 -0
  415. package/dist/tools/registry-test-fixtures.js +120 -0
  416. package/dist/tools/registry-test-fixtures.js.map +1 -0
  417. package/dist/tools/registry.d.ts +2 -0
  418. package/dist/tools/registry.js +82 -52
  419. package/dist/tools/registry.js.map +1 -1
  420. package/dist/tools/result.d.ts +1 -0
  421. package/dist/tools/result.js +90 -22
  422. package/dist/tools/result.js.map +1 -1
  423. package/dist/tools/toolsets.d.ts +16 -0
  424. package/dist/tools/toolsets.js +25 -1
  425. package/dist/tools/toolsets.js.map +1 -1
  426. package/dist/types/api-contract.gen.d.ts +105875 -22205
  427. package/dist/utils/credential-signatures.d.ts +3 -0
  428. package/dist/utils/credential-signatures.js +10 -0
  429. package/dist/utils/credential-signatures.js.map +1 -0
  430. package/dist/utils/data-source-column-name.d.ts +10 -0
  431. package/dist/utils/data-source-column-name.js +36 -0
  432. package/dist/utils/data-source-column-name.js.map +1 -0
  433. package/dist/utils/logger.d.ts +1 -0
  434. package/dist/utils/logger.js +32 -8
  435. package/dist/utils/logger.js.map +1 -1
  436. package/dist/utils/secret-read-boundary.d.ts +2 -0
  437. package/dist/utils/secret-read-boundary.js +88 -0
  438. package/dist/utils/secret-read-boundary.js.map +1 -0
  439. package/dist/utils/setup-secrets.d.ts +4 -0
  440. package/dist/utils/setup-secrets.js +40 -0
  441. package/dist/utils/setup-secrets.js.map +1 -0
  442. package/docs/agent-models.md +9 -0
  443. package/docs/agent-usage-guide.full.ru.md +1 -4
  444. package/docs/analytics-contract.md +67 -0
  445. package/docs/async-operation-lifecycle.md +10 -3
  446. package/docs/connect.ru.md +144 -4
  447. package/docs/contact-company-profile.md +17 -0
  448. package/docs/contact-recommendations.md +7 -0
  449. package/docs/crm-stage-pool.md +73 -0
  450. package/docs/customer-data.md +61 -0
  451. package/docs/edit-versions.md +36 -0
  452. package/docs/failure-semantics.md +17 -9
  453. package/docs/help-navigation.md +15 -42
  454. package/docs/host-compatibility-matrix.md +72 -93
  455. package/docs/host-setup.md +33 -35
  456. package/docs/invariants/public-api-and-contracts.md +16 -0
  457. package/docs/legacy-action-settings.md +21 -0
  458. package/docs/main-transfer-dependencies.md +38 -0
  459. package/docs/mcp2-parity.md +50 -0
  460. package/docs/modules/analytics.md +79 -0
  461. package/docs/modules/besales-mcp.md +137 -0
  462. package/docs/modules/dialog-review-report.md +38 -0
  463. package/docs/modules/dialogue-followup-adapters.md +39 -0
  464. package/docs/modules/message-reactions.md +17 -0
  465. package/docs/modules/network-mode.md +284 -0
  466. package/docs/modules/prompt-batch-and-dialogue-controls.md +69 -0
  467. package/docs/pii-policy.md +21 -16
  468. package/docs/response-size-budget.md +197 -7
  469. package/docs/secret-read-boundary.md +102 -0
  470. package/docs/task-state.md +7 -2
  471. package/docs/telegram-pool-policy.md +19 -0
  472. package/docs/tool-effects.md +15 -9
  473. package/docs/tool-surface.md +205 -4
  474. package/docs/voice-actions-and-inbox.md +95 -0
  475. package/docs/wazzup-instagram-comments.md +26 -0
  476. package/package.json +17 -10
  477. package/scripts/api-source-loader.spec.mjs +40 -0
  478. package/scripts/audit-bridge-controls.mjs +129 -0
  479. package/scripts/audit-revert-mutants.mjs +107 -0
  480. package/scripts/bridge-api-input.spec.mjs +98 -0
  481. package/scripts/bridge-controls-mutants.mjs +130 -0
  482. package/scripts/brx3-shipped-refusals.spec.mjs +149 -0
  483. package/scripts/brx4-ledger-payload.spec.mjs +127 -0
  484. package/scripts/build-onprem.mjs +108 -0
  485. package/scripts/connect-clients.sh +141 -0
  486. package/scripts/connect-scopes.mjs +166 -0
  487. package/scripts/connect-scopes.spec.mjs +83 -0
  488. package/scripts/emit-personal-link-carrier.mjs +31 -0
  489. package/scripts/emit-setup-init-schemas.mjs +24 -0
  490. package/scripts/emit-tool-routes.mjs +878 -0
  491. package/scripts/emit-tool-routes.spec.mjs +300 -0
  492. package/scripts/followup-registry.spec.mjs +107 -0
  493. package/scripts/generate-crm-rotation-contract.mjs +98 -0
  494. package/scripts/generate-followup-session-contract.mjs +16 -0
  495. package/scripts/icp-import-verification/mutate.cjs +70 -0
  496. package/scripts/import-secret-read-contract.mjs +53 -0
  497. package/scripts/install-claude-desktop.js +1 -1
  498. package/scripts/lag-decisions.mjs +37 -0
  499. package/scripts/lag-decisions.spec.mjs +87 -0
  500. package/scripts/lag-open-task-owners.mjs +46 -0
  501. package/scripts/lag-open-task-owners.spec.mjs +78 -0
  502. package/scripts/lag-payload.mjs +10 -0
  503. package/scripts/legacy-action-lag.spec.mjs +22 -0
  504. package/scripts/legacy-field-refusal.shipped.spec.mjs +99 -0
  505. package/scripts/mcpcompat-handler.mjs +34 -0
  506. package/scripts/mcpcompat-loader.mjs +36 -0
  507. package/scripts/mcpcompat-typescript.mjs +3 -0
  508. package/scripts/platform-settings-shipped-parity.spec.mjs +89 -0
  509. package/scripts/prbr-round1-recheck.spec.mjs +605 -0
  510. package/scripts/prbr-shipped-compatibility.spec.mjs +276 -0
  511. package/scripts/publish-mcp-registry.mjs +98 -0
  512. package/scripts/publish-mcp-registry.spec.mjs +68 -0
  513. package/scripts/record-scheduled-task-call.mjs +26 -0
  514. package/scripts/refusal-probe.mjs +17 -0
  515. package/scripts/refusal-probe.spec.mjs +29 -0
  516. package/scripts/registry-diff.mjs +502 -0
  517. package/scripts/registry-diff.spec.mjs +787 -0
  518. package/scripts/request-query.mjs +43 -0
  519. package/scripts/request-query.spec.mjs +82 -0
  520. package/scripts/route-lag.mjs +23 -0
  521. package/scripts/route-lag.spec.mjs +30 -0
  522. package/scripts/sample-args.mjs +174 -0
  523. package/scripts/sample-args.spec.mjs +160 -0
  524. package/scripts/shipped-definitions.mjs +197 -0
  525. package/scripts/shipped-definitions.spec.mjs +57 -0
  526. package/scripts/shipped-inputs.spec.mjs +136 -0
  527. package/scripts/shipped-recordings.spec.mjs +19 -0
  528. package/scripts/shipped-ref.d.mts +5 -0
  529. package/scripts/shipped-ref.mjs +20 -0
  530. package/scripts/shipped-ref.spec.mjs +59 -0
  531. package/scripts/shipped-registry.mjs +107 -0
  532. package/scripts/shipped-sample-candidates.mjs +69 -0
  533. package/scripts/shipped-sample-candidates.spec.mjs +117 -0
  534. package/scripts/shipped-sample.mjs +868 -0
  535. package/scripts/shipped-sample.spec.mjs +451 -0
  536. package/scripts/sync-agent-model-schemas.mjs +72 -0
  537. package/scripts/sync-api-contracts.mjs +85 -0
  538. package/scripts/sync-api-contracts.spec.mjs +144 -0
  539. package/scripts/sync-contract-ledger-schemas.mjs +161 -0
  540. package/scripts/sync-crm-stage-pool-schema.mjs +85 -0
  541. package/scripts/sync-prompt-draft-schema.mjs +92 -0
  542. package/scripts/sync-route-map.mjs +94 -0
  543. package/scripts/sync-schema-descriptions.mjs +25 -0
  544. package/scripts/sync-tool-inventory.mjs +27 -0
  545. package/scripts/sync-trigger-argument-schema.mjs +121 -0
  546. package/scripts/sync-trigger-repeat-mapping.mjs +12 -0
  547. package/scripts/sync-voice-actions-contract.mjs +67 -0
  548. package/scripts/sync-voice-actions-tools.mjs +97 -0
  549. package/scripts/sync-wave-id-schemas.mjs +55 -0
  550. package/scripts/tool-input-schemas.mjs +46 -0
  551. package/scripts/tool-input-schemas.spec.mjs +50 -0
  552. package/scripts/verify-followup-adapters.mjs +235 -0
  553. package/scripts/verify-secret-read-import.mjs +84 -0
  554. package/scripts/verify-secret-read-mutants.mjs +143 -0
  555. package/dist/tools/definitions/analytics.d.ts +0 -40
  556. package/dist/tools/definitions/analytics.js +0 -233
  557. package/dist/tools/definitions/analytics.js.map +0 -1
  558. package/dist/tools/definitions/broadcast-mailing-variants.d.ts +0 -41
  559. package/dist/tools/definitions/broadcast-mailing-variants.js +0 -194
  560. package/dist/tools/definitions/broadcast-mailing-variants.js.map +0 -1
  561. package/dist/tools/definitions/dialogue-audit.d.ts +0 -38
  562. package/dist/tools/definitions/dialogue-audit.js +0 -292
  563. package/dist/tools/definitions/dialogue-audit.js.map +0 -1
  564. package/dist/tools/definitions/getcourse-wave.js +0 -519
  565. package/dist/tools/definitions/getcourse-wave.js.map +0 -1
  566. package/dist/tools/definitions/operational-overview.d.ts +0 -3
  567. package/dist/tools/definitions/operational-overview.js +0 -23
  568. package/dist/tools/definitions/operational-overview.js.map +0 -1
  569. package/dist/tools/definitions/scheduled-tasks.js +0 -173
  570. package/dist/tools/definitions/scheduled-tasks.js.map +0 -1
  571. package/dist/tools/definitions/trigger-contact-claims.d.ts +0 -7
  572. package/dist/tools/definitions/trigger-contact-claims.js +0 -60
  573. package/dist/tools/definitions/trigger-contact-claims.js.map +0 -1
  574. package/dist/tools/registry-mapping-cases-core.d.ts +0 -38
  575. package/dist/tools/registry-mapping-cases-core.js +0 -963
  576. package/dist/tools/registry-mapping-cases-core.js.map +0 -1
  577. package/dist/tools/registry-mapping-cases-platform.d.ts +0 -2
  578. package/dist/tools/registry-mapping-cases-platform.js +0 -1052
  579. package/dist/tools/registry-mapping-cases-platform.js.map +0 -1
  580. package/dist/tools/trigger-action-config.d.ts +0 -49
  581. package/dist/tools/trigger-action-config.js +0 -171
  582. package/dist/tools/trigger-action-config.js.map +0 -1
  583. package/docs/evals/model-behavior-scorecard.md +0 -80
  584. package/docs/operation-coverage.md +0 -114
  585. package/scripts/mock-credentials.js +0 -77
  586. package/scripts/model-eval-server.js +0 -213
  587. package/scripts/run-model-eval.js +0 -447
@@ -0,0 +1,787 @@
1
+ import { readFileSync } from 'node:fs';
2
+
3
+ import { describe, expect, it } from 'vitest';
4
+
5
+ import {
6
+ diffRegistries,
7
+ enumValuesByPath,
8
+ lagKey,
9
+ lagMetadataErrors,
10
+ LAG_SECTIONS,
11
+ reconcileWithRegistry,
12
+ schemaConstraints,
13
+ schemaRetainsPath,
14
+ validLagOwner,
15
+ } from './registry-diff.mjs';
16
+
17
+ describe('строгий владелец реестра', () => {
18
+ it.each([
19
+ ['TASK-6.22.10', true],
20
+ ['DELIBERATE', true],
21
+ ['6.22.10', false],
22
+ ['TASK-6.22.10 (закрыта)', false],
23
+ ['TASK-6.22', true],
24
+ ['', false],
25
+ [undefined, false],
26
+ [42, false],
27
+ ])('%s: допустим=%s', (owner, expected) => {
28
+ expect(validLagOwner(owner)).toBe(expected);
29
+ });
30
+
31
+ it('неправильный владелец попадает в incompleteEntries через сверку', () => {
32
+ const row = { reason: 'Названная причина', owner: 'команда', severity: 'LIVE_400' };
33
+ expect(
34
+ reconcileWithRegistry(
35
+ { missingTools: ['a'], contractLag: [] },
36
+ {
37
+ missingTools: { a: row },
38
+ contractLag: {},
39
+ },
40
+ ).incomplete,
41
+ ).toEqual(['a']);
42
+ });
43
+
44
+ it.each(LAG_SECTIONS)(
45
+ '%s: осознанный долг требует согласованную пару владельца и тяжести',
46
+ (section) => {
47
+ const row = { reason: 'Принятое ограничение; decision:6.19.51:test', owner: 'DELIBERATE', severity: 'DELIBERATE' };
48
+ expect(lagMetadataErrors({ [section]: { field: row } })).toEqual([]);
49
+ for (const broken of [
50
+ { ...row, owner: 'TASK-6.19.51' },
51
+ { ...row, severity: 'LIVE_400' },
52
+ { ...row, owner: 'домен' },
53
+ { ...row, reason: ' ' },
54
+ { ...row, reason: 'Принято без ссылки' },
55
+ { ...row, reason: 'decision:6.19.51:test' },
56
+ { ...row, severity: 'UNKNOWN' },
57
+ ])
58
+ expect(lagMetadataErrors({ [section]: { field: broken } })).toEqual([`${section}:field`]);
59
+ expect(
60
+ lagMetadataErrors({
61
+ [section]: { field: { ...row, owner: 'TASK-6.19.51', severity: 'LIVE_400' } },
62
+ }),
63
+ ).toEqual([]);
64
+ },
65
+ );
66
+
67
+ it('в реальном реестре нет строк без строгого владельца, включая дополнительные разделы', () => {
68
+ const registry = JSON.parse(
69
+ readFileSync(new URL('../src/schemas/shipped-lag.json', import.meta.url), 'utf8'),
70
+ );
71
+ expect(lagMetadataErrors(registry)).toEqual([]);
72
+ });
73
+ });
74
+ import {
75
+ loadShippedRegistry,
76
+ SHIPPED_REGISTRY_COMMIT_ENV,
77
+ SHIPPED_REGISTRY_PATH_ENV,
78
+ } from './shipped-registry.mjs';
79
+
80
+ const packageRoot = new URL('..', import.meta.url).pathname;
81
+ const SHIPPED_REGISTRY_TEST_TIMEOUT_MS = 30_000;
82
+
83
+ function tool(name, properties, required = []) {
84
+ return { name, inputSchema: { type: 'object', properties, required } };
85
+ }
86
+
87
+ describe('schemaConstraints', () => {
88
+ it('разворачивает вложенные объекты и элементы массивов', () => {
89
+ const paths = [
90
+ ...schemaConstraints({
91
+ type: 'object',
92
+ properties: {
93
+ a: { type: 'string' },
94
+ nested: { type: 'object', properties: { b: { type: 'number' } } },
95
+ list: { type: 'array', items: { type: 'object', properties: { c: { type: 'string' } } } },
96
+ },
97
+ }).keys(),
98
+ ].sort();
99
+
100
+ // `list[]` — сам элемент массива: его ограничения (тип, перечисление) сравниваются отдельно
101
+ // от свойств внутри него, иначе сужение `items.enum` прошло бы незамеченным.
102
+ expect(paths).toEqual(['a', 'list', 'list[]', 'list[].c', 'nested', 'nested.b']);
103
+ });
104
+
105
+ it('объединяет ветки oneOf: инструмент принимает любую из них', () => {
106
+ const paths = [
107
+ ...schemaConstraints({
108
+ oneOf: [
109
+ { type: 'object', properties: { left: { type: 'string' } } },
110
+ { type: 'object', properties: { right: { type: 'string' } } },
111
+ ],
112
+ }).keys(),
113
+ ].sort();
114
+
115
+ expect(paths).toEqual(['left', 'right']);
116
+ });
117
+ });
118
+
119
+ describe('разыменование локальных $ref', () => {
120
+ const refTool = (name, defs) => ({
121
+ name,
122
+ inputSchema: {
123
+ type: 'object',
124
+ properties: {
125
+ media: {
126
+ type: 'object',
127
+ properties: {
128
+ image: { $ref: '#/$defs/rich' },
129
+ audio: { $ref: '#/$defs/plain' },
130
+ },
131
+ },
132
+ },
133
+ $defs: defs,
134
+ },
135
+ });
136
+ const rich = (values) => ({
137
+ type: 'object',
138
+ properties: { behavior: { type: 'string', enum: values } },
139
+ });
140
+
141
+ it('сужение перечисления ВНУТРИ $defs попадает в отставание', () => {
142
+ const shipped = [refTool('a', { rich: rich(['SILENT', 'LLM']), plain: rich(['SILENT']) })];
143
+ const local = [refTool('a', { rich: rich(['SILENT']), plain: rich(['SILENT']) })];
144
+
145
+ // До разыменования путей `media.image.behavior` не существовало НИ НА ОДНОЙ стороне, и
146
+ // расхождение внутри общего определения не мог заметить никто.
147
+ expect(diffRegistries(shipped, local).contractLag.map((entry) => entry.path)).toContain(
148
+ 'media.image.behavior#enum',
149
+ );
150
+ });
151
+
152
+ it('две РАЗНЫЕ ссылки резолвятся каждая в своё определение', () => {
153
+ const constraints = schemaConstraints(
154
+ refTool('a', { rich: rich(['SILENT', 'LLM']), plain: rich(['SILENT']) }).inputSchema,
155
+ );
156
+
157
+ // ⛔ Различающая проверка: разыменование, подставляющее ПЕРВОЕ попавшееся определение обеим
158
+ // ссылкам, дало бы здесь одинаковые перечисления и прошло бы незамеченным.
159
+ expect(constraints.get('media.image.behavior').enum).toBe('LLM|SILENT');
160
+ expect(constraints.get('media.audio.behavior').enum).toBe('SILENT');
161
+ });
162
+
163
+ it.each([
164
+ ['указатель никуда не ведёт', { $ref: '#/$defs/typo' }, 'никуда не ведёт'],
165
+ ['внешний документ', { $ref: 'other.json#/x' }, 'внешняя ссылка'],
166
+ ])('fail-closed: %s роняет сверку', (_name, node, message) => {
167
+ // ⛔ Молчаливый пропуск здесь был бы той же слепотой под другим именем: узел с одним `$ref`
168
+ // неотличим для обхода от схемы БЕЗ ограничений.
169
+ expect(() => schemaConstraints({ type: 'object', properties: { x: node }, $defs: {} })).toThrow(
170
+ message,
171
+ );
172
+ });
173
+
174
+ it.each([
175
+ ['запрещающая схема false', false],
176
+ ['разрешающая схема true', true],
177
+ ['null', null],
178
+ ['массив', []],
179
+ ['скаляр', 42],
180
+ ])('fail-closed: цель ссылки «%s» роняет сверку', (_name, target) => {
181
+ // ⛔ Самый коварный из этих случаев — `false`: в JSON Schema он означает «не принимать
182
+ // НИЧЕГО», а спред объекта превращал его в пустой узел, то есть в «ограничений нет». Это
183
+ // был бы fail-open внутри правки, написанной ради устранения fail-open.
184
+ expect(() =>
185
+ schemaConstraints({
186
+ type: 'object',
187
+ properties: { x: { $ref: '#/$defs/target' } },
188
+ $defs: { target },
189
+ }),
190
+ ).toThrow('не на объект-схему');
191
+ });
192
+
193
+ it('указатель на КОРЕНЬ схемы законен и не считается внешней ссылкой', () => {
194
+ const constraints = schemaConstraints({
195
+ type: 'object',
196
+ properties: { self: { $ref: '#' }, x: { type: 'string' } },
197
+ });
198
+
199
+ // Прежняя редакция объявляла `#` внешним документом: отказ безопасен, но диагноз уводил
200
+ // искать причину не туда.
201
+ expect(constraints.get('self.x')).toMatchObject({ type: 'string' });
202
+ });
203
+
204
+ it('fail-closed: цикл ссылок роняет сверку, а не вешает её', () => {
205
+ expect(() =>
206
+ schemaConstraints({
207
+ type: 'object',
208
+ properties: { x: { $ref: '#/$defs/a' } },
209
+ $defs: { a: { $ref: '#/$defs/b' }, b: { $ref: '#/$defs/a' } },
210
+ }),
211
+ ).toThrow('вероятен цикл');
212
+ });
213
+
214
+ it('соседние ключи узла-ссылки побеждают тело определения', () => {
215
+ const constraints = schemaConstraints({
216
+ type: 'object',
217
+ properties: { x: { $ref: '#/$defs/a', format: 'uuid' } },
218
+ $defs: { a: { type: 'string', format: 'email' } },
219
+ });
220
+
221
+ expect(constraints.get('x')).toMatchObject({ type: 'string', format: 'uuid' });
222
+ });
223
+
224
+ it('живой реестр: обработчики вложения раскрываются полем в поле', () => {
225
+ const local = JSON.parse(
226
+ readFileSync(`${packageRoot}src/schemas/mcp-tools.json`, 'utf8'),
227
+ ).tools;
228
+ const behavior = local.find((entry) => entry.name === 'besales_behavior_update');
229
+ const constraints = schemaConstraints(behavior.inputSchema);
230
+ const media = [...constraints.keys()]
231
+ .filter((path) => path.startsWith('media_handling'))
232
+ .sort();
233
+
234
+ // ⛔ Проверяется ДЕЛЬТА по обработчикам вложения, а не итог по инструменту: итог меняется от
235
+ // любой соседней правки схемы, и хардкод протух бы на здоровой работе. До разыменования этих
236
+ // путей было пять (сама секция и четыре ссылки), теперь двадцать два (плюс версия формата секции).
237
+ expect(media).toEqual([
238
+ 'media_handling',
239
+ 'media_handling.audio',
240
+ 'media_handling.audio.behavior',
241
+ 'media_handling.audio.call_operator',
242
+ 'media_handling.audio.fixed_response_text',
243
+ 'media_handling.audio.operator_comment',
244
+ 'media_handling.document',
245
+ 'media_handling.document.behavior',
246
+ 'media_handling.document.call_operator',
247
+ 'media_handling.document.fixed_response_text',
248
+ 'media_handling.document.operator_comment',
249
+ 'media_handling.image',
250
+ 'media_handling.image.behavior',
251
+ 'media_handling.image.call_operator',
252
+ 'media_handling.image.fixed_response_text',
253
+ 'media_handling.image.operator_comment',
254
+ 'media_handling.schema_version',
255
+ 'media_handling.video',
256
+ 'media_handling.video.behavior',
257
+ 'media_handling.video.call_operator',
258
+ 'media_handling.video.fixed_response_text',
259
+ 'media_handling.video.operator_comment',
260
+ ]);
261
+ expect(constraints.get('media_handling.audio.behavior').enum).toBe('FIXED_RESPONSE|SILENT');
262
+ expect(constraints.get('media_handling.image.behavior').enum).toBe(
263
+ 'FIXED_RESPONSE|PROCESS_WITH_LLM|SILENT',
264
+ );
265
+ });
266
+
267
+ it('ни один узел реестра не объявляет const без type', () => {
268
+ const local = JSON.parse(
269
+ readFileSync(`${packageRoot}src/schemas/mcp-tools.json`, 'utf8'),
270
+ ).tools;
271
+ const naked = [];
272
+ const walk = (node, tool, path) => {
273
+ if (node === null || typeof node !== 'object') return;
274
+ if (node.const !== undefined && node.type === undefined) naked.push(`${tool}::${path}`);
275
+ for (const key of ['properties', '$defs']) {
276
+ if (node[key]) {
277
+ for (const [name, child] of Object.entries(node[key])) {
278
+ walk(child, tool, path === '' ? name : `${path}.${name}`);
279
+ }
280
+ }
281
+ }
282
+ if (node.items) walk(node.items, tool, `${path}[]`);
283
+ for (const key of ['oneOf', 'anyOf', 'allOf']) {
284
+ if (Array.isArray(node[key])) for (const branch of node[key]) walk(branch, tool, path);
285
+ }
286
+ };
287
+ for (const entry of local) walk(entry.inputSchema, entry.name, '');
288
+
289
+ // Класс уже возвращался руками: без `type` клиент сериализует подтверждение СТРОКОЙ, zod ждёт
290
+ // булев литерал, и вызов падает ВСЕГДА. Обход намеренно не заходит в `if`-матчеры: там
291
+ // `const` — часть условия, а не объявление значения поля.
292
+ expect(naked).toEqual([]);
293
+ });
294
+ });
295
+
296
+ describe('diffRegistries', () => {
297
+ it('новое обязательное поле видно даже без отгруженного пути; необязательное не ломает вызов', () => {
298
+ const shipped = [tool('a', { id: { type: 'string' } }, ['id'])];
299
+ const local = [tool('a', {
300
+ id: { type: 'string' }, request_key: { type: 'string' }, optional: { type: 'string' },
301
+ }, ['id', 'request_key'])];
302
+ expect(diffRegistries(shipped, local).contractLag).toEqual([
303
+ { tool: 'a', path: 'request_key#required', kind: 'CONSTRAINT', shipped: false, v2: true },
304
+ ]);
305
+ local[0].inputSchema.required = ['id'];
306
+ expect(diffRegistries(shipped, local).contractLag).toEqual([]);
307
+ });
308
+ it('видит инструмент, которого нет в ветке v2', () => {
309
+ const diff = diffRegistries([tool('a', {}), tool('b', {})], [tool('a', {})]);
310
+
311
+ expect(diff.missingTools).toEqual(['b']);
312
+ });
313
+
314
+ it('видит поле, которого нет у v2-двойника, включая вложенное', () => {
315
+ const shipped = [
316
+ tool('a', {
317
+ top: { type: 'string' },
318
+ list: {
319
+ type: 'array',
320
+ items: { type: 'object', properties: { inner: { type: 'string' } } },
321
+ },
322
+ }),
323
+ ];
324
+ const local = [
325
+ tool('a', { list: { type: 'array', items: { type: 'object', properties: {} } } }),
326
+ ];
327
+
328
+ expect(diffRegistries(shipped, local).contractLag.map((entry) => entry.path)).toEqual([
329
+ 'list[].inner',
330
+ 'top',
331
+ ]);
332
+ });
333
+
334
+ it('видит расхождение ограничений: значения перечисления, обязательность, предел', () => {
335
+ const shipped = [
336
+ tool(
337
+ 'a',
338
+ { mode: { type: 'string', enum: ['x', 'y'] }, name: { type: 'string', maxLength: 200 } },
339
+ ['name'],
340
+ ),
341
+ ];
342
+ const local = [
343
+ tool('a', {
344
+ mode: { type: 'string', enum: ['x'] },
345
+ name: { type: 'string', maxLength: 100 },
346
+ }),
347
+ ];
348
+
349
+ expect(
350
+ diffRegistries(shipped, local)
351
+ .contractLag.map((entry) => entry.path)
352
+ .sort(),
353
+ ).toEqual(['mode#enum', 'name#maxLength', 'name#required']);
354
+ });
355
+
356
+ /** Мутация, которая красит: читать у свойства-объединения только собственные ограничения узла. */
357
+ it('свойство-объединение: отгруженная форма, которую держит одна из веток, отставанием не считается', () => {
358
+ const shipped = [tool('a', { from: { type: 'string', format: 'date' } })];
359
+ const local = [
360
+ tool('a', { from: { type: 'string', anyOf: [{ format: 'date' }, { format: 'date-time' }] } }),
361
+ ];
362
+
363
+ expect(diffRegistries(shipped, local).contractLag).toEqual([]);
364
+ });
365
+
366
+ it('объединение без совпадающей ветки — расхождение с ограничениями самого узла', () => {
367
+ const shipped = [tool('a', { from: { type: 'string', format: 'date' } })];
368
+ const local = [tool('a', { from: { type: 'string', anyOf: [{ format: 'date-time' }] } })];
369
+
370
+ expect(diffRegistries(shipped, local).contractLag).toEqual([
371
+ { tool: 'a', path: 'from#format', kind: 'CONSTRAINT', shipped: 'date', v2: null },
372
+ ]);
373
+ });
374
+
375
+ it('инструмент, которого нет в отгруженной копии, отставанием не считается', () => {
376
+ expect(diffRegistries([tool('a', {})], [tool('a', {}), tool('b', {})]).v2Only).toEqual(['b']);
377
+ });
378
+
379
+ it('переименование показывает ОБА факта: имя исчезло и поля разошлись', () => {
380
+ // ⚠️ Прежде здесь стояло `missingTools = []`: переименование считалось «ничего не пропало».
381
+ // Это прятало клиентский разрыв — вызов старого имени сегодня отвечает «инструмент не найден».
382
+ // Преемник объясняет, ЧЕМ заменён инструмент, и даёт сверить схемы; он не отменяет того, что
383
+ // отгруженное имя ушло с поверхности. Оба факта обязаны быть объявлены отдельно.
384
+ const shipped = [tool('old', { legacy: { type: 'string' } })];
385
+ const local = [tool('new', { canonical: { type: 'string' } })];
386
+ const diff = diffRegistries(shipped, local, { old: 'new' });
387
+
388
+ expect(diff.missingTools).toEqual(['old']);
389
+ expect(diff.v2Only).toEqual([]);
390
+ expect(diff.contractLag).toEqual([
391
+ { tool: 'old', path: 'legacy', kind: 'MISSING_FIELD', shipped: 'string', v2: null },
392
+ ]);
393
+ });
394
+
395
+ it('отвергает протухшее сопоставление преемника', () => {
396
+ expect(() =>
397
+ diffRegistries([tool('old', {})], [tool('old', {}), tool('new', {})], { old: 'new' }),
398
+ ).toThrow(/старое имя уже существует/u);
399
+ expect(() => diffRegistries([tool('old', {})], [], { old: 'missing' })).toThrow(
400
+ /отсутствующий v2-инструмент/u,
401
+ );
402
+ });
403
+ });
404
+
405
+ describe('reconcileWithRegistry', () => {
406
+ const diff = {
407
+ missingTools: ['b'],
408
+ contractLag: [{ tool: 'a', path: 'top', kind: 'MISSING_FIELD', shipped: 'string', v2: null }],
409
+ };
410
+
411
+ it('незаявленное расхождение называется поимённо', () => {
412
+ const actual = reconcileWithRegistry(diff, { missingTools: {}, contractLag: {} });
413
+
414
+ expect(actual.undeclaredMissing).toEqual(['b']);
415
+ expect(actual.undeclaredLag).toEqual(['a::top::"string"→null']);
416
+ });
417
+
418
+ it('протухшая строка реестра называется тоже — иначе она станет способом замолчать будущее', () => {
419
+ const actual = reconcileWithRegistry(diff, {
420
+ missingTools: { b: {}, c: {} },
421
+ contractLag: { 'a::top::"string"→null': {}, 'a::gone::"x"→null': {} },
422
+ });
423
+
424
+ expect(actual.undeclaredMissing).toEqual([]);
425
+ expect(actual.staleMissing).toEqual(['c']);
426
+ expect(actual.staleLag).toEqual(['a::gone::"x"→null']);
427
+ });
428
+
429
+ it('второе расхождение у уже отстающего инструмента не проходит по старой строке', () => {
430
+ const actual = reconcileWithRegistry(
431
+ {
432
+ missingTools: [],
433
+ contractLag: [
434
+ { tool: 'a', path: 'top', kind: 'MISSING_FIELD', shipped: 'string', v2: null },
435
+ { tool: 'a', path: 'second', kind: 'MISSING_FIELD', shipped: 'string', v2: null },
436
+ ],
437
+ },
438
+ { missingTools: {}, contractLag: { 'a::top::"string"→null': {} } },
439
+ );
440
+
441
+ expect(actual.undeclaredLag).toEqual(['a::second::"string"→null']);
442
+ });
443
+ });
444
+
445
+ describe('loadShippedRegistry', () => {
446
+ it('офлайн-вход без коммита отвергается: непроверяемый эталон хуже отсутствующего', () => {
447
+ expect(() =>
448
+ loadShippedRegistry(packageRoot, {
449
+ env: { [SHIPPED_REGISTRY_PATH_ENV]: 'src/schemas/mcp-tools.json' },
450
+ }),
451
+ ).toThrow(SHIPPED_REGISTRY_COMMIT_ENV);
452
+ });
453
+
454
+ it('офлайн-вход с коммитом принимается и называет своё происхождение', () => {
455
+ const actual = loadShippedRegistry(packageRoot, {
456
+ env: {
457
+ [SHIPPED_REGISTRY_PATH_ENV]: `${packageRoot}src/schemas/mcp-tools.json`,
458
+ [SHIPPED_REGISTRY_COMMIT_ENV]: 'deadbeef',
459
+ },
460
+ });
461
+
462
+ expect(actual.provenance).toMatchObject({ source: 'file', commit: 'deadbeef' });
463
+ expect(actual.tools.length).toBeGreaterThan(0);
464
+ });
465
+ });
466
+
467
+ describe('реестр отставания', () => {
468
+ it(
469
+ 'совпадает с фактическим расхождением ветки v2 и отгруженной копии',
470
+ () => {
471
+ const shipped = loadShippedRegistry(packageRoot);
472
+ const local = JSON.parse(
473
+ readFileSync(`${packageRoot}src/schemas/mcp-tools.json`, 'utf8'),
474
+ ).tools;
475
+ const fullRegistry = JSON.parse(
476
+ readFileSync(`${packageRoot}src/schemas/shipped-lag.json`, 'utf8'),
477
+ );
478
+ // Строки «путь#refused» — отказ, живущий в КОДЕ инструмента: сравнение схем его не видит, его наблюдает выгрузка
479
+ // исполнением законного отгруженного вызова и сверяет в обе стороны сама. Здесь сверяется только сравнение схем.
480
+ const registry = {
481
+ ...fullRegistry,
482
+ contractLag: Object.fromEntries(
483
+ Object.entries(fullRegistry.contractLag).filter(([key]) => !key.includes('#refused::')),
484
+ ),
485
+ };
486
+ const diff = diffRegistries(shipped.tools, local, registry.toolSuccessors);
487
+ const actual = reconcileWithRegistry(diff, registry);
488
+
489
+ expect({
490
+ необъявленные: [...actual.undeclaredMissing, ...actual.undeclaredLag],
491
+ протухшие: [...actual.staleMissing, ...actual.staleLag],
492
+ }).toEqual({ необъявленные: [], протухшие: [] });
493
+ expect(diff.contractLag.every((entry) => registry.contractLag[lagKey(entry)]?.severity)).toBe(
494
+ true,
495
+ );
496
+ },
497
+ SHIPPED_REGISTRY_TEST_TIMEOUT_MS,
498
+ );
499
+ });
500
+
501
+ describe('паритет JSON-реестра и zod по ВЛОЖЕННЫМ путям', () => {
502
+ it('отличает сохранение произвольного config от настоящего удаления полей', async () => {
503
+ const { z } = await import('zod');
504
+ const inputValue = { config: { stopOnPayment: true, steps: [{ statusName: 'Новый' }] } };
505
+ const inputPreserving = z.object({ config: z.object({}).passthrough() });
506
+ const inputStripping = z.object({ config: z.object({}) });
507
+ const inputPaths = [
508
+ 'config.stopOnPayment',
509
+ 'config.steps',
510
+ 'config.steps[]',
511
+ 'config.steps[].statusName',
512
+ ];
513
+
514
+ for (const inputPath of inputPaths) {
515
+ expect(schemaRetainsPath(z.toJSONSchema(inputPreserving, { io: 'output' }), inputPath)).toBe(
516
+ true,
517
+ );
518
+ expect(schemaRetainsPath(z.toJSONSchema(inputStripping, { io: 'output' }), inputPath)).toBe(
519
+ false,
520
+ );
521
+ }
522
+ expect(inputPreserving.parse(inputValue)).toEqual(inputValue);
523
+ expect(inputStripping.parse(inputValue)).toEqual({ config: {} });
524
+ expect(
525
+ z.toJSONSchema(inputStripping, { io: 'input' }).properties.config.additionalProperties,
526
+ ).toBeUndefined();
527
+ });
528
+
529
+ it('passthrough родителя не скрывает удаление поля известного вложенного объекта', async () => {
530
+ const { z } = await import('zod');
531
+ const inputSchema = z.object({
532
+ config: z.object({ steps: z.array(z.object({ message: z.string() })) }).passthrough(),
533
+ });
534
+ const inputValue = {
535
+ config: { stopOnPayment: true, steps: [{ message: 'Привет', statusName: 'Новый' }] },
536
+ };
537
+ const actualSchema = z.toJSONSchema(inputSchema, { io: 'output' });
538
+
539
+ expect(schemaRetainsPath(actualSchema, 'config.stopOnPayment')).toBe(true);
540
+ expect(schemaRetainsPath(actualSchema, 'config.steps[].message')).toBe(true);
541
+ expect(schemaRetainsPath(actualSchema, 'config.steps[].statusName')).toBe(false);
542
+ expect(inputSchema.parse(inputValue)).toEqual({
543
+ config: { stopOnPayment: true, steps: [{ message: 'Привет' }] },
544
+ });
545
+ });
546
+
547
+ it('проверяет схему значений record и разыменовывает локальные ссылки', async () => {
548
+ const { z } = await import('zod');
549
+ const inputSchema = z.object({
550
+ config: z.record(z.string(), z.object({ message: z.string() })),
551
+ });
552
+ const actualSchema = z.toJSONSchema(inputSchema, { io: 'output' });
553
+
554
+ expect(schemaRetainsPath(actualSchema, 'config.first.message')).toBe(true);
555
+ expect(schemaRetainsPath(actualSchema, 'config.first.statusName')).toBe(false);
556
+ expect(
557
+ schemaRetainsPath(
558
+ {
559
+ properties: { config: { $ref: '#/$defs/config' } },
560
+ $defs: { config: { additionalProperties: true } },
561
+ },
562
+ 'config.stopOnPayment',
563
+ ),
564
+ ).toBe(true);
565
+ expect(
566
+ schemaRetainsPath(
567
+ { properties: { config: { additionalProperties: false } } },
568
+ 'config.stopOnPayment',
569
+ ),
570
+ ).toBe(false);
571
+ expect(() =>
572
+ schemaRetainsPath(
573
+ { properties: { config: { $ref: '#/$defs/missing' } } },
574
+ 'config.stopOnPayment',
575
+ ),
576
+ ).toThrow();
577
+ });
578
+
579
+ it('пересечение сохраняет поля из обеих частей результата разбора', async () => {
580
+ const { z } = await import('zod');
581
+ const inputSchema = z.intersection(
582
+ z.object({ config: z.object({ message: z.string() }) }),
583
+ z.object({ config: z.object({ stopOnPayment: z.boolean() }) }),
584
+ );
585
+ const inputValue = { config: { message: 'Привет', stopOnPayment: true } };
586
+ const actualSchema = z.toJSONSchema(inputSchema, { io: 'output' });
587
+
588
+ expect(schemaRetainsPath(actualSchema, 'config.message')).toBe(true);
589
+ expect(schemaRetainsPath(actualSchema, 'config.stopOnPayment')).toBe(true);
590
+ expect(schemaRetainsPath(actualSchema, 'config.statusName')).toBe(false);
591
+ expect(inputSchema.parse(inputValue)).toEqual(inputValue);
592
+ });
593
+
594
+ it('конечный элемент nullable-массива сохраняется на пределе, незавершённый спуск всё ещё ограничен', async () => {
595
+ const { z } = await import('zod');
596
+ const schema = z.object({
597
+ config: z.object({
598
+ steps: z.array(
599
+ z.object({
600
+ getcourse: z
601
+ .object({ delivery: z.object({ transports: z.array(z.string()) }).nullable() })
602
+ .nullable(),
603
+ }),
604
+ ),
605
+ }),
606
+ });
607
+ const derived = z.toJSONSchema(schema, { io: 'output' });
608
+ expect(schemaRetainsPath(derived, 'config.steps[].getcourse.delivery.transports[]')).toBe(true);
609
+ expect(() =>
610
+ schemaRetainsPath(derived, 'config.steps[].getcourse.delivery.transports[].extra'),
611
+ ).toThrow('глубже 8');
612
+ });
613
+
614
+ it('ни одно объявленное поле не теряется фактическим фильтром аргументов', async () => {
615
+ const { z } = await import('zod');
616
+ const { toolDefinitions } = await import('../src/tools/definitions.js');
617
+ const registry = JSON.parse(readFileSync(`${packageRoot}src/schemas/mcp-tools.json`, 'utf8'));
618
+ const declared = new Map(registry.tools.map((tool) => [tool.name, tool.inputSchema]));
619
+
620
+ const lost = [];
621
+ for (const definition of toolDefinitions) {
622
+ const schema = declared.get(definition.name);
623
+ if (schema === undefined) continue;
624
+ let derived;
625
+ try {
626
+ derived = z.toJSONSchema(definition.schema, { io: 'output' });
627
+ } catch {
628
+ // Transform может иметь преобразуемый вход и непредставимый выход. Для него сохраняем
629
+ // прежнюю проверку именованных путей: смена направления не должна уменьшать охват.
630
+ let inputDerived;
631
+ try {
632
+ inputDerived = z.toJSONSchema(definition.schema, { io: 'input' });
633
+ } catch {
634
+ // Непреобразуемые в обоих направлениях схемы по-прежнему сверяет registry.spec.
635
+ continue;
636
+ }
637
+ const inputPaths = new Set(schemaConstraints(inputDerived).keys());
638
+ for (const path of schemaConstraints(schema).keys()) {
639
+ if (!inputPaths.has(path)) lost.push(`${definition.name}::${path}`);
640
+ }
641
+ continue;
642
+ }
643
+ for (const path of schemaConstraints(schema).keys()) {
644
+ // Произвольный объект сохраняет поля без их поимённого перечисления. Известные вложенные
645
+ // объекты всё равно проверяются: у них может быть собственный срезающий фильтр.
646
+ if (!schemaRetainsPath(derived, path)) lost.push(`${definition.name}::${path}`);
647
+ }
648
+ }
649
+
650
+ expect(lost).toEqual([]);
651
+ });
652
+
653
+ it('перечни общих путей совпадают; односторонние перечни и отказы конвертера учтены поимённо', async () => {
654
+ const { z } = await import('zod');
655
+ const { toolDefinitions } = await import('../src/tools/definitions.js');
656
+ const declared = new Map(
657
+ JSON.parse(readFileSync(`${packageRoot}src/schemas/mcp-tools.json`, 'utf8')).tools.map(
658
+ (tool) => [tool.name, tool.inputSchema],
659
+ ),
660
+ );
661
+ const different = [],
662
+ oneSided = [],
663
+ conversionFailed = [];
664
+ let compared = 0;
665
+ for (const definition of toolDefinitions) {
666
+ if (!declared.has(definition.name)) continue;
667
+ let derived;
668
+ try {
669
+ derived = z.toJSONSchema(definition.schema, { io: 'input' });
670
+ } catch {
671
+ conversionFailed.push(definition.name);
672
+ continue;
673
+ }
674
+ const a = enumValuesByPath(declared.get(definition.name)),
675
+ b = enumValuesByPath(derived);
676
+ for (const path of new Set([...a.keys(), ...b.keys()])) {
677
+ const name = `${definition.name}::${path}`;
678
+ if (!a.has(path) || !b.has(path)) {
679
+ oneSided.push(name);
680
+ continue;
681
+ }
682
+ compared += 1;
683
+ if (JSON.stringify(a.get(path)) !== JSON.stringify(b.get(path)))
684
+ different.push({ name, registry: a.get(path), zod: b.get(path) });
685
+ }
686
+ }
687
+ // Замер импорта схем 01.10.2026: 274 общих пути, 4 односторонних, отказов конвертера нет.
688
+ // Обратное направление полей остаётся границей #1074; этот сторож держит именно значения перечней.
689
+ expect(compared).toBeGreaterThanOrEqual(274);
690
+ expect(conversionFailed.sort()).toEqual([]);
691
+ expect(oneSided.sort()).toEqual(Object.keys(ENUM_PARITY_ONE_SIDED).sort());
692
+ expect(different).toEqual([]);
693
+ });
694
+ });
695
+
696
+ const ENUM_PARITY_ONE_SIDED = {
697
+ 'besales_followup_sequence_upsert::config.filterMode':
698
+ 'config проходит через passthrough верхнего уровня; перечисление проверяет refineFollowUpConfig через reactivationConfigSchema.',
699
+ 'besales_followup_sequence_upsert::config.sequenceConditions[].op':
700
+ 'config проходит через passthrough верхнего уровня; перечисление проверяет refineFollowUpConfig через reactivationConfigSchema.',
701
+ 'besales_followup_sequence_upsert::config.steps[].anchorMode':
702
+ 'config проходит через passthrough верхнего уровня; перечисление проверяет refineFollowUpConfig через reactivationConfigSchema.',
703
+ 'besales_followup_sequence_upsert::config.steps[].conditions[].op':
704
+ 'config проходит через passthrough верхнего уровня; перечисление проверяет refineFollowUpConfig через reactivationConfigSchema.',
705
+ 'besales_followup_sequence_upsert::config.steps[].scheduleMode':
706
+ 'config проходит через passthrough верхнего уровня; перечисление проверяет refineFollowUpConfig через reactivationConfigSchema.',
707
+ 'besales_followup_sequence_upsert::config.steps[].stepType':
708
+ 'config проходит через passthrough верхнего уровня; перечисление проверяет refineFollowUpConfig через reactivationConfigSchema.',
709
+ 'besales_behavior_update::message_filters.schema_version':
710
+ 'В zod версия literal(1), в JSON — числовое поле версии.',
711
+ 'besales_platform_settings_update::crm_behavior.deal_marker_type':
712
+ 'JSON перечисляет маркировки, фактический фильтр принимает прежнюю строковую форму.',
713
+ 'besales_knowledge_table_patch::source.kind':
714
+ 'Прежний дискриминатор источника объявлен в JSON; неподдерживаемую запись отвергает код инструмента.',
715
+ };
716
+
717
+ describe('полный обход перечней', () => {
718
+ it('M1: реальный freshness_anchor чувствителен к потере и лишнему значению; null пропускается', () => {
719
+ const registry = JSON.parse(readFileSync(`${packageRoot}src/schemas/mcp-tools.json`, 'utf8'));
720
+ const original = registry.tools.find(
721
+ (entry) => entry.inputSchema.properties?.freshness_anchor !== undefined,
722
+ ).inputSchema;
723
+ const originalValues = enumValuesByPath(original).get('freshness_anchor');
724
+ expect(originalValues).toContain('FIRST_SESSION_END_PLUS_WAIT');
725
+ const lost = structuredClone(original),
726
+ extra = structuredClone(original),
727
+ nullable = structuredClone(original);
728
+ lost.properties.freshness_anchor.enum = lost.properties.freshness_anchor.enum.filter(
729
+ (value) => value !== 'FIRST_SESSION_END_PLUS_WAIT',
730
+ );
731
+ extra.properties.freshness_anchor.enum.push('UNDECLARED');
732
+ nullable.properties.freshness_anchor.enum.push(null);
733
+ expect(enumValuesByPath(lost).get('freshness_anchor')).not.toEqual(originalValues);
734
+ expect(enumValuesByPath(extra).get('freshness_anchor')).not.toEqual(originalValues);
735
+ expect(enumValuesByPath(nullable).get('freshness_anchor')).toEqual(originalValues);
736
+ });
737
+ it.each([false, true])(
738
+ 'потеря только скалярного значения видна (вложенное объединение=%s)',
739
+ (nested) => {
740
+ const scalar = { enum: ['x', 'y'] },
741
+ array = { type: 'array', items: { enum: ['x', 'y'] } };
742
+ const wrap = (node) => ({
743
+ properties: {
744
+ modes: { anyOf: nested ? [{ oneOf: [node, { type: 'null' }] }, array] : [node, array] },
745
+ },
746
+ });
747
+ const original = enumValuesByPath(wrap(scalar));
748
+ const mutation = enumValuesByPath(wrap({ enum: ['x'] }));
749
+ expect(original.get('modes')).toEqual(['x', 'y']);
750
+ expect(original.get('modes[]')).toEqual(['x', 'y']);
751
+ expect(mutation.get('modes[]')).toEqual(original.get('modes[]'));
752
+ expect(mutation.get('modes')).not.toEqual(original.get('modes'));
753
+ },
754
+ );
755
+ it('разворачивает refs, const и allOf, не считает null и не скрывает битый ref', () => {
756
+ expect(
757
+ enumValuesByPath({
758
+ $defs: { a: { enum: ['x', null] } },
759
+ properties: { a: { allOf: [{ $ref: '#/$defs/a' }, { const: 'y' }] } },
760
+ }).get('a'),
761
+ ).toEqual(['x', 'y']);
762
+ expect(() => enumValuesByPath({ properties: { a: { $ref: '#/$defs/missing' } } })).toThrow(
763
+ 'никуда не ведёт',
764
+ );
765
+ });
766
+ });
767
+
768
+ it('перечни реактивации проверяются по схеме refineFollowUpConfig, включая вложенные шаги', async () => {
769
+ const { z } = await import('zod');
770
+ const { reactivationConfigSchema } = await import('../src/tools/definitions/follow-up-config.js');
771
+ const registry = JSON.parse(readFileSync(`${packageRoot}src/schemas/mcp-tools.json`, 'utf8'));
772
+ const published = registry.tools.find((tool) => tool.name === 'besales_followup_sequence_upsert')
773
+ .inputSchema.properties.config;
774
+ const expected = enumValuesByPath(z.toJSONSchema(reactivationConfigSchema, { io: 'input' }));
775
+ const actual = enumValuesByPath(published);
776
+ for (const path of [
777
+ 'filterMode',
778
+ 'sequenceConditions[].op',
779
+ 'steps[].anchorMode',
780
+ 'steps[].conditions[].op',
781
+ 'steps[].scheduleMode',
782
+ 'steps[].stepType',
783
+ ]) {
784
+ expect(expected.has(path), path).toBe(true);
785
+ expect(actual.get(path), path).toEqual(expected.get(path));
786
+ }
787
+ });