@besales/mcp 0.31.0 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,84 @@
1
+ import { z } from 'zod';
2
+ export declare const reactivationConfigSchema: z.ZodObject<{
3
+ enabled: z.ZodOptional<z.ZodBoolean>;
4
+ filterMode: z.ZodOptional<z.ZodEnum<{
5
+ status: "status";
6
+ variable: "variable";
7
+ eventType: "eventType";
8
+ }>>;
9
+ sequenceConditions: z.ZodOptional<z.ZodArray<z.ZodObject<{
10
+ variable: z.ZodString;
11
+ op: z.ZodEnum<{
12
+ in: "in";
13
+ eq: "eq";
14
+ neq: "neq";
15
+ }>;
16
+ value: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
17
+ }, z.core.$loose>>>;
18
+ steps: z.ZodArray<z.ZodObject<{
19
+ stepType: z.ZodOptional<z.ZodEnum<{
20
+ message: "message";
21
+ webhook: "webhook";
22
+ }>>;
23
+ scheduleMode: z.ZodOptional<z.ZodEnum<{
24
+ offset: "offset";
25
+ clock: "clock";
26
+ }>>;
27
+ anchorMode: z.ZodOptional<z.ZodEnum<{
28
+ variable: "variable";
29
+ last_inbound: "last_inbound";
30
+ }>>;
31
+ anchorVariable: z.ZodOptional<z.ZodString>;
32
+ atTime: z.ZodOptional<z.ZodString>;
33
+ dayOffset: z.ZodOptional<z.ZodNumber>;
34
+ validityMinutes: z.ZodOptional<z.ZodNumber>;
35
+ slotGroup: z.ZodOptional<z.ZodString>;
36
+ conditions: z.ZodOptional<z.ZodArray<z.ZodObject<{
37
+ variable: z.ZodString;
38
+ op: z.ZodEnum<{
39
+ in: "in";
40
+ eq: "eq";
41
+ neq: "neq";
42
+ }>;
43
+ value: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
44
+ }, z.core.$loose>>>;
45
+ delayHours: z.ZodOptional<z.ZodNumber>;
46
+ delayMinutes: z.ZodOptional<z.ZodNumber>;
47
+ messageConfig: z.ZodOptional<z.ZodObject<{}, z.core.$loose>>;
48
+ }, z.core.$loose>>;
49
+ }, z.core.$loose>;
50
+ /**
51
+ * Применяет типизированную схему к `config` ТОЛЬКО для реактивационных цепочек.
52
+ *
53
+ * Через `z.union([reactivationConfigSchema, anyObjectSchema])` это делать нельзя: второй вариант
54
+ * принимает любой объект, поэтому union проходит всегда и ограничения (`steps.min(1)`,
55
+ * `filterMode` enum) не срабатывают ни разу — типизация выглядит рабочей, но её нет.
56
+ *
57
+ * @param input - Разобранный ввод тула
58
+ * @param ctx - Контекст zod для добавления ошибок
59
+ */
60
+ export declare const refineFollowUpConfig: (input: {
61
+ type?: unknown;
62
+ config?: unknown;
63
+ }, ctx: z.RefinementCtx) => void;
64
+ export declare const followUpSequenceListSchema: z.ZodObject<{
65
+ platform_id: z.ZodString;
66
+ sequence_id: z.ZodOptional<z.ZodString>;
67
+ include_config: z.ZodOptional<z.ZodBoolean>;
68
+ }, z.core.$strip>;
69
+ export declare const followUpStepStatsSchema: z.ZodObject<{
70
+ platform_id: z.ZodString;
71
+ sequence_id: z.ZodString;
72
+ from: z.ZodOptional<z.ZodString>;
73
+ to: z.ZodOptional<z.ZodString>;
74
+ }, z.core.$strip>;
75
+ export declare const followUpTools: (import("./shared.js").ToolDefinition<{
76
+ platform_id: string;
77
+ sequence_id?: string | undefined;
78
+ include_config?: boolean | undefined;
79
+ }> | import("./shared.js").ToolDefinition<{
80
+ platform_id: string;
81
+ sequence_id: string;
82
+ from?: string | undefined;
83
+ to?: string | undefined;
84
+ }>)[];
@@ -0,0 +1,161 @@
1
+ import { z } from 'zod';
2
+ import { anyObjectSchema, buildListQuery, defineTool, uuidSchema, withInstructions, } from './shared.js';
3
+ /**
4
+ * Схема конфига реактивационной цепочки.
5
+ *
6
+ * ⚠️ Ключи ВНУТРИ `config` — camelCase, в отличие от snake_case параметров тулов: `config`
7
+ * уходит в тело запроса БЕЗ преобразования, а рантайм читает `anchorMode`/`anchorVariable`/
8
+ * `slotGroup`. Назвать их в snake_case значило бы сохранить конфиг, который рантайм не
9
+ * прочитает: календарный шаг молча стал бы обычным offset-шагом.
10
+ *
11
+ * ⚠️ `.passthrough()` на каждом уровне обязателен по двум причинам: legacy-конфиги несут поля
12
+ * вне этой схемы (`finalAction`, `maxCycleCount`, `webhookConfig`, …), и сама схема исполняется
13
+ * на машине оператора — она обновляется независимо от бэкенда и не должна отвергать конфиг,
14
+ * который бэкенд принял бы.
15
+ */
16
+ const variableConditionSchema = z
17
+ .object({
18
+ variable: z.string().describe('Имя per-contact переменной'),
19
+ op: z.enum(['eq', 'neq', 'in']),
20
+ value: z
21
+ .union([z.string(), z.array(z.string())])
22
+ .describe("Строка для eq/neq, непустой массив непустых строк для 'in'"),
23
+ })
24
+ .passthrough();
25
+ const reactivationStepSchema = z
26
+ .object({
27
+ stepType: z.enum(['message', 'webhook']).optional(),
28
+ scheduleMode: z
29
+ .enum(['offset', 'clock'])
30
+ .optional()
31
+ .describe("'clock' — время суток + сдвиг дней вместо задержки от последней активности"),
32
+ anchorMode: z
33
+ .enum(['last_inbound', 'variable'])
34
+ .optional()
35
+ .describe("'variable' — расписание считается от даты из переменной контакта (календарная цепочка)"),
36
+ anchorVariable: z
37
+ .string()
38
+ .optional()
39
+ .describe('Имя переменной с якорной датой. Обязательно при anchorMode=variable; переменная должна ' +
40
+ 'существовать у платформы и НЕ быть константой'),
41
+ atTime: z.string().optional().describe('Время суток "HH:MM" для scheduleMode=clock'),
42
+ dayOffset: z
43
+ .number()
44
+ .int()
45
+ .min(0)
46
+ .max(30)
47
+ .optional()
48
+ .describe('Сдвиг в днях от якоря для scheduleMode=clock'),
49
+ validityMinutes: z
50
+ .number()
51
+ .optional()
52
+ .describe('Окно валидности слота. Меньше 10 минут крон почти всегда промахнётся'),
53
+ slotGroup: z
54
+ .string()
55
+ .optional()
56
+ .describe('Метка взаимоисключающих вариантов одного момента (first-match-wins). Варианты ОБЯЗАНЫ ' +
57
+ 'идти в steps подряд: разнесённые по цепочке перестают быть взаимоисключающими'),
58
+ conditions: z
59
+ .array(variableConditionSchema)
60
+ .max(3)
61
+ .optional()
62
+ .describe('Условия на переменные контакта; не прошли — шаг пропускается'),
63
+ delayHours: z.number().optional(),
64
+ delayMinutes: z.number().optional(),
65
+ messageConfig: anyObjectSchema.optional(),
66
+ })
67
+ .passthrough();
68
+ export const reactivationConfigSchema = z
69
+ .object({
70
+ enabled: z.boolean().optional(),
71
+ filterMode: z
72
+ .enum(['status', 'eventType', 'variable'])
73
+ .optional()
74
+ .describe('Режим отбора контактов. Неизвестное значение НЕ включает ни один фильтр — цепочка уйдёт ' +
75
+ 'всем контактам канала, поэтому бэкенд отвергает такой конфиг'),
76
+ sequenceConditions: z
77
+ .array(variableConditionSchema)
78
+ .optional()
79
+ .describe("Обязателен и непуст при filterMode='variable'"),
80
+ steps: z.array(reactivationStepSchema).min(1),
81
+ })
82
+ .passthrough();
83
+ /**
84
+ * Применяет типизированную схему к `config` ТОЛЬКО для реактивационных цепочек.
85
+ *
86
+ * Через `z.union([reactivationConfigSchema, anyObjectSchema])` это делать нельзя: второй вариант
87
+ * принимает любой объект, поэтому union проходит всегда и ограничения (`steps.min(1)`,
88
+ * `filterMode` enum) не срабатывают ни разу — типизация выглядит рабочей, но её нет.
89
+ *
90
+ * @param input - Разобранный ввод тула
91
+ * @param ctx - Контекст zod для добавления ошибок
92
+ */
93
+ export const refineFollowUpConfig = (input, ctx) => {
94
+ if (input.type !== 'INACTIVE_USER_REACTIVATION') {
95
+ return;
96
+ }
97
+ const parsed = reactivationConfigSchema.safeParse(input.config);
98
+ if (parsed.success) {
99
+ return;
100
+ }
101
+ for (const issue of parsed.error.issues) {
102
+ ctx.addIssue({
103
+ code: z.ZodIssueCode.custom,
104
+ path: ['config', ...issue.path],
105
+ message: issue.message,
106
+ });
107
+ }
108
+ };
109
+ export const followUpSequenceListSchema = z.object({
110
+ platform_id: uuidSchema,
111
+ sequence_id: uuidSchema
112
+ .optional()
113
+ .describe('Вернуть одну цепочку вместе с полным конфигом'),
114
+ include_config: z
115
+ .boolean()
116
+ .optional()
117
+ .describe('Отдать конфиги всех цепочек. По умолчанию не отдаются — они объёмные'),
118
+ });
119
+ export const followUpStepStatsSchema = z.object({
120
+ platform_id: uuidSchema,
121
+ sequence_id: uuidSchema,
122
+ from: z.string().optional().describe('Начало окна, ISO-дата. По умолчанию 30 дней назад'),
123
+ to: z.string().optional().describe('Конец окна, ISO-дата. По умолчанию сейчас'),
124
+ });
125
+ export const followUpTools = [
126
+ defineTool({
127
+ name: 'besales_followup_sequence_list',
128
+ requiredFields: ['platform_id'],
129
+ schema: followUpSequenceListSchema,
130
+ // Параметры клеим в путь: транспорт (`ApiToolRequest`) знает только method/path/body, и
131
+ // отдельное поле `query` было бы молча выброшено — тул вернул бы 200 без учёта фильтров.
132
+ createRequest: (input) => ({
133
+ method: 'GET',
134
+ path: `/platforms/${input.platform_id}/follow-up/sequences${buildListQuery({
135
+ sequence_id: input.sequence_id,
136
+ include_config: input.include_config,
137
+ })}`,
138
+ }),
139
+ enhanceResponse: (response) => withInstructions(response, [
140
+ 'besales_followup_sequence_upsert заменяет config ЦЕЛИКОМ: прочитай конфиг здесь (sequence_id) и пришли ПОЛНЫЙ желаемый набор шагов, а не дельту.',
141
+ 'Без sequence_id и include_config конфиги не отдаются — так список остаётся дешёвым по контексту.',
142
+ ]),
143
+ }),
144
+ defineTool({
145
+ name: 'besales_followup_step_stats',
146
+ requiredFields: ['platform_id', 'sequence_id'],
147
+ schema: followUpStepStatsSchema,
148
+ createRequest: (input) => ({
149
+ method: 'GET',
150
+ path: `/platforms/${input.platform_id}/follow-up/sequences/${input.sequence_id}/step-stats${buildListQuery({ from: input.from, to: input.to })}`,
151
+ }),
152
+ enhanceResponse: (response) => withInstructions(response, [
153
+ 'wasSent=false НЕ означает «отправка не удалась»: SEND_FAILED пишется только если до fence не дошло (мьют, стадия CRM, тихий канал).',
154
+ 'SHADOWED — вариант слота, который не оценивался, потому что слот уже закрыт отправкой другого варианта. Это не ошибка.',
155
+ 'SENT_UNVERIFIED — отправка зависла после fence: исходом владеет recovery, повтор запрещён.',
156
+ 'waitingUpperBound — ВЕРХНЯЯ оценка ожидающих: прогресс создаётся всем включённым цепочкам платформы без проверки sequenceConditions.',
157
+ 'Окно по умолчанию — 30 дней; для более раннего периода передай from/to.',
158
+ ]),
159
+ }),
160
+ ];
161
+ //# sourceMappingURL=follow-up.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"follow-up.js","sourceRoot":"","sources":["../../../src/tools/definitions/follow-up.ts"],"names":[],"mappings":"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"}
@@ -65,7 +65,7 @@ export declare const knowledgeTools: (import("./shared.js").ToolDefinition<{
65
65
  }> | import("./shared.js").ToolDefinition<{
66
66
  knowledge_space_id: string;
67
67
  workspace_id?: string | undefined;
68
- status?: "LOADED" | "SPLIT" | "EMBEDDINGS_GENERATED" | "VECTOR_DB_UPLOADED" | "COMPLETED" | "FAILED" | "TEXT_EXTRACTED" | "CHUNKS_SAVED" | undefined;
68
+ status?: "FAILED" | "LOADED" | "SPLIT" | "EMBEDDINGS_GENERATED" | "VECTOR_DB_UPLOADED" | "COMPLETED" | "TEXT_EXTRACTED" | "CHUNKS_SAVED" | undefined;
69
69
  page?: number | undefined;
70
70
  limit?: number | undefined;
71
71
  }> | import("./shared.js").ToolDefinition<{
@@ -149,7 +149,7 @@ export declare const knowledgeTools: (import("./shared.js").ToolDefinition<{
149
149
  }> | import("./shared.js").ToolDefinition<{
150
150
  knowledge_space_id: string;
151
151
  workspace_id?: string | undefined;
152
- status?: "COMPLETED" | "FAILED" | "PENDING" | "CRAWLING" | "PROCESSING" | undefined;
152
+ status?: "PENDING" | "FAILED" | "COMPLETED" | "CRAWLING" | "PROCESSING" | undefined;
153
153
  page?: number | undefined;
154
154
  limit?: number | undefined;
155
155
  }> | import("./shared.js").ToolDefinition<{
@@ -44,6 +44,51 @@ export declare const platformSetupTools: (import("./shared.js").ToolDefinition<{
44
44
  }> | import("./shared.js").ToolDefinition<{
45
45
  job_id: string;
46
46
  workspace_id?: string | undefined;
47
+ }> | import("./shared.js").ToolDefinition<{
48
+ platform_id: string;
49
+ crm_id: string;
50
+ channels: string[];
51
+ contacts: {
52
+ contact_id: string;
53
+ deal_id?: string | undefined;
54
+ transport?: string | undefined;
55
+ dialog_id?: string | undefined;
56
+ lead_last_active_at?: string | undefined;
57
+ segment_entered_at?: string | undefined;
58
+ }[];
59
+ workspace_id?: string | undefined;
60
+ launch_type?: "status" | undefined;
61
+ funnel?: string | undefined;
62
+ segment?: string | undefined;
63
+ cohort_label?: string | undefined;
64
+ message_variant?: string | undefined;
65
+ found_count?: number | undefined;
66
+ product_code?: string | undefined;
67
+ product_name?: string | undefined;
68
+ deal_fields?: Record<string, string | number | boolean | null> | undefined;
69
+ confirm?: boolean | undefined;
70
+ check_messenger_presence?: boolean | undefined;
71
+ dedup_window_days?: number | undefined;
72
+ dedup_scope?: "any" | "same_funnel" | undefined;
73
+ preflight?: {
74
+ enabled?: boolean | undefined;
75
+ product_title?: string | undefined;
76
+ product_match_key?: string | undefined;
77
+ offer_id?: number | undefined;
78
+ other_product_active_deal_mode?: "warn" | "block" | "ignore" | undefined;
79
+ } | undefined;
80
+ chunk_size?: number | undefined;
81
+ explicit_confirmation?: true | undefined;
82
+ confirmation_reason?: string | undefined;
83
+ }> | import("./shared.js").ToolDefinition<{
84
+ run_id: string;
85
+ workspace_id?: string | undefined;
86
+ }> | import("./shared.js").ToolDefinition<{
87
+ run_id: string;
88
+ workspace_id?: string | undefined;
89
+ status?: "PENDING" | "CLAIMED" | "SENT" | "SKIPPED" | "FAILED" | "UNKNOWN" | undefined;
90
+ cursor?: string | undefined;
91
+ limit?: number | undefined;
47
92
  }> | import("./shared.js").ToolDefinition<{
48
93
  platform_id: string;
49
94
  workspace_id?: string | undefined;
@@ -472,6 +517,10 @@ export declare const platformSetupTools: (import("./shared.js").ToolDefinition<{
472
517
  manual_mute_field_id?: string | null | undefined;
473
518
  manual_mute_value?: string | null | undefined;
474
519
  manual_mute_active_value?: string | null | undefined;
520
+ create_missing_deal_enabled?: boolean | undefined;
521
+ create_missing_deal_offer_id?: number | null | undefined;
522
+ create_missing_deal_funnel_id?: number | null | undefined;
523
+ create_missing_deal_stage_id?: number | null | undefined;
475
524
  } | undefined;
476
525
  }> | import("./shared.js").ToolDefinition<{
477
526
  audit_log_id: string;
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { refineFollowUpConfig } from './follow-up.js';
2
3
  import { agentOutputConfigSchema, agentRuntimeConfigSchema, agentScopeSchema, agentToolPatchSchema, anyObjectSchema, behaviorErrorHandlingSchema, behaviorGreetingSchema, behaviorMediaHandlingSchema, behaviorMessageFilterSchema, behaviorMessageSplittingSchema, behaviorOperatorScheduleSchema, behaviorPrivacySchema, behaviorTypingBehaviorSchema, behaviorUrlHandlingSchema, compactObject, defineTool, feedbackSheetRowSchema, followUpSequenceTypeSchema, followUpTriggerSchema, mapBehaviorErrorHandlingInput, mapBehaviorGreetingInput, mapBehaviorMediaHandlingInput, mapBehaviorOperatorScheduleInput, mapBehaviorTypingInput, mapBehaviorUrlHandlingInput, mapOutputConfigInput, mapPipelineAutoFinishInput, mapPipelineCrmBehaviorInput, mapPlatformAccessControlInput, mapPlatformBitrix24BotSettingsInput, mapPlatformCrmBehaviorInput, mapPlatformGeneralSettingsInput, mapPlatformGetcourseSettingsInput, mapPlatformInstagramCommentInput, mapPlatformSalebotSettingsInput, mapPlatformSmsgorodSettingsInput, mapPlatformVariablesConfigInput, mapPlatformWidgetMarketingInput, mapRuntimeConfigInput, modelLaneSchema, pipelineAutoFinishSchema, pipelineCrmBehaviorSchema, platformAccessControlSchema, platformBitrix24BotSettingsSchema, platformCrmBehaviorSchema, platformGeneralSettingsSchema, platformGetcourseSettingsSchema, platformInstagramCommentSchema, platformSalebotSettingsSchema, platformSetupConfirmationSchema, platformSmsgorodSettingsSchema, platformVariablesConfigSchema, platformWidgetMarketingSchema, routingRuleConditionSchema, routingRuleLeadStatusSchema, routingStrategySchema, setupConfirmation, triggerScopeSchema, uuidSchema, withInstructions, } from './shared.js';
3
4
  export const platformSetupTools = [
4
5
  defineTool({
@@ -204,6 +205,186 @@ export const platformSetupTools = [
204
205
  'preflight.blocked/errors — контакты, которым НЕ отправляли; BroadcastRecipient для них не создаётся, поэтому received в аналитике не раздувается.',
205
206
  ]),
206
207
  }),
208
+ defineTool({
209
+ name: 'besales_getcourse_wave_run_start',
210
+ requiredFields: ['platform_id', 'crm_id', 'channels', 'contacts'],
211
+ defaultWorkspaceId: true,
212
+ schema: z
213
+ .object({
214
+ workspace_id: uuidSchema.optional(),
215
+ platform_id: uuidSchema,
216
+ crm_id: uuidSchema,
217
+ launch_type: z.literal('status').optional(),
218
+ channels: z.array(z.string()).min(1),
219
+ funnel: z.string().max(200).optional(),
220
+ segment: z.string().max(200).optional(),
221
+ cohort_label: z.string().max(200).optional(),
222
+ message_variant: z.string().max(120).optional(),
223
+ found_count: z.number().int().min(0).max(10_000_000).optional(),
224
+ product_code: z
225
+ .string()
226
+ .trim()
227
+ .max(100)
228
+ .regex(/^[^\s/]+$/, 'product_code: без пробелов и «/»')
229
+ .optional(),
230
+ product_name: z.string().max(200).optional(),
231
+ deal_fields: z
232
+ .record(z.string(), z.union([z.string(), z.number(), z.boolean(), z.null()]))
233
+ .optional(),
234
+ contacts: z
235
+ .array(z.object({
236
+ contact_id: z.string(),
237
+ deal_id: z.string().optional(),
238
+ transport: z.string().optional(),
239
+ dialog_id: z.string().optional(),
240
+ lead_last_active_at: z.string().datetime({ offset: true }).optional(),
241
+ segment_entered_at: z.string().datetime({ offset: true }).optional(),
242
+ }))
243
+ .min(1)
244
+ .max(5000),
245
+ confirm: z.boolean().optional(),
246
+ check_messenger_presence: z.boolean().optional(),
247
+ dedup_window_days: z.number().int().min(0).max(365).optional(),
248
+ dedup_scope: z.enum(['any', 'same_funnel']).optional(),
249
+ preflight: z
250
+ .object({
251
+ enabled: z.boolean().optional(),
252
+ product_title: z.string().max(500).optional(),
253
+ product_match_key: z.string().max(500).optional(),
254
+ offer_id: z.number().int().positive().optional(),
255
+ other_product_active_deal_mode: z.enum(['warn', 'block', 'ignore']).optional(),
256
+ })
257
+ .optional(),
258
+ chunk_size: z.number().int().min(1).max(500).optional(),
259
+ explicit_confirmation: z.literal(true).optional(),
260
+ confirmation_reason: z.string().optional(),
261
+ })
262
+ .refine((input) => !input.confirm || input.explicit_confirmation === true, {
263
+ message: 'Реальный запуск требует explicit_confirmation=true: отправка волны необратима',
264
+ path: ['explicit_confirmation'],
265
+ }),
266
+ createRequest: (input) => ({
267
+ method: 'POST',
268
+ path: `/workspaces/${input.workspace_id}/getcourse-wave-runs`,
269
+ body: compactObject({
270
+ platformId: input.platform_id,
271
+ crmId: input.crm_id,
272
+ launchType: input.launch_type,
273
+ channels: input.channels,
274
+ funnel: input.funnel,
275
+ segment: input.segment,
276
+ cohortLabel: input.cohort_label,
277
+ messageVariant: input.message_variant,
278
+ foundCount: input.found_count,
279
+ productCode: input.product_code,
280
+ productName: input.product_name,
281
+ dealFields: input.deal_fields,
282
+ contacts: input.contacts?.map((contact) => compactObject({
283
+ contactId: contact.contact_id,
284
+ dealId: contact.deal_id,
285
+ transport: contact.transport,
286
+ dialogId: contact.dialog_id,
287
+ leadLastActiveAt: contact.lead_last_active_at,
288
+ segmentEnteredAt: contact.segment_entered_at,
289
+ })),
290
+ confirm: input.confirm,
291
+ checkMessengerPresence: input.check_messenger_presence,
292
+ dedupWindowDays: input.dedup_window_days,
293
+ dedupScope: input.dedup_scope,
294
+ preflight: input.preflight
295
+ ? compactObject({
296
+ enabled: input.preflight.enabled,
297
+ productTitle: input.preflight.product_title,
298
+ productMatchKey: input.preflight.product_match_key,
299
+ offerId: input.preflight.offer_id,
300
+ otherProductActiveDealMode: input.preflight.other_product_active_deal_mode,
301
+ })
302
+ : undefined,
303
+ chunkSize: input.chunk_size,
304
+ }),
305
+ }),
306
+ enhanceResponse: (response) => withInstructions(response, [
307
+ 'Durable-прогон сам режет когорту на куски и отправляет их последовательно, сам ждёт освобождения лока импорта и переживает рестарт сервиса. Опрашивай ОДИН прогон через besales_getcourse_wave_run_status, не собирай волну вручную.',
308
+ 'БЕЗ confirm=true — DRY-RUN: возвращается проверка, но прогон НЕ создаётся и ничего не отправляется.',
309
+ 'Реальный запуск требует ОДНОВРЕМЕННО confirm=true и explicit_confirmation=true. explicit_confirmation — MCP-гейт явного согласия оператора и в HTTP-запрос не передаётся.',
310
+ 'Поддерживается ТОЛЬКО launch_type=status. Для board/group используй старый besales_getcourse_wave_import.',
311
+ 'HTTP 409 означает, что на платформе уже идёт активный прогон: в ответе есть его runId — не создавай новую волну, опрашивай этот runId.',
312
+ 'Статус прогона отдаёт только агрегаты. Полный построчный разбор получай через besales_getcourse_wave_run_contacts с курсорной пагинацией.',
313
+ 'Исходы контактов: SENT — отправлено; SKIPPED — дедуп, нет мессенджера или preflight заблокировал; FAILED — построчная ошибка; UNKNOWN — исход НЕИЗВЕСТЕН: кусок был прерван, но отправка могла состояться.',
314
+ 'UNKNOWN и FAILED НИКОГДА не переотправляются автоматически. Повторная отправка таких контактов — только осознанное решение оператора отдельной волной: отсутствие строки получателя не доказывает отсутствие отправки.',
315
+ ]),
316
+ }),
317
+ defineTool({
318
+ name: 'besales_getcourse_wave_run_status',
319
+ requiredFields: ['run_id'],
320
+ defaultWorkspaceId: true,
321
+ schema: z.object({
322
+ workspace_id: uuidSchema.optional(),
323
+ run_id: uuidSchema,
324
+ }),
325
+ createRequest: (input) => ({
326
+ method: 'GET',
327
+ path: `/workspaces/${input.workspace_id}/getcourse-wave-runs/${input.run_id}`,
328
+ }),
329
+ enhanceResponse: (response) => withInstructions(response, [
330
+ 'Это единый агрегированный статус durable-прогона: он сам последовательно обрабатывает куски, ждёт лок импорта и продолжает после рестарта сервиса. Не собирай и не запускай куски вручную.',
331
+ 'Статус содержит только агрегаты. Для полного построчного разбора используй besales_getcourse_wave_run_contacts с курсорной пагинацией.',
332
+ 'SENT — отправлено; SKIPPED — дедуп, нет мессенджера или блокировка preflight; FAILED — построчная ошибка; UNKNOWN — исход НЕИЗВЕСТЕН после прерванного куска, отправка могла состояться.',
333
+ 'UNKNOWN и FAILED НИКОГДА не переотправляются автоматически. Решение о повторе принимает оператор и запускает отдельную волну, потому что отсутствие строки получателя не доказывает отсутствие отправки.',
334
+ ]),
335
+ }),
336
+ defineTool({
337
+ name: 'besales_getcourse_wave_run_contacts',
338
+ requiredFields: ['run_id'],
339
+ defaultWorkspaceId: true,
340
+ schema: z.object({
341
+ workspace_id: uuidSchema.optional(),
342
+ run_id: uuidSchema,
343
+ status: z
344
+ .enum(['PENDING', 'CLAIMED', 'SENT', 'SKIPPED', 'FAILED', 'UNKNOWN'])
345
+ .optional(),
346
+ cursor: uuidSchema.optional(),
347
+ limit: z.number().int().min(1).max(500).optional(),
348
+ }),
349
+ createRequest: (input) => {
350
+ const params = new URLSearchParams();
351
+ if (input.status)
352
+ params.set('status', input.status);
353
+ if (input.cursor)
354
+ params.set('cursor', input.cursor);
355
+ if (input.limit !== undefined)
356
+ params.set('limit', String(input.limit));
357
+ const query = params.toString();
358
+ return {
359
+ method: 'GET',
360
+ path: `/workspaces/${input.workspace_id}/getcourse-wave-runs/${input.run_id}/contacts${query ? `?${query}` : ''}`,
361
+ };
362
+ },
363
+ enhanceResponse: (response) => withInstructions(response, [
364
+ 'Полный построчный разбор durable-прогона. Продолжай курсорную пагинацию по nextCursor до конца; агрегированный besales_getcourse_wave_run_status строк контактов не возвращает.',
365
+ 'Фильтр status: PENDING | CLAIMED | SENT | SKIPPED | FAILED | UNKNOWN. SKIPPED означает дедуп, отсутствие мессенджера или блокировку preflight; FAILED — построчную ошибку.',
366
+ 'UNKNOWN означает, что исход НЕИЗВЕСТЕН: кусок был прерван, но отправка могла состояться. UNKNOWN и FAILED НИКОГДА не переотправляются автоматически.',
367
+ 'Повтор UNKNOWN/FAILED допустим только осознанным решением оператора отдельной волной: отсутствие строки получателя не доказывает отсутствие отправки.',
368
+ ]),
369
+ }),
370
+ defineTool({
371
+ name: 'besales_getcourse_wave_run_cancel',
372
+ requiredFields: ['run_id'],
373
+ defaultWorkspaceId: true,
374
+ schema: z.object({
375
+ workspace_id: uuidSchema.optional(),
376
+ run_id: uuidSchema,
377
+ }),
378
+ createRequest: (input) => ({
379
+ method: 'POST',
380
+ path: `/workspaces/${input.workspace_id}/getcourse-wave-runs/${input.run_id}/cancel`,
381
+ }),
382
+ enhanceResponse: (response) => withInstructions(response, [
383
+ 'Отмена запрещает прогону заявлять новые куски. Уже заявленный кусок может завершиться; после отмены опроси besales_getcourse_wave_run_status.',
384
+ 'Для полного построчного результата используй besales_getcourse_wave_run_contacts с курсорной пагинацией; статус прогона отдаёт только агрегаты.',
385
+ 'UNKNOWN — исход НЕИЗВЕСТЕН после прерванного куска, отправка могла состояться. UNKNOWN и FAILED автоматически не переотправляются; повтор возможен только отдельной волной по осознанному решению оператора.',
386
+ ]),
387
+ }),
207
388
  defineTool({
208
389
  name: 'besales_wazzup_templates_list',
209
390
  requiredFields: ['platform_id'],
@@ -688,7 +869,8 @@ export const platformSetupTools = [
688
869
  trigger: followUpTriggerSchema,
689
870
  config: anyObjectSchema,
690
871
  ...platformSetupConfirmationSchema,
691
- }),
872
+ })
873
+ .superRefine(refineFollowUpConfig),
692
874
  createRequest: (input) => ({
693
875
  method: input.sequence_id ? 'PATCH' : 'POST',
694
876
  path: input.sequence_id