@besales/mcp 0.42.0 → 0.45.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.
Files changed (43) hide show
  1. package/dist/instructions/server-instructions.d.ts +1 -1
  2. package/dist/instructions/server-instructions.js +5 -2
  3. package/dist/instructions/server-instructions.js.map +1 -1
  4. package/dist/schemas/mcp-tools.json +837 -25
  5. package/dist/tools/definitions/broadcast-analytics.js +5 -2
  6. package/dist/tools/definitions/broadcast-analytics.js.map +1 -1
  7. package/dist/tools/definitions/broadcast-mailing-variants.d.ts +2 -0
  8. package/dist/tools/definitions/broadcast-mailing-variants.js +12 -3
  9. package/dist/tools/definitions/broadcast-mailing-variants.js.map +1 -1
  10. package/dist/tools/definitions/broadcast-scheduler.d.ts +69 -0
  11. package/dist/tools/definitions/broadcast-scheduler.js +258 -0
  12. package/dist/tools/definitions/broadcast-scheduler.js.map +1 -0
  13. package/dist/tools/definitions/follow-up-setup.d.ts +6 -0
  14. package/dist/tools/definitions/follow-up-setup.js +31 -0
  15. package/dist/tools/definitions/follow-up-setup.js.map +1 -1
  16. package/dist/tools/definitions/follow-up.d.ts +18 -1
  17. package/dist/tools/definitions/follow-up.js +78 -5
  18. package/dist/tools/definitions/follow-up.js.map +1 -1
  19. package/dist/tools/definitions/getcourse-anchor-refresh.d.ts +30 -0
  20. package/dist/tools/definitions/getcourse-anchor-refresh.js +95 -0
  21. package/dist/tools/definitions/getcourse-anchor-refresh.js.map +1 -0
  22. package/dist/tools/definitions/getcourse-cold-welcome.d.ts +8 -0
  23. package/dist/tools/definitions/getcourse-cold-welcome.js +42 -4
  24. package/dist/tools/definitions/getcourse-cold-welcome.js.map +1 -1
  25. package/dist/tools/definitions/getcourse-wave.d.ts +3 -3
  26. package/dist/tools/definitions/getcourse-wave.js +5 -5
  27. package/dist/tools/definitions/getcourse-wave.js.map +1 -1
  28. package/dist/tools/definitions/platform-setup.d.ts +9 -3
  29. package/dist/tools/definitions/platform-setup.js +38 -1
  30. package/dist/tools/definitions/platform-setup.js.map +1 -1
  31. package/dist/tools/definitions/shared.d.ts +14 -0
  32. package/dist/tools/definitions/shared.js +11 -0
  33. package/dist/tools/definitions/shared.js.map +1 -1
  34. package/dist/tools/definitions.d.ts +96 -3
  35. package/dist/tools/definitions.js +4 -0
  36. package/dist/tools/definitions.js.map +1 -1
  37. package/dist/tools/registry-mapping-cases-platform.js +29 -0
  38. package/dist/tools/registry-mapping-cases-platform.js.map +1 -1
  39. package/dist/tools/trigger-action-config.d.ts +49 -0
  40. package/dist/tools/trigger-action-config.js +167 -0
  41. package/dist/tools/trigger-action-config.js.map +1 -0
  42. package/dist/types/api-contract.gen.d.ts +1289 -164
  43. package/package.json +1 -1
@@ -42,9 +42,21 @@ export declare const reactivationConfigSchema: z.ZodObject<{
42
42
  }>;
43
43
  value: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
44
44
  }, z.core.$loose>>>;
45
+ skipIfContactRepliedSincePreviousSlot: z.ZodOptional<z.ZodBoolean>;
45
46
  delayHours: z.ZodOptional<z.ZodNumber>;
46
47
  delayMinutes: z.ZodOptional<z.ZodNumber>;
47
- messageConfig: z.ZodOptional<z.ZodObject<{}, z.core.$loose>>;
48
+ messageConfig: z.ZodOptional<z.ZodObject<{
49
+ type: z.ZodOptional<z.ZodNullable<z.ZodString>>;
50
+ content: z.ZodOptional<z.ZodNullable<z.ZodString>>;
51
+ templateId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
52
+ templateValues: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodUnknown>>>;
53
+ contentMode: z.ZodOptional<z.ZodNullable<z.ZodString>>;
54
+ generationContext: z.ZodOptional<z.ZodNullable<z.ZodString>>;
55
+ fallbackContent: z.ZodOptional<z.ZodNullable<z.ZodString>>;
56
+ attachments: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodUnknown>>>;
57
+ buttons: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodUnknown>>>;
58
+ crmNote: z.ZodOptional<z.ZodNullable<z.ZodString>>;
59
+ }, z.core.$loose>>;
48
60
  getcourse: z.ZodOptional<z.ZodObject<{
49
61
  delivery: z.ZodOptional<z.ZodObject<{
50
62
  mode: z.ZodEnum<{
@@ -67,6 +79,9 @@ export declare const reactivationConfigSchema: z.ZodObject<{
67
79
  }, z.core.$loose>>;
68
80
  }, z.core.$loose>>;
69
81
  }, z.core.$loose>>;
82
+ sessionTimeVariable: z.ZodOptional<z.ZodString>;
83
+ sessionDayOffsets: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
84
+ sessionDurationMinutes: z.ZodOptional<z.ZodNumber>;
70
85
  }, z.core.$loose>;
71
86
  /**
72
87
  * Применяет типизированную схему к `config` ТОЛЬКО для реактивационных цепочек.
@@ -99,6 +114,7 @@ export declare const followUpReactionsSchema: z.ZodObject<{
99
114
  to: z.ZodString;
100
115
  sequence_id: z.ZodOptional<z.ZodArray<z.ZodString>>;
101
116
  horizon_minutes: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<30>, z.ZodLiteral<60>, z.ZodLiteral<90>, z.ZodLiteral<180>, z.ZodLiteral<360>, z.ZodLiteral<720>, z.ZodLiteral<1440>]>>;
117
+ run_window_days: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<1>, z.ZodLiteral<3>, z.ZodLiteral<5>, z.ZodLiteral<7>, z.ZodLiteral<14>]>>;
102
118
  segment: z.ZodOptional<z.ZodArray<z.ZodString>>;
103
119
  product: z.ZodOptional<z.ZodArray<z.ZodString>>;
104
120
  channel: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -118,6 +134,7 @@ export declare const followUpTools: (import("./shared.js").ToolDefinition<{
118
134
  to: string;
119
135
  sequence_id?: string[] | undefined;
120
136
  horizon_minutes?: 30 | 60 | 90 | 180 | 360 | 720 | 1440 | undefined;
137
+ run_window_days?: 3 | 1 | 5 | 7 | 14 | undefined;
121
138
  segment?: string[] | undefined;
122
139
  product?: string[] | undefined;
123
140
  channel?: string[] | undefined;
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { anyObjectSchema, buildListQuery, defineTool, uuidSchema, withInstructions, } from './shared.js';
2
+ import { buildListQuery, defineTool, uuidSchema, withInstructions, } from './shared.js';
3
3
  /**
4
4
  * Схема конфига реактивационной цепочки.
5
5
  *
@@ -22,6 +22,47 @@ const variableConditionSchema = z
22
22
  .describe("Строка для eq/neq, непустой массив непустых строк для 'in'"),
23
23
  })
24
24
  .passthrough();
25
+ /**
26
+ * Сообщение шага. Поля перечислены поимённо не ради валидации, а ради ОБНАРУЖИМОСТИ: раньше
27
+ * здесь стоял безымянный объект, и «как задать в шаге WABA-шаблон вместо текста» неоткуда было
28
+ * узнать — форма не описана ни в схеме тула, ни в OpenAPI.
29
+ *
30
+ * `.passthrough()` обязателен: конфиги несут поля вне этого списка (wabaVariableMapping и др.),
31
+ * а схема живёт на машине оператора и обновляется независимо от бэкенда.
32
+ */
33
+ const followUpMessageConfigSchema = z
34
+ .object({
35
+ type: z
36
+ .string()
37
+ .nullish()
38
+ .describe("'text' — обычный текст в content; 'template' — ШАБЛОН WABA (Wazzup), тогда обязателен " +
39
+ "templateId; 'media' — вложения. Шаблон уходит и при ЗАКРЫТОМ 24-часовом окне " +
40
+ 'переписки, обычный текст — нет (шаг молча ждёт реплики клиента)'),
41
+ content: z.string().nullish().describe('Текст для type=text'),
42
+ templateId: z
43
+ .string()
44
+ .nullish()
45
+ .describe('ID шаблона WABA. Берётся из besales_wazzup_templates_list — это id шаблона канала, ' +
46
+ 'а не его имя'),
47
+ templateValues: z
48
+ .array(z.unknown())
49
+ .nullish()
50
+ .describe('Значения подстановок шаблона по порядку ({{1}}, {{2}}, …). Поддерживают переменные ' +
51
+ 'контакта — они резолвятся перед отправкой'),
52
+ contentMode: z
53
+ .string()
54
+ .nullish()
55
+ .describe("'static' (по умолчанию) либо 'dynamic' — текст генерирует LLM"),
56
+ generationContext: z.string().nullish().describe('Инструкция LLM при contentMode=dynamic'),
57
+ fallbackContent: z.string().nullish().describe('Запасной текст, если генерация не удалась'),
58
+ attachments: z
59
+ .array(z.unknown())
60
+ .nullish()
61
+ .describe('Вложения: [{ storedFileId, caption? }]'),
62
+ buttons: z.array(z.unknown()).nullish(),
63
+ crmNote: z.string().nullish(),
64
+ })
65
+ .passthrough();
25
66
  const reactivationStepSchema = z
26
67
  .object({
27
68
  stepType: z.enum(['message', 'webhook']).optional(),
@@ -60,9 +101,10 @@ const reactivationStepSchema = z
60
101
  .max(3)
61
102
  .optional()
62
103
  .describe('Условия на переменные контакта; не прошли — шаг пропускается'),
104
+ skipIfContactRepliedSincePreviousSlot: z.boolean().optional().describe("Не отправлять шаг, если клиент писал ПОСЛЕ предыдущего слота цепочки. Даёт правило «ответил — ближайшее касание пропускаем, через одно продолжаем»: ответ в 13:05 гасит касание в 16:00, а к 17:00 предыдущим слотом становится 16:00, ответ старше него — и цепочка идёт дальше. Прогресс при пропуске двигается, контакт не зависает. Только для календарного шага (scheduleMode=clock + anchorMode=variable): на остальных рантайм и так проверяет свежесть входящего, и флаг там отвергается."),
63
105
  delayHours: z.number().optional(),
64
106
  delayMinutes: z.number().optional(),
65
- messageConfig: anyObjectSchema.optional(),
107
+ messageConfig: followUpMessageConfigSchema.optional(),
66
108
  getcourse: z
67
109
  .object({
68
110
  delivery: z
@@ -101,6 +143,23 @@ const reactivationStepSchema = z
101
143
  // проверку, пользователь подтверждает запись, и только сервер отвечает 400 — самый дорогой
102
144
  // момент, чтобы узнать про опечатку.
103
145
  .superRefine((step, ctx) => {
146
+ const message = step.messageConfig;
147
+ const isDynamic = message?.contentMode === 'dynamic';
148
+ const hasAttachments = !!message?.attachments?.length;
149
+ if (message?.type === 'template' && !message.templateId) {
150
+ ctx.addIssue({
151
+ code: z.ZodIssueCode.custom,
152
+ path: ['messageConfig', 'templateId'],
153
+ message: 'Для type=template обязателен templateId (id шаблона из besales_wazzup_templates_list)',
154
+ });
155
+ }
156
+ if (message?.type === 'text' && !message.content && !isDynamic && !hasAttachments) {
157
+ ctx.addIssue({
158
+ code: z.ZodIssueCode.custom,
159
+ path: ['messageConfig', 'content'],
160
+ message: 'Для type=text обязателен content — кроме шага из одних вложений и contentMode=dynamic',
161
+ });
162
+ }
104
163
  const delivery = step.getcourse?.delivery;
105
164
  if (!delivery)
106
165
  return;
@@ -167,6 +226,9 @@ export const reactivationConfigSchema = z
167
226
  'отправку. Набор цепочек обязан быть строгим разбиением: eq A, eq B и fallback ' +
168
227
  'neq A AND neq B (у контакта без переменной eq/in → false, neq → true)'),
169
228
  steps: z.array(reactivationStepSchema).min(1),
229
+ sessionTimeVariable: z.string().min(1).optional().describe("ИМЯ переменной контакта с ЧАСОМ события (ЧЧ:ММ), парное к anchorVariable шага, который задаёт ДАТУ. Нужно потому, что поле-якорь во внешней CRM хранит только дату, а разбор нормализует её в служебный ПОЛДЕНЬ: без этого поля агент в своём промпте видит «Эфир: 21.08.2026 12:00» при эфире в 19:00. Задаётся НА ЦЕПОЧКУ (час слота одинаков для всех касаний и всех дней — dayOffset сдвигает дату, не час). Хранится ИМЯ, а не сам час: цепочка отбирает контактов условием по этой же переменной, и вторая копия значения разошлась бы с отбором молча. На расписание НЕ влияет — его задают atTime шагов; поле читает только промпт агента. Значение у контакта вне ЧЧ:ММ игнорируется: печатается одна дата, выдуманный час не подставляется."),
230
+ sessionDayOffsets: z.array(z.number().int().min(0).max(30)).min(1).optional().describe("На каких dayOffset цепочки проходят САМИ эфиры ([0,1,2] — три дня подряд от якоря). Вывести из шагов нельзя: касания стоят и на днях без эфира (у вебинарной цепочки шаги занимают дни 0–5, а эфиров три — дни 4 и 5 это продажа), поэтому дни объявляются явно, как явно объявлено время в sessionTimeVariable. На расписание НЕ влияет — его задают atTime/dayOffset шагов; поле читает только промпт агента. Без объявления блок печатает ОДИН эфир по якорю: якорь несёт дату лишь ПЕРВОГО эфира, и на второй день агент видел «был вчера», не зная, что сегодня вечером эфир есть. Порядок задаёт нумерацию («эфир 2 из 3») и нормализуется по возрастанию."),
231
+ sessionDurationMinutes: z.number().int().positive().max(1440).optional().describe("Сколько минут длится эфир. Объявляется, ТОЛЬКО если длительность действительно известна: с ней блок промпта утверждает «идёт сейчас» и перестаёт это утверждать по её истечении, без неё — только «уже начался», то есть факт, который нельзя опровергнуть временем. Дефолта нет намеренно: подставленная длительность это утверждение о мире, сделанное за клиента, а такие утверждения агент пересказывает человеку. Читается только при объявленном sessionDayOffsets."),
170
232
  })
171
233
  .passthrough();
172
234
  /**
@@ -235,7 +297,13 @@ export const followUpReactionsSchema = z.object({
235
297
  z.literal(REACTION_HORIZONS[6]),
236
298
  ])
237
299
  .optional()
238
- .describe('Горизонт сравнительной метрики. По умолчанию 90'),
300
+ .describe('Горизонт сравнительной метрики (на ШАГЕ). По умолчанию 90'),
301
+ run_window_days: z
302
+ .union([z.literal(1), z.literal(3), z.literal(5), z.literal(7), z.literal(14)])
303
+ .optional()
304
+ .describe('Окно ПРОХОЖДЕНИЯ цепочки для метрики runs: ответил ли человек хоть раз за N дней от ' +
305
+ 'входа. По умолчанию 1. Не путать с horizon_minutes — там единица касание, здесь ' +
306
+ 'прохождение.'),
239
307
  segment: z
240
308
  .array(z.string())
241
309
  .optional()
@@ -297,15 +365,20 @@ export const followUpTools = [
297
365
  to: input.to,
298
366
  sequenceId: input.sequence_id,
299
367
  horizonMinutes: input.horizon_minutes,
368
+ runWindowDays: input.run_window_days,
300
369
  segment: input.segment,
301
370
  product: input.product,
302
371
  channel: input.channel,
303
372
  })}`,
304
373
  }),
305
374
  enhanceResponse: (response) => withInstructions(response, [
306
- 'Метрик ДВЕ и они НЕ взаимозаменяемы. `replied` — операционная: у каждого шага СВОЁ окно (до следующего касания), поэтому шаг с суточным интервалом почти всегда выглядит лучше часового. Сравнивать шаги по ней НЕЛЬЗЯ.',
375
+ 'Метрик ТРИ и они НЕ взаимозаменяемы (третья — runs, см. ниже). `replied` — операционная: у каждого шага СВОЁ окно (до следующего касания), поэтому шаг с суточным интервалом почти всегда выглядит лучше часового. Сравнивать шаги по ней НЕЛЬЗЯ.',
307
376
  'Сравнивать шаги можно ТОЛЬКО по `horizonReplied` (равный горизонт) и ТОЛЬКО там, где `comparableReliable: true`.',
308
- 'comparableReliable=false означает, что доля ЗАВЫШЕНА, а не просто шумна: цензурирование информативно — следующий шаг уходит лишь к промолчавшим, поэтому выжившие обогащены ответившими. Замер: 67 % вырезанных дали 20,8 % против настоящих 5,5 %. Лечение — horizon_minutes КОРОЧЕ интервала между шагами.',
377
+ 'comparableReliable=false означает, что доля ЗАВЫШЕНА, а не просто шумна: цензурирование информативно — следующий шаг уходит лишь к промолчавшим, поэтому выжившие обогащены ответившими. Замер: 67 % вырезанных дали 20,8 % против настоящих 5,5 %. Первое лечение — horizon_minutes КОРОЧЕ интервала между шагами.',
378
+ 'Но у частых цепочек подходящего горизонта НЕ СУЩЕСТВУЕТ: у вебинарной цепочки 35 % касаний имеют следующий шаг раньше 15 минут, то есть их вырезает даже минимальный горизонт 30. Не подбирай горизонт бесконечно — переходи на метрику runs.',
379
+ 'runs — ТРЕТЬЯ метрика и ДРУГАЯ единица: прохождение цепочки, а не касание. «Ответил ли хоть раз за run_window_days от входа». Вырезать там некого (все шаги внутри окна принадлежат этому же прохождению), поэтому частота пингов на неё не влияет. Отвечает «работает ли цепочка», а НЕ «какой шаг сработал» — текст шага по ней не оптимизируется.',
380
+ 'runs.immature — прохождения, у которых окно ещё не вышло. Они НЕ входят в runs.started, то есть доля считается от созревших. Растёт окно — растёт и число незрелых: это размен по ВРЕМЕНИ, а не по выборке.',
381
+ 'Сравнивать ВЕРСИИ одного шага (A/B текста) надо по replied, а не по horizonReplied: у версий одного шага пауза до следующего касания одинаковая, значит окно равное и сравнение честное.',
309
382
  'Знаменатели разные: `replied` считается от `resolvedSent` (без ещё открытых окон), `horizonReplied` — от `comparableSent`. От `sent` не считается НИ ОДНА доля.',
310
383
  'Ответ атрибутируется РОВНО ОДНОМУ касанию — ближайшему предшествующему. Окно закрывают шаг любой цепочки, волна рассылки и реплика живого оператора.',
311
384
  'Цепочки с `measurable: false` шагов не записывают вообще (LOST_CLIENTS): их отсутствие в byStep означает «не меряем», а не «не работает».',
@@ -1 +1 @@
1
- {"version":3,"file":"follow-up.js","sourceRoot":"","sources":["../../../src/tools/definitions/follow-up.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"follow-up.js","sourceRoot":"","sources":["../../../src/tools/definitions/follow-up.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Перечитывание КАЛЕНДАРНЫХ ЯКОРЕЙ (даты эфира) из запинённой сделки GetCourse.
3
+ *
4
+ * ⚠️⚠️ Единственный способ донести до контактов ПЕРЕНЕСЁННЫЙ эфир. Автоматический добор якоря —
5
+ * set-if-absent и к заполненному значению не возвращается никогда, поэтому смена даты на сделке
6
+ * сама по себе никуда не доезжает: «Первое сообщение» будет возвращать SKIPPED_ANCHOR_IN_PAST,
7
+ * сколько его ни запускай.
8
+ *
9
+ * Операция необратима ПО ДАННЫМ — она затирает дату у живых контактов и переносит расписание их
10
+ * касаний, — поэтому старт требует explicit_confirmation и предваряется предпросмотром, как у
11
+ * сестринской рассылки «Первое сообщение».
12
+ */
13
+ export declare const getcourseAnchorRefreshTools: (import("./shared.js").ToolDefinition<{
14
+ platform_id: string;
15
+ workspace_id?: string | undefined;
16
+ contacts?: {
17
+ contact_id: string;
18
+ deal_id?: string | undefined;
19
+ }[] | undefined;
20
+ current_anchor_value?: string | undefined;
21
+ }> | import("./shared.js").ToolDefinition<{
22
+ explicit_confirmation: true;
23
+ platform_id: string;
24
+ workspace_id?: string | undefined;
25
+ contacts?: {
26
+ contact_id: string;
27
+ deal_id?: string | undefined;
28
+ }[] | undefined;
29
+ current_anchor_value?: string | undefined;
30
+ }>)[];
@@ -0,0 +1,95 @@
1
+ import { z } from 'zod';
2
+ import { compactObject, defineTool, uuidSchema, withInstructions } from './shared.js';
3
+ /** Потолок аудитории — общий с «Первым сообщением» и волнами: тот же разобранный файл. */
4
+ const MAX_CONTACTS = 5000;
5
+ /** Значение якоря — это дата, а не текст; паритет с DTO бэкенда. */
6
+ const ANCHOR_VALUE_MAX_LENGTH = 64;
7
+ const numericIdSchema = (field) => z
8
+ .string()
9
+ .trim()
10
+ .regex(/^\d+$/, `${field} обязан быть числовым идентификатором GetCourse`);
11
+ const contactSchema = z
12
+ .object({
13
+ contact_id: numericIdSchema('contact_id'),
14
+ deal_id: numericIdSchema('deal_id').optional(),
15
+ })
16
+ .strict();
17
+ const toContacts = (contacts) => contacts?.map((contact) => ({ contactId: contact.contact_id, dealId: contact.deal_id }));
18
+ /** Общее тело обеих ручек: отбор у предпросмотра и у запуска обязан быть ОДИН. */
19
+ const audienceShape = {
20
+ workspace_id: uuidSchema.optional(),
21
+ platform_id: uuidSchema,
22
+ contacts: z.array(contactSchema).min(1).max(MAX_CONTACTS).optional(),
23
+ current_anchor_value: z.string().trim().min(1).max(ANCHOR_VALUE_MAX_LENGTH).optional(),
24
+ };
25
+ /** Обе аудитории сразу отвергаются схемой, а не 400-й ошибкой сервера. */
26
+ const audienceRefinement = [
27
+ (input) => input.contacts === undefined || input.current_anchor_value === undefined,
28
+ {
29
+ message: 'Не больше одной аудитории: contacts (поимённый список) ЛИБО current_anchor_value (когорта). ' +
30
+ 'Без обеих — вся платформа.',
31
+ },
32
+ ];
33
+ const toBody = (input) => compactObject({
34
+ platformId: input.platform_id,
35
+ contacts: toContacts(input.contacts),
36
+ currentAnchorValue: input.current_anchor_value,
37
+ });
38
+ /**
39
+ * Перечитывание КАЛЕНДАРНЫХ ЯКОРЕЙ (даты эфира) из запинённой сделки GetCourse.
40
+ *
41
+ * ⚠️⚠️ Единственный способ донести до контактов ПЕРЕНЕСЁННЫЙ эфир. Автоматический добор якоря —
42
+ * set-if-absent и к заполненному значению не возвращается никогда, поэтому смена даты на сделке
43
+ * сама по себе никуда не доезжает: «Первое сообщение» будет возвращать SKIPPED_ANCHOR_IN_PAST,
44
+ * сколько его ни запускай.
45
+ *
46
+ * Операция необратима ПО ДАННЫМ — она затирает дату у живых контактов и переносит расписание их
47
+ * касаний, — поэтому старт требует explicit_confirmation и предваряется предпросмотром, как у
48
+ * сестринской рассылки «Первое сообщение».
49
+ */
50
+ export const getcourseAnchorRefreshTools = [
51
+ defineTool({
52
+ name: 'besales_getcourse_anchor_refresh_preview',
53
+ requiredFields: ['platform_id'],
54
+ defaultWorkspaceId: true,
55
+ schema: z.object(audienceShape).strict().refine(...audienceRefinement),
56
+ createRequest: (input) => ({
57
+ method: 'POST',
58
+ path: `/workspaces/${input.workspace_id}/getcourse-anchor-refresh/preview`,
59
+ body: toBody(input),
60
+ }),
61
+ enhanceResponse: (response) => withInstructions(response, [
62
+ 'Ничего не пишет и в школу не ходит — считает по базе. Показывай пользователю currentValues ДО запуска: это единственный способ увидеть, ЧТО именно будет переписано.',
63
+ 'currentValues — переменная → текущее значение → сколько контактов его несут. Если там несколько разных дат, отбор шире, чем один перенесённый эфир: сузь его current_anchor_value.',
64
+ 'withoutPinnedDeal — этим прогон ОТКАЖЕТ (SKIPPED_NO_PINNED_DEAL): без запинённой сделки читалась бы произвольная сделка контакта, и верная дата была бы затёрта чужой. Пин появится на следующем входящем либо его проставит импорт сделок.',
65
+ '⚠️ notInBase — строки присланного списка, которым НЕ нашлось контакта в базе площадки. «Прислали 500, найдено 200» означает, что 300 человек прогон не тронет вовсе: сначала импорт контактов (besales_getcourse_contacts_import), потом перечит. Называй это число пользователю — молча оно читается как полный охват.',
66
+ 'refreshableVariables пуст → перечитывать нечем: у площадки нет включённых календарных цепочек либо их якорь не заведён в карте полей сделки. Запуск в этом случае не стартует.',
67
+ '⚠️ НОВЫЕ значения предпросмотр не знает и знать не может: чтобы их узнать, надо прочитать все сделки — то есть выполнить сам прогон.',
68
+ ]),
69
+ }),
70
+ defineTool({
71
+ name: 'besales_getcourse_anchor_refresh_start',
72
+ requiredFields: ['platform_id', 'explicit_confirmation'],
73
+ defaultWorkspaceId: true,
74
+ schema: z
75
+ .object({ ...audienceShape, explicit_confirmation: z.literal(true) })
76
+ .strict()
77
+ .refine(...audienceRefinement),
78
+ createRequest: (input) => ({
79
+ method: 'POST',
80
+ path: `/workspaces/${input.workspace_id}/getcourse-anchor-refresh`,
81
+ body: toBody(input),
82
+ }),
83
+ enhanceResponse: (response) => withInstructions(response, [
84
+ 'Прогон идёт ФОНОМ: ответ подтверждает старт. Итоги ищутся по correlationId — в логах и в построчном аудите (audit_logs: entityType=ChatUser, action=GETCOURSE_ANCHOR_REFRESH, changes.variables содержит прежнее и новое значение каждого якоря).',
85
+ 'Перечитываются ТОЛЬКО якоря календарных цепочек площадки и ТОЛЬКО с запинённой сделки. Переменные ветвления (посещаемость эфира, клики) не трогаются: у них авторитетнее ответ клиента в диалоге.',
86
+ '⚠️⚠️ Прогресс цепочек НЕ откатывается. Уже отправленные касания остаются отправленными; заново планируются только не ушедшие. Перенос эфира ВПЕРЁД доезжает целиком, НАЗАД — закрывает часть слотов как просроченные. Скажи это пользователю до подтверждения: откатить нельзя.',
87
+ 'Исходы в логе прогона различают ПРИЧИНЫ, и чинятся они разным: UPDATED — дата перенесена, прежняя сохранена в аудите; UPDATED_AUDIT_LOST — дата перенесена, но строка аудита не легла (прежнее значение только в логе, откатить нечем); UNCHANGED — на сделке та же дата; SKIPPED_DEAL_FIELD_EMPTY — поле сделки пусто (заполнить в школе); SKIPPED_NO_PINNED_DEAL / SKIPPED_STALE_PIN — сделка не закреплена либо исчезла (перезакрепить импортом); SKIPPED_NOT_IN_BASE — контакта нет в базе (сначала импорт); FAILED — школа не ответила, повтор осмыслен.',
88
+ '⚠️ FAILED и UNCHANGED — противоположные новости: первое означает «не прочитали ни одной сделки, повтори», второе — «переносить нечего». Не сваливай их в одно «ничего не изменилось».',
89
+ 'alreadyRunning=true означает, что по этой площадке прогон уже идёт — дождись его, второй не запускается. ⚠️ Отмены у прогона НЕТ, а статус узнать неоткуда, кроме логов: сначала preview, потом запуск.',
90
+ 'started=false при пустом refreshableVariables — перечитывать нечем, а не сбой: у площадки нет включённых календарных цепочек либо якорь не замаплен на поле сделки.',
91
+ 'После прогона контакты с обновлённой датой снова доступны обычному запуску «Первого сообщения» — до него они отсеивались исходом SKIPPED_ANCHOR_IN_PAST.',
92
+ ]),
93
+ }),
94
+ ];
95
+ //# sourceMappingURL=getcourse-anchor-refresh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getcourse-anchor-refresh.js","sourceRoot":"","sources":["../../../src/tools/definitions/getcourse-anchor-refresh.ts"],"names":[],"mappings":"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"}
@@ -11,9 +11,11 @@ export declare const getcourseColdWelcomeTools: (import("./shared.js").ToolDefin
11
11
  contacts?: {
12
12
  contact_id: string;
13
13
  deal_id?: string | undefined;
14
+ segment_entered_at?: string | undefined;
14
15
  }[] | undefined;
15
16
  mailing_variant_id?: string | undefined;
16
17
  channels?: ("Site" | "Email" | "SMS" | "Telegram" | "Facebook" | "VK" | "Chatium" | "WhatsApp" | "Viber" | "MAX")[] | undefined;
18
+ channels_mode?: "RESTRICT" | "ADD" | undefined;
17
19
  }> | import("./shared.js").ToolDefinition<{
18
20
  explicit_confirmation: true;
19
21
  workspace_id?: string | undefined;
@@ -22,6 +24,7 @@ export declare const getcourseColdWelcomeTools: (import("./shared.js").ToolDefin
22
24
  contacts?: {
23
25
  contact_id: string;
24
26
  deal_id?: string | undefined;
27
+ segment_entered_at?: string | undefined;
25
28
  }[] | undefined;
26
29
  deal_fields?: Record<string, string | number | boolean | null> | undefined;
27
30
  message?: string | undefined;
@@ -29,16 +32,20 @@ export declare const getcourseColdWelcomeTools: (import("./shared.js").ToolDefin
29
32
  segment?: string | undefined;
30
33
  product_code?: string | undefined;
31
34
  funnel?: string | undefined;
35
+ cohort_label?: string | undefined;
32
36
  channels?: ("Site" | "Email" | "SMS" | "Telegram" | "Facebook" | "VK" | "Chatium" | "WhatsApp" | "Viber" | "MAX")[] | undefined;
37
+ channels_mode?: "RESTRICT" | "ADD" | undefined;
33
38
  }> | import("./shared.js").ToolDefinition<{
34
39
  segments: {
35
40
  contacts: {
36
41
  contact_id: string;
37
42
  deal_id?: string | undefined;
43
+ segment_entered_at?: string | undefined;
38
44
  }[];
39
45
  segment?: string | undefined;
40
46
  product_code?: string | undefined;
41
47
  funnel?: string | undefined;
48
+ cohort_label?: string | undefined;
42
49
  deal_fields?: Record<string, string | number | boolean | null> | undefined;
43
50
  }[];
44
51
  explicit_confirmation: true;
@@ -47,6 +54,7 @@ export declare const getcourseColdWelcomeTools: (import("./shared.js").ToolDefin
47
54
  message?: string | undefined;
48
55
  mailing_variant_id?: string | undefined;
49
56
  channels?: ("Site" | "Email" | "SMS" | "Telegram" | "Facebook" | "VK" | "Chatium" | "WhatsApp" | "Viber" | "MAX")[] | undefined;
57
+ channels_mode?: "RESTRICT" | "ADD" | undefined;
50
58
  }> | import("./shared.js").ToolDefinition<{
51
59
  batch_id: string;
52
60
  workspace_id?: string | undefined;
@@ -1,14 +1,15 @@
1
1
  import { z } from 'zod';
2
- import { compactObject, defineTool, getcourseTransportEnum, uuidSchema, withInstructions, } from './shared.js';
2
+ import { compactObject, defineTool, coldWelcomeChannelsModeEnum, getcourseTransportEnum, uuidSchema, withInstructions, } from './shared.js';
3
3
  const MESSAGE_MAX_LENGTH = 4000;
4
4
  const LABEL_MAX_LENGTH = 200;
5
5
  const MAX_CONTACTS = 5000;
6
6
  /** Потолок сегментов в пакете — паритет с бэкендом и с батчем волны. */
7
7
  const MAX_BATCH_SEGMENTS = 10;
8
8
  /**
9
- * Строка предсписка: getcourseUserId + опциональный пин сделки для штампа полей.
9
+ * Строка предсписка: getcourseUserId, опциональный пин сделки для штампа полей и — опционально —
10
+ * момент входа человека в когорту.
10
11
  *
11
- * Оба поля — ЧИСЛА в строковом виде, и проверяется именно форма: паритет с сестринской
12
+ * Оба ИДЕНТИФИКАТОРА — ЧИСЛА в строковом виде, и проверяется именно форма: паритет с сестринской
12
13
  * `besales_getcourse_wave_run_start`. Непустоты мало — «AB-123» доезжал до штампа уже ПОСЛЕ
13
14
  * доставки сообщения, а «user@mail.ru» молча выпадал в отбракованные строки.
14
15
  */
@@ -20,9 +21,29 @@ const contactSchema = z
20
21
  .object({
21
22
  contact_id: numericIdSchema('contact_id'),
22
23
  deal_id: numericIdSchema('deal_id').optional(),
24
+ /**
25
+ * Момент входа контакта в когорту — на нём считается свежесть в «Аналитике рассылок».
26
+ *
27
+ * ⚠️⚠️ Заполняй его, когда предсписок собран ПРАВИЛОМ (события LeakEngine, выгрузка по
28
+ * сегменту): у каждой строки там свой момент, и он ФАКТ О ЧЕЛОВЕКЕ. Пока поле не принималось,
29
+ * все холодные строки уезжали без возраста и выпадали из любого непустого фильтра свежести.
30
+ *
31
+ * ⚠️⚠️ Дату запуска сюда подставлять НЕЛЬЗЯ. Разброс возраста в таких когортах — от часа до
32
+ * 294 дней при медиане 6,3 дня: общий момент схлопнул бы месяцы когорт в одну корзину, а
33
+ * разрез при этом выглядел бы наполненным. Не знаешь момента строки — не передавай ключ.
34
+ *
35
+ * `{ offset: true }` — зона обязательна (…Z либо ±ЧЧ:ММ), паритет с сестринской
36
+ * `besales_getcourse_wave_run_start`: без зоны момент читался бы по зоне процесса, и возраст
37
+ * разошёлся бы на часы между средами.
38
+ */
39
+ segment_entered_at: z.string().datetime({ offset: true }).optional(),
23
40
  })
24
41
  .strict();
25
- const toContacts = (contacts) => contacts?.map((contact) => ({ contactId: contact.contact_id, dealId: contact.deal_id }));
42
+ const toContacts = (contacts) => contacts?.map((contact) => ({
43
+ contactId: contact.contact_id,
44
+ dealId: contact.deal_id,
45
+ segmentEnteredAt: contact.segment_entered_at,
46
+ }));
26
47
  /**
27
48
  * Пустая строка = поля НЕТ.
28
49
  *
@@ -71,6 +92,7 @@ export const getcourseColdWelcomeTools = [
71
92
  contacts: z.array(contactSchema).min(1).max(MAX_CONTACTS).optional(),
72
93
  mailing_variant_id: uuidSchema.optional(),
73
94
  channels: z.array(getcourseTransportEnum).optional(),
95
+ channels_mode: coldWelcomeChannelsModeEnum.optional(),
74
96
  })
75
97
  .strict()
76
98
  .refine((input) => (input.created_after !== undefined) !== (input.contacts !== undefined), {
@@ -85,11 +107,14 @@ export const getcourseColdWelcomeTools = [
85
107
  contacts: toContacts(input.contacts),
86
108
  mailingVariantId: input.mailing_variant_id,
87
109
  channels: input.channels,
110
+ channelsMode: input.channels_mode,
88
111
  }),
89
112
  }),
90
113
  enhanceResponse: (response) => withInstructions(response, [
91
114
  'totalContacts — сколько ЕЩЁ может получить первое сообщение; alreadyWelcomed — сколько уже получали его раньше (оно одноразово НА КОНТАКТ, не на рассылку).',
92
115
  '⚠️⚠️ Если задано ограничение каналов, ФАКТИЧЕСКИЙ охват = totalContacts − narrowedOutByChannels (доказанно отсечённые каналами). Называй пользователю именно эту разность: сырой totalContacts завысит обещание, а отправку не отозвать. channelsUnknown вычитать НЕ надо — у этих контактов привязки просто ещё не проверены, при отправке их спросят у школы живьём.',
116
+ `⚠️ channels_mode УСТАРЕЛ и на поведение НЕ влияет (с 23.08.2026). Список channels значит «слать сюда», а судьбу КАЖДОГО канала решает сервер: мессенджер (Telegram/VK/MAX) уходит, только если школа подтвердила его наличие у контакта; SMS — только при известном телефоне; Email, WhatsApp, Viber, Сайт, Chatium уходят вслепую всем выбранным. Просят «написать ещё и на почту» — добавь Email в channels, а мессенджеры перечисли явно, иначе уедет только письмо. Поле принимается ради обратной совместимости и игнорируется.`,
117
+ 'channelsNotAllowedByPlatform — выбранные каналы, не включённые на площадке: они не уедут ни в каком режиме. Назови их пользователю, это чинится в настройках площадки, а не повтором запуска.',
93
118
  'Для аудитории по файлу дополнительно приходят alreadyInDialog и notInBase: контакты предсписка обязаны быть импортированы (Шаг 1) до запуска.',
94
119
  'Всегда показывай пользователю фактический охват и alreadyWelcomed ДО старта: отправку нельзя отозвать.',
95
120
  'Часть контактов отсеется уже в прогоне (мьют бота, нет даты эфира, недостижимость, переменные без значения) — разбивка по причинам будет в outcomes задачи.',
@@ -113,7 +138,9 @@ export const getcourseColdWelcomeTools = [
113
138
  segment: z.string().max(LABEL_MAX_LENGTH).optional(),
114
139
  product_code: z.string().max(LABEL_MAX_LENGTH).optional(),
115
140
  funnel: z.string().max(LABEL_MAX_LENGTH).optional(),
141
+ cohort_label: z.string().max(LABEL_MAX_LENGTH).optional(),
116
142
  channels: z.array(getcourseTransportEnum).optional(),
143
+ channels_mode: coldWelcomeChannelsModeEnum.optional(),
117
144
  explicit_confirmation: z.literal(true),
118
145
  })
119
146
  .strict()
@@ -136,18 +163,23 @@ export const getcourseColdWelcomeTools = [
136
163
  segment: input.segment,
137
164
  productCode: input.product_code,
138
165
  funnel: input.funnel,
166
+ cohortLabel: input.cohort_label,
139
167
  channels: input.channels,
168
+ channelsMode: input.channels_mode,
140
169
  }),
141
170
  }),
142
171
  enhanceResponse: (response) => withInstructions(response, [
143
172
  'Рассылка запущена фоном: прогресс и разбивка исходов — в задаче импорта (jobId).',
173
+ 'cohort_label — имя ЭТОГО запуска в «Аналитике рассылок». Задавай его на каждом запуске: без него разрез «по запускам» различает их только датой, а два запуска одной рассылки за день сольются в одну подпись.',
144
174
  'Текст заморожен снимком: правка рассылки НЕ меняет идущий запуск.',
145
175
  'Если у рассылки заданы message_text_by_segment и segment совпал — уедет ТЕКСТ СЕГМЕНТА. Назови пользователю, какой текст выбран, ДО подтверждения: отправку не отозвать.',
146
176
  '⚠️ Метка segment аудиторию НЕ отбирает. Поэтому посегментный текст разрешён только с contacts (файл): с created_after он ушёл бы ВСЕЙ когорте после даты и выжег бы отметку приветствия — второй сегмент получил бы нулевой охват. Такой запуск отвергается 400.',
147
177
  'Повторный запуск по той же границе безопасен: уже получившие первое сообщение не получат второе.',
148
178
  'platform_id и deal_fields можно опустить, если они заданы в рассылке как «по умолчанию» — сервер подставит их сам.',
149
179
  'Пустой объект deal_fields — НЕ то же, что отсутствие ключа: он означает «на этом запуске без полей» и гасит дефолты рассылки.',
180
+ `⚠️ channels_mode УСТАРЕЛ и на поведение НЕ влияет (с 23.08.2026). Список channels значит «слать сюда», а судьбу КАЖДОГО канала решает сервер: мессенджер (Telegram/VK/MAX) уходит, только если школа подтвердила его наличие у контакта; SMS — только при известном телефоне; Email, WhatsApp, Viber, Сайт, Chatium уходят вслепую всем выбранным. Просят «написать ещё и на почту» — добавь Email в channels, а мессенджеры перечисли явно, иначе уедет только письмо. Поле принимается ради обратной совместимости и игнорируется.`,
150
181
  'deal_fields штампуются ПОСЛЕ успешной отправки (пин из строки файла, иначе закреплённая сделка контакта); их отказ не отменяет отправку и виден счётчиком dealStamp.',
182
+ 'contacts[].segment_entered_at — точный ISO-момент (с зоной) входа человека в когорту; на нём считается свежесть в «Аналитике рассылок». Передавай его, когда предсписок собран ПРАВИЛОМ и у каждой строки есть свой момент события. ⚠️ Дату запуска подставлять НЕЛЬЗЯ: разброс возраста бывает от часа до 294 дней, и общий момент схлопнул бы месяцы когорт в одну.',
151
183
  'Контакт, у которого переменная текста осталась без значения, ПРОПУСКАЕТСЯ (outcome SKIPPED_UNRESOLVED_VARS) — сырые {скобки} живому человеку не уедут. Отметка приветствия при этом не сгорает: проставьте значение или перезапись у агента и запустите повторно, контакт доберётся.',
152
184
  ]),
153
185
  }),
@@ -162,12 +194,14 @@ export const getcourseColdWelcomeTools = [
162
194
  message: z.string().trim().min(1).max(MESSAGE_MAX_LENGTH).optional(),
163
195
  mailing_variant_id: uuidSchema.optional(),
164
196
  channels: z.array(getcourseTransportEnum).optional(),
197
+ channels_mode: coldWelcomeChannelsModeEnum.optional(),
165
198
  segments: z
166
199
  .array(z
167
200
  .object({
168
201
  segment: z.string().max(LABEL_MAX_LENGTH).optional(),
169
202
  product_code: z.string().max(LABEL_MAX_LENGTH).optional(),
170
203
  funnel: z.string().max(LABEL_MAX_LENGTH).optional(),
204
+ cohort_label: z.string().max(LABEL_MAX_LENGTH).optional(),
171
205
  deal_fields: z
172
206
  .record(z
173
207
  .string()
@@ -192,10 +226,12 @@ export const getcourseColdWelcomeTools = [
192
226
  message: input.message,
193
227
  mailingVariantId: input.mailing_variant_id,
194
228
  channels: input.channels,
229
+ channelsMode: input.channels_mode,
195
230
  segments: input.segments.map((segment) => compactObject({
196
231
  segment: segment.segment,
197
232
  productCode: segment.product_code,
198
233
  funnel: segment.funnel,
234
+ cohortLabel: segment.cohort_label,
199
235
  dealFields: segment.deal_fields,
200
236
  contacts: toContacts(segment.contacts),
201
237
  })),
@@ -203,7 +239,9 @@ export const getcourseColdWelcomeTools = [
203
239
  }),
204
240
  enhanceResponse: (response) => withInstructions(response, [
205
241
  'Пакет: несколько сегментов одним вызовом, у каждого свой список контактов, свои метки и свои поля сделки.',
242
+ 'cohort_label задаётся ПОСЕГМЕНТНО: каждый сегмент — отдельный запуск со своей задачей и когортой, и общая на пакет подпись схлопнула бы их в отчёте в одну корзину.',
206
243
  'Сегменты идут ПО ОЧЕРЕДИ — задачи импорта не могут выполняться параллельно. Прогресс смотри через besales_getcourse_cold_welcome_batch_status по batchId.',
244
+ 'contacts[].segment_entered_at — точный ISO-момент (с зоной) входа человека в когорту; на нём считается свежесть в «Аналитике рассылок». Передавай его посегментно, когда предсписок собран ПРАВИЛОМ и у каждой строки есть свой момент события. ⚠️ Дату запуска подставлять НЕЛЬЗЯ: разброс возраста бывает от часа до 294 дней, и общий момент схлопнул бы месяцы когорт в одну.',
207
245
  'Весь пакет проверяется ДО первой отправки: сегмент без текста ИЛИ текст с переменными, которые не подставляются его контактам, отменяет пакет целиком, пока никому ничего не ушло. В ошибке названы сами переменные, контакт проверки и агент — чинить надо значение переменной или перезапись у этого агента.',
208
246
  '⚠️ Проверка переменных на плане ВЫБОРОЧНАЯ (несколько контактов сегмента) и не гарантирует, что подставится всем: агента выбирает роутер по контакту. Полноту держит гейт на самой отправке — контакт с сырыми скобками пропускается с outcome SKIPPED_UNRESOLVED_VARS.',
209
247
  '⚠️ Переменные, которые отправка дочитывает из сделки (календарные поля замапленной площадки), план НЕ проверяет — судить по до-синковому снимку значило бы блокировать каждый пакет календарной площадки. Все остальные проверяются. Если пакет упал на календарной переменной — это баг, а не настройка.',