@besales/mcp 0.37.1 → 0.39.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,241 @@
1
+ import { z } from 'zod';
2
+ import { compactObject, defineTool, uuidSchema, withInstructions } from './shared.js';
3
+ const MESSAGE_MAX_LENGTH = 4000;
4
+ const LABEL_MAX_LENGTH = 200;
5
+ const MAX_CONTACTS = 5000;
6
+ /** Потолок сегментов в пакете — паритет с бэкендом и с батчем волны. */
7
+ const MAX_BATCH_SEGMENTS = 10;
8
+ /**
9
+ * Строка предсписка: getcourseUserId + опциональный пин сделки для штампа полей.
10
+ *
11
+ * Оба поля — ЧИСЛА в строковом виде, и проверяется именно форма: паритет с сестринской
12
+ * `besales_getcourse_wave_run_start`. Непустоты мало — «AB-123» доезжал до штампа уже ПОСЛЕ
13
+ * доставки сообщения, а «user@mail.ru» молча выпадал в отбракованные строки.
14
+ */
15
+ const numericIdSchema = (field) => z
16
+ .string()
17
+ .trim()
18
+ .regex(/^\d+$/, `${field} обязан быть числовым идентификатором GetCourse`);
19
+ const contactSchema = z
20
+ .object({
21
+ contact_id: numericIdSchema('contact_id'),
22
+ deal_id: numericIdSchema('deal_id').optional(),
23
+ })
24
+ .strict();
25
+ const toContacts = (contacts) => contacts?.map((contact) => ({ contactId: contact.contact_id, dealId: contact.deal_id }));
26
+ /**
27
+ * Рассылка «Первое сообщение» (холодное приветствие GetCourse): МЫ пишем первыми контактам без
28
+ * открытого диалога. Отправка НЕОБРАТИМА и не идемпотентна на стороне GetCourse — повтор создал
29
+ * бы второе сообщение живому человеку, поэтому старт требует explicit_confirmation и всегда
30
+ * предваряется preview.
31
+ */
32
+ export const getcourseColdWelcomeTools = [
33
+ defineTool({
34
+ name: 'besales_getcourse_cold_welcome_preview',
35
+ requiredFields: ['platform_id'],
36
+ defaultWorkspaceId: true,
37
+ schema: z
38
+ .object({
39
+ workspace_id: uuidSchema.optional(),
40
+ platform_id: uuidSchema,
41
+ created_after: z.string().datetime({ offset: true }).optional(),
42
+ contacts: z.array(contactSchema).min(1).max(MAX_CONTACTS).optional(),
43
+ })
44
+ .strict()
45
+ .refine((input) => (input.created_after !== undefined) !== (input.contacts !== undefined), {
46
+ message: 'Ровно одна аудитория: created_after (по дате) ЛИБО contacts (по файлу)',
47
+ }),
48
+ createRequest: (input) => ({
49
+ method: 'POST',
50
+ path: `/workspaces/${input.workspace_id}/getcourse-cold-welcome/preview`,
51
+ body: compactObject({
52
+ platformId: input.platform_id,
53
+ createdAfter: input.created_after,
54
+ contacts: toContacts(input.contacts),
55
+ }),
56
+ }),
57
+ enhanceResponse: (response) => withInstructions(response, [
58
+ 'totalContacts — сколько ЕЩЁ может получить первое сообщение; alreadyWelcomed — сколько уже получали его раньше (оно одноразово НА КОНТАКТ, не на рассылку).',
59
+ 'Для аудитории по файлу дополнительно приходят alreadyInDialog и notInBase: контакты предсписка обязаны быть импортированы (Шаг 1) до запуска.',
60
+ 'Всегда показывай пользователю totalContacts и alreadyWelcomed ДО старта: отправку нельзя отозвать.',
61
+ 'Часть контактов отсеется уже в прогоне (мьют бота, нет даты эфира, недостижимость) — разбивка по причинам будет в outcomes задачи.',
62
+ ]),
63
+ }),
64
+ defineTool({
65
+ name: 'besales_getcourse_cold_welcome_start',
66
+ requiredFields: ['explicit_confirmation'],
67
+ defaultWorkspaceId: true,
68
+ schema: z
69
+ .object({
70
+ workspace_id: uuidSchema.optional(),
71
+ platform_id: uuidSchema.optional(),
72
+ created_after: z.string().datetime({ offset: true }).optional(),
73
+ contacts: z.array(contactSchema).min(1).max(MAX_CONTACTS).optional(),
74
+ deal_fields: z
75
+ .record(z.string().regex(/^\d+$/, 'Ключ deal_fields обязан быть числовым fieldId GetCourse'), z.union([z.string(), z.number(), z.boolean(), z.null()]))
76
+ .optional(),
77
+ message: z.string().trim().min(1).max(MESSAGE_MAX_LENGTH).optional(),
78
+ mailing_variant_id: uuidSchema.optional(),
79
+ segment: z.string().max(LABEL_MAX_LENGTH).optional(),
80
+ product_code: z.string().max(LABEL_MAX_LENGTH).optional(),
81
+ funnel: z.string().max(LABEL_MAX_LENGTH).optional(),
82
+ explicit_confirmation: z.literal(true),
83
+ })
84
+ .strict()
85
+ .refine((input) => (input.message !== undefined) !== (input.mailing_variant_id !== undefined), {
86
+ message: 'Ровно один источник текста: message (разовый) ЛИБО mailing_variant_id (сохранённая рассылка)',
87
+ })
88
+ .refine((input) => (input.created_after !== undefined) !== (input.contacts !== undefined), {
89
+ message: 'Ровно одна аудитория: created_after (по дате) ЛИБО contacts (по файлу)',
90
+ }),
91
+ createRequest: (input) => ({
92
+ method: 'POST',
93
+ path: `/workspaces/${input.workspace_id}/getcourse-cold-welcome`,
94
+ body: compactObject({
95
+ platformId: input.platform_id,
96
+ createdAfter: input.created_after,
97
+ contacts: toContacts(input.contacts),
98
+ dealFields: input.deal_fields,
99
+ message: input.message,
100
+ mailingVariantId: input.mailing_variant_id,
101
+ segment: input.segment,
102
+ productCode: input.product_code,
103
+ funnel: input.funnel,
104
+ }),
105
+ }),
106
+ enhanceResponse: (response) => withInstructions(response, [
107
+ 'Рассылка запущена фоном: прогресс и разбивка исходов — в задаче импорта (jobId).',
108
+ 'Текст заморожен снимком: правка рассылки НЕ меняет идущий запуск.',
109
+ 'Если у рассылки заданы message_text_by_segment и segment совпал — уедет ТЕКСТ СЕГМЕНТА. Назови пользователю, какой текст выбран, ДО подтверждения: отправку не отозвать.',
110
+ '⚠️ Метка segment аудиторию НЕ отбирает. Поэтому посегментный текст разрешён только с contacts (файл): с created_after он ушёл бы ВСЕЙ когорте после даты и выжег бы отметку приветствия — второй сегмент получил бы нулевой охват. Такой запуск отвергается 400.',
111
+ 'Повторный запуск по той же границе безопасен: уже получившие первое сообщение не получат второе.',
112
+ 'platform_id и deal_fields можно опустить, если они заданы в рассылке как «по умолчанию» — сервер подставит их сам.',
113
+ 'Пустой объект deal_fields — НЕ то же, что отсутствие ключа: он означает «на этом запуске без полей» и гасит дефолты рассылки.',
114
+ 'deal_fields штампуются ПОСЛЕ успешной отправки (пин из строки файла, иначе закреплённая сделка контакта); их отказ не отменяет отправку и виден счётчиком dealStamp.',
115
+ ]),
116
+ }),
117
+ defineTool({
118
+ name: 'besales_getcourse_cold_welcome_batch_start',
119
+ requiredFields: ['segments', 'explicit_confirmation'],
120
+ defaultWorkspaceId: true,
121
+ schema: z
122
+ .object({
123
+ workspace_id: uuidSchema.optional(),
124
+ platform_id: uuidSchema.optional(),
125
+ message: z.string().trim().min(1).max(MESSAGE_MAX_LENGTH).optional(),
126
+ mailing_variant_id: uuidSchema.optional(),
127
+ segments: z
128
+ .array(z
129
+ .object({
130
+ segment: z.string().max(LABEL_MAX_LENGTH).optional(),
131
+ product_code: z.string().max(LABEL_MAX_LENGTH).optional(),
132
+ funnel: z.string().max(LABEL_MAX_LENGTH).optional(),
133
+ deal_fields: z
134
+ .record(z
135
+ .string()
136
+ .regex(/^\d+$/, 'Ключ deal_fields обязан быть числовым fieldId GetCourse'), z.union([z.string(), z.number(), z.boolean(), z.null()]))
137
+ .optional(),
138
+ contacts: z.array(contactSchema).min(1).max(MAX_CONTACTS),
139
+ })
140
+ .strict())
141
+ .min(1)
142
+ .max(MAX_BATCH_SEGMENTS),
143
+ explicit_confirmation: z.literal(true),
144
+ })
145
+ .strict()
146
+ .refine((input) => (input.message !== undefined) !== (input.mailing_variant_id !== undefined), {
147
+ message: 'Ровно один источник текста: message (разовый) ЛИБО mailing_variant_id (сохранённая рассылка)',
148
+ }),
149
+ createRequest: (input) => ({
150
+ method: 'POST',
151
+ path: `/workspaces/${input.workspace_id}/getcourse-cold-welcome/batch`,
152
+ body: compactObject({
153
+ platformId: input.platform_id,
154
+ message: input.message,
155
+ mailingVariantId: input.mailing_variant_id,
156
+ segments: input.segments.map((segment) => compactObject({
157
+ segment: segment.segment,
158
+ productCode: segment.product_code,
159
+ funnel: segment.funnel,
160
+ dealFields: segment.deal_fields,
161
+ contacts: toContacts(segment.contacts),
162
+ })),
163
+ }),
164
+ }),
165
+ enhanceResponse: (response) => withInstructions(response, [
166
+ 'Пакет: несколько сегментов одним вызовом, у каждого свой список контактов, свои метки и свои поля сделки.',
167
+ 'Сегменты идут ПО ОЧЕРЕДИ — задачи импорта не могут выполняться параллельно. Прогресс смотри через besales_getcourse_cold_welcome_batch_status по batchId.',
168
+ 'Весь пакет проверяется ДО первой отправки: сегмент без текста отменяет пакет целиком, пока никому ничего не ушло.',
169
+ 'Отказ одного сегмента НЕ останавливает остальные — смотри разбивку в статусе.',
170
+ ]),
171
+ }),
172
+ defineTool({
173
+ name: 'besales_getcourse_cold_welcome_batch_status',
174
+ requiredFields: ['batch_id'],
175
+ defaultWorkspaceId: true,
176
+ schema: z
177
+ .object({ workspace_id: uuidSchema.optional(), batch_id: uuidSchema })
178
+ .strict(),
179
+ createRequest: (input) => ({
180
+ method: 'GET',
181
+ path: `/workspaces/${input.workspace_id}/getcourse-cold-welcome/batch/${input.batch_id}`,
182
+ }),
183
+ enhanceResponse: (response) => withInstructions(response, [
184
+ 'state — агрегат по пакету, segments[] — разбивка: у каждого свой статус, свой importJobId и своя причина отказа.',
185
+ 'stalled=true означает, что драйвер оборвался (рестарт инстанса). НЕ запускай пакет заново — зови besales_getcourse_cold_welcome_batch_resume по тому же batch_id: источник текста хранится в плане, пересобирать списки не нужно, а уже отправленным контактам повтор не уйдёт (приветствие одноразово НА КОНТАКТ).',
186
+ ]),
187
+ }),
188
+ defineTool({
189
+ name: 'besales_getcourse_cold_welcome_batch_resume',
190
+ requiredFields: ['batch_id'],
191
+ defaultWorkspaceId: true,
192
+ schema: z
193
+ .object({ workspace_id: uuidSchema.optional(), batch_id: uuidSchema })
194
+ .strict(),
195
+ createRequest: (input) => ({
196
+ method: 'POST',
197
+ path: `/workspaces/${input.workspace_id}/getcourse-cold-welcome/batch/${input.batch_id}/resume`,
198
+ }),
199
+ enhanceResponse: (response) => withInstructions(response, [
200
+ 'Поднимает драйвер для НЕ НАЧАТЫХ сегментов пакета. Подтверждения не требует: повтора отправки не будет — приветствие одноразово НА КОНТАКТ, а уже начатые сегменты не перезапускаются.',
201
+ ]),
202
+ }),
203
+ defineTool({
204
+ name: 'besales_getcourse_cold_welcome_batch_cancel',
205
+ requiredFields: ['batch_id', 'explicit_confirmation'],
206
+ defaultWorkspaceId: true,
207
+ schema: z
208
+ .object({
209
+ workspace_id: uuidSchema.optional(),
210
+ batch_id: uuidSchema,
211
+ explicit_confirmation: z.literal(true),
212
+ })
213
+ .strict(),
214
+ createRequest: (input) => ({
215
+ method: 'POST',
216
+ path: `/workspaces/${input.workspace_id}/getcourse-cold-welcome/batch/${input.batch_id}/cancel`,
217
+ }),
218
+ enhanceResponse: (response) => withInstructions(response, [
219
+ 'Отменяет ОСТАТОК пакета: не начатые сегменты и идущую задачу. Уже отправленное не отзывается.',
220
+ ]),
221
+ }),
222
+ defineTool({
223
+ name: 'besales_getcourse_cold_welcome_cancel',
224
+ requiredFields: ['job_id'],
225
+ defaultWorkspaceId: true,
226
+ schema: z
227
+ .object({
228
+ workspace_id: uuidSchema.optional(),
229
+ job_id: uuidSchema,
230
+ })
231
+ .strict(),
232
+ createRequest: (input) => ({
233
+ method: 'POST',
234
+ path: `/workspaces/${input.workspace_id}/getcourse-cold-welcome/${input.job_id}/cancel`,
235
+ }),
236
+ enhanceResponse: (response) => withInstructions(response, [
237
+ 'Останавливает ОСТАТОК когорты в пределах ~10 секунд; уже отправленное не отзывается.',
238
+ ]),
239
+ }),
240
+ ];
241
+ //# sourceMappingURL=getcourse-cold-welcome.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getcourse-cold-welcome.js","sourceRoot":"","sources":["../../../src/tools/definitions/getcourse-cold-welcome.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEtF,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAChC,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAC7B,MAAM,YAAY,GAAG,IAAI,CAAC;AAC1B,wEAAwE;AACxE,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAE9B;;;;;;GAMG;AACH,MAAM,eAAe,GAAG,CAAC,KAAa,EAAE,EAAE,CACxC,CAAC;KACE,MAAM,EAAE;KACR,IAAI,EAAE;KACN,KAAK,CAAC,OAAO,EAAE,GAAG,KAAK,iDAAiD,CAAC,CAAC;AAE/E,MAAM,aAAa,GAAG,CAAC;KACpB,MAAM,CAAC;IACN,UAAU,EAAE,eAAe,CAAC,YAAY,CAAC;IACzC,OAAO,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC,QAAQ,EAAE;CAC/C,CAAC;KACD,MAAM,EAAE,CAAC;AAEZ,MAAM,UAAU,GAAG,CAAC,QAA0D,EAAE,EAAE,CAChF,QAAQ,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;AAE3F;;;;;GAKG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,UAAU,CAAC;QACT,IAAI,EAAE,wCAAwC;QAC9C,cAAc,EAAE,CAAC,aAAa,CAAC;QAC/B,kBAAkB,EAAE,IAAI;QACxB,MAAM,EAAE,CAAC;aACN,MAAM,CAAC;YACN,YAAY,EAAE,UAAU,CAAC,QAAQ,EAAE;YACnC,WAAW,EAAE,UAAU;YACvB,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,QAAQ,EAAE;YAC/D,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,QAAQ,EAAE;SACrE,CAAC;aACD,MAAM,EAAE;aACR,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,aAAa,KAAK,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,EAAE;YACzF,OAAO,EAAE,wEAAwE;SAClF,CAAC;QACJ,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACzB,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,eAAe,KAAK,CAAC,YAAY,iCAAiC;YACxE,IAAI,EAAE,aAAa,CAAC;gBAClB,UAAU,EAAE,KAAK,CAAC,WAAW;gBAC7B,YAAY,EAAE,KAAK,CAAC,aAAa;gBACjC,QAAQ,EAAE,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC;aACrC,CAAC;SACH,CAAC;QACF,eAAe,EAAE,CAAC,QAAQ,EAAE,EAAE,CAC5B,gBAAgB,CAAC,QAAQ,EAAE;YACzB,6JAA6J;YAC7J,+IAA+I;YAC/I,oGAAoG;YACpG,oIAAoI;SACrI,CAAC;KACL,CAAC;IACF,UAAU,CAAC;QACT,IAAI,EAAE,sCAAsC;QAC5C,cAAc,EAAE,CAAC,uBAAuB,CAAC;QACzC,kBAAkB,EAAE,IAAI;QACxB,MAAM,EAAE,CAAC;aACN,MAAM,CAAC;YACN,YAAY,EAAE,UAAU,CAAC,QAAQ,EAAE;YACnC,WAAW,EAAE,UAAU,CAAC,QAAQ,EAAE;YAClC,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,QAAQ,EAAE;YAC/D,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,QAAQ,EAAE;YACpE,WAAW,EAAE,CAAC;iBACX,MAAM,CACL,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,yDAAyD,CAAC,EACpF,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CACzD;iBACA,QAAQ,EAAE;YACb,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,QAAQ,EAAE;YACpE,kBAAkB,EAAE,UAAU,CAAC,QAAQ,EAAE;YACzC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,QAAQ,EAAE;YACpD,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,QAAQ,EAAE;YACzD,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,QAAQ,EAAE;YACnD,qBAAqB,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;SACvC,CAAC;aACD,MAAM,EAAE;aACR,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,kBAAkB,KAAK,SAAS,CAAC,EAAE;YAC7F,OAAO,EACL,8FAA8F;SACjG,CAAC;aACD,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,aAAa,KAAK,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,EAAE;YACzF,OAAO,EAAE,wEAAwE;SAClF,CAAC;QACJ,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACzB,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,eAAe,KAAK,CAAC,YAAY,yBAAyB;YAChE,IAAI,EAAE,aAAa,CAAC;gBAClB,UAAU,EAAE,KAAK,CAAC,WAAW;gBAC7B,YAAY,EAAE,KAAK,CAAC,aAAa;gBACjC,QAAQ,EAAE,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC;gBACpC,UAAU,EAAE,KAAK,CAAC,WAAW;gBAC7B,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,gBAAgB,EAAE,KAAK,CAAC,kBAAkB;gBAC1C,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,WAAW,EAAE,KAAK,CAAC,YAAY;gBAC/B,MAAM,EAAE,KAAK,CAAC,MAAM;aACrB,CAAC;SACH,CAAC;QACF,eAAe,EAAE,CAAC,QAAQ,EAAE,EAAE,CAC5B,gBAAgB,CAAC,QAAQ,EAAE;YACzB,kFAAkF;YAClF,mEAAmE;YACnE,0KAA0K;YAC1K,kQAAkQ;YAClQ,kGAAkG;YAClG,oHAAoH;YACpH,+HAA+H;YAC/H,sKAAsK;SACvK,CAAC;KACL,CAAC;IACF,UAAU,CAAC;QACT,IAAI,EAAE,4CAA4C;QAClD,cAAc,EAAE,CAAC,UAAU,EAAE,uBAAuB,CAAC;QACrD,kBAAkB,EAAE,IAAI;QACxB,MAAM,EAAE,CAAC;aACN,MAAM,CAAC;YACN,YAAY,EAAE,UAAU,CAAC,QAAQ,EAAE;YACnC,WAAW,EAAE,UAAU,CAAC,QAAQ,EAAE;YAClC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,QAAQ,EAAE;YACpE,kBAAkB,EAAE,UAAU,CAAC,QAAQ,EAAE;YACzC,QAAQ,EAAE,CAAC;iBACR,KAAK,CACJ,CAAC;iBACE,MAAM,CAAC;gBACN,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,QAAQ,EAAE;gBACpD,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,QAAQ,EAAE;gBACzD,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,QAAQ,EAAE;gBACnD,WAAW,EAAE,CAAC;qBACX,MAAM,CACL,CAAC;qBACE,MAAM,EAAE;qBACR,KAAK,CAAC,OAAO,EAAE,yDAAyD,CAAC,EAC5E,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CACzD;qBACA,QAAQ,EAAE;gBACb,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC;aAC1D,CAAC;iBACD,MAAM,EAAE,CACZ;iBACA,GAAG,CAAC,CAAC,CAAC;iBACN,GAAG,CAAC,kBAAkB,CAAC;YAC1B,qBAAqB,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;SACvC,CAAC;aACD,MAAM,EAAE;aACR,MAAM,CACL,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,kBAAkB,KAAK,SAAS,CAAC,EACrF;YACE,OAAO,EACL,8FAA8F;SACjG,CACF;QACH,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACzB,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,eAAe,KAAK,CAAC,YAAY,+BAA+B;YACtE,IAAI,EAAE,aAAa,CAAC;gBAClB,UAAU,EAAE,KAAK,CAAC,WAAW;gBAC7B,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,gBAAgB,EAAE,KAAK,CAAC,kBAAkB;gBAC1C,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CACvC,aAAa,CAAC;oBACZ,OAAO,EAAE,OAAO,CAAC,OAAO;oBACxB,WAAW,EAAE,OAAO,CAAC,YAAY;oBACjC,MAAM,EAAE,OAAO,CAAC,MAAM;oBACtB,UAAU,EAAE,OAAO,CAAC,WAAW;oBAC/B,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC;iBACvC,CAAC,CACH;aACF,CAAC;SACH,CAAC;QACF,eAAe,EAAE,CAAC,QAAQ,EAAE,EAAE,CAC5B,gBAAgB,CAAC,QAAQ,EAAE;YACzB,2GAA2G;YAC3G,2JAA2J;YAC3J,mHAAmH;YACnH,+EAA+E;SAChF,CAAC;KACL,CAAC;IACF,UAAU,CAAC;QACT,IAAI,EAAE,6CAA6C;QACnD,cAAc,EAAE,CAAC,UAAU,CAAC;QAC5B,kBAAkB,EAAE,IAAI;QACxB,MAAM,EAAE,CAAC;aACN,MAAM,CAAC,EAAE,YAAY,EAAE,UAAU,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;aACrE,MAAM,EAAE;QACX,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACzB,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,eAAe,KAAK,CAAC,YAAY,iCAAiC,KAAK,CAAC,QAAQ,EAAE;SACzF,CAAC;QACF,eAAe,EAAE,CAAC,QAAQ,EAAE,EAAE,CAC5B,gBAAgB,CAAC,QAAQ,EAAE;YACzB,kHAAkH;YAClH,qTAAqT;SACtT,CAAC;KACL,CAAC;IACF,UAAU,CAAC;QACT,IAAI,EAAE,6CAA6C;QACnD,cAAc,EAAE,CAAC,UAAU,CAAC;QAC5B,kBAAkB,EAAE,IAAI;QACxB,MAAM,EAAE,CAAC;aACN,MAAM,CAAC,EAAE,YAAY,EAAE,UAAU,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;aACrE,MAAM,EAAE;QACX,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACzB,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,eAAe,KAAK,CAAC,YAAY,iCAAiC,KAAK,CAAC,QAAQ,SAAS;SAChG,CAAC;QACF,eAAe,EAAE,CAAC,QAAQ,EAAE,EAAE,CAC5B,gBAAgB,CAAC,QAAQ,EAAE;YACzB,wLAAwL;SACzL,CAAC;KACL,CAAC;IACF,UAAU,CAAC;QACT,IAAI,EAAE,6CAA6C;QACnD,cAAc,EAAE,CAAC,UAAU,EAAE,uBAAuB,CAAC;QACrD,kBAAkB,EAAE,IAAI;QACxB,MAAM,EAAE,CAAC;aACN,MAAM,CAAC;YACN,YAAY,EAAE,UAAU,CAAC,QAAQ,EAAE;YACnC,QAAQ,EAAE,UAAU;YACpB,qBAAqB,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;SACvC,CAAC;aACD,MAAM,EAAE;QACX,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACzB,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,eAAe,KAAK,CAAC,YAAY,iCAAiC,KAAK,CAAC,QAAQ,SAAS;SAChG,CAAC;QACF,eAAe,EAAE,CAAC,QAAQ,EAAE,EAAE,CAC5B,gBAAgB,CAAC,QAAQ,EAAE;YACzB,+FAA+F;SAChG,CAAC;KACL,CAAC;IACF,UAAU,CAAC;QACT,IAAI,EAAE,uCAAuC;QAC7C,cAAc,EAAE,CAAC,QAAQ,CAAC;QAC1B,kBAAkB,EAAE,IAAI;QACxB,MAAM,EAAE,CAAC;aACN,MAAM,CAAC;YACN,YAAY,EAAE,UAAU,CAAC,QAAQ,EAAE;YACnC,MAAM,EAAE,UAAU;SACnB,CAAC;aACD,MAAM,EAAE;QACX,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACzB,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,eAAe,KAAK,CAAC,YAAY,2BAA2B,KAAK,CAAC,MAAM,SAAS;SACxF,CAAC;QACF,eAAe,EAAE,CAAC,QAAQ,EAAE,EAAE,CAC5B,gBAAgB,CAAC,QAAQ,EAAE;YACzB,sFAAsF;SACvF,CAAC;KACL,CAAC;CACH,CAAC"}
@@ -0,0 +1,34 @@
1
+ export declare const linkTrackingTools: (import("./shared.js").ToolDefinition<{
2
+ workspace_id?: string | undefined;
3
+ include_archived?: boolean | undefined;
4
+ days?: number | undefined;
5
+ limit?: number | undefined;
6
+ offset?: number | undefined;
7
+ }> | import("./shared.js").ToolDefinition<{
8
+ name: string;
9
+ slug: string;
10
+ target_url: string;
11
+ workspace_id?: string | undefined;
12
+ product_code?: string | undefined;
13
+ }> | import("./shared.js").ToolDefinition<{
14
+ link_id: string;
15
+ workspace_id?: string | undefined;
16
+ name?: string | undefined;
17
+ target_url?: string | undefined;
18
+ product_code?: string | undefined;
19
+ restore?: true | undefined;
20
+ }> | import("./shared.js").ToolDefinition<{
21
+ link_id: string;
22
+ explicit_confirmation: true;
23
+ workspace_id?: string | undefined;
24
+ }> | import("./shared.js").ToolDefinition<{
25
+ link_id: string;
26
+ workspace_id?: string | undefined;
27
+ days?: number | undefined;
28
+ }> | import("./shared.js").ToolDefinition<{
29
+ link_id: string;
30
+ workspace_id?: string | undefined;
31
+ days?: number | undefined;
32
+ page?: number | undefined;
33
+ page_size?: number | undefined;
34
+ }>)[];
@@ -0,0 +1,200 @@
1
+ import { z } from 'zod';
2
+ import { buildListQuery, compactObject, defineTool, uuidSchema, withInstructions } from './shared.js';
3
+ const NAME_MAX_LENGTH = 200;
4
+ const TARGET_URL_MAX_LENGTH = 2048;
5
+ const PRODUCT_CODE_MAX_LENGTH = 64;
6
+ /** Форма слага — паритет с `CreateTrackedLinkDto`: латиница, цифры, дефис, подчёркивание. */
7
+ const SLUG_PATTERN = /^[a-zA-Z0-9_-]{1,64}$/;
8
+ /** Потолок глубины аналитики — паритет с `LINK_ANALYTICS_MAX_DAYS`. */
9
+ const ANALYTICS_MAX_DAYS = 366;
10
+ /** Потолок страницы каталога и ленты кликов — паритет с `LINK_CATALOG_PAGE_SIZE_MAX`. */
11
+ const PAGE_SIZE_MAX = 200;
12
+ /** Схемы целевого адреса — паритет с `LINK_ALLOWED_PROTOCOLS`. */
13
+ const ALLOWED_TARGET_PROTOCOLS = ['http:', 'https:'];
14
+ const slugSchema = z
15
+ .string()
16
+ .regex(SLUG_PATTERN, 'slug: латиница, цифры, дефис и подчёркивание, до 64 символов');
17
+ /**
18
+ * Целевой адрес обязан быть абсолютным http(s). Проверяем здесь, а не только на бэкенде:
19
+ * `javascript:`-адрес через наш общий домен — это фишинг, и узнавать о запрете из 400-й
20
+ * после того, как агент уже сочинил каталог ссылок, дороже, чем отвергнуть на входе.
21
+ *
22
+ * ⚠️ Разбираем `new URL()`, а не сверяем префикс: бэкендский `isAllowedTargetUrl` устроен
23
+ * именно так, и проверка префиксом принимала бы `http://` без хоста — значение, которое
24
+ * клиент считает валидным, а сервер отвергает 400-й.
25
+ */
26
+ const targetUrlSchema = z
27
+ .string()
28
+ .max(TARGET_URL_MAX_LENGTH)
29
+ .refine((value) => {
30
+ try {
31
+ return ALLOWED_TARGET_PROTOCOLS.includes(new URL(value).protocol);
32
+ }
33
+ catch {
34
+ return false;
35
+ }
36
+ }, { message: 'target_url должен быть абсолютным http(s)-адресом' });
37
+ const daysSchema = z.number().int().min(1).max(ANALYTICS_MAX_DAYS);
38
+ export const linkTrackingTools = [
39
+ defineTool({
40
+ name: 'besales_tracked_link_list',
41
+ requiredFields: [],
42
+ defaultWorkspaceId: true,
43
+ schema: z
44
+ .object({
45
+ workspace_id: uuidSchema.optional(),
46
+ include_archived: z.boolean().optional(),
47
+ days: daysSchema.optional(),
48
+ limit: z.number().int().min(1).max(PAGE_SIZE_MAX).optional(),
49
+ offset: z.number().int().min(0).optional(),
50
+ })
51
+ .strict(),
52
+ createRequest: (input) => ({
53
+ method: 'GET',
54
+ path: `/workspaces/${input.workspace_id}/tracked-links${buildListQuery({
55
+ includeArchived: input.include_archived,
56
+ days: input.days,
57
+ limit: input.limit,
58
+ offset: input.offset,
59
+ })}`,
60
+ }),
61
+ enhanceResponse: (response) => withInstructions(response, [
62
+ 'Каталог коротких ссылок воркспейса со сводкой «выдано → клики → ответы → заявки → оплаты».',
63
+ 'Поле placeholder — готовый токен ссылки. Копируй его ДОСЛОВНО в текст дожима, рассылки или шаблона; сырой URL мимо каталога не трекается.',
64
+ '⚠️ Токен не пиши руками и не подставляй в него свой слаг: несуществующий слаг вырезается вместе с маркером, и клиент получит фразу ВООБЩЕ без ссылки — молча, только с ошибкой в логах. В промпт агента токен не клади вовсе (модель воспроизведёт его неточно) — там используй staticUrl.',
65
+ 'staticUrl — анонимный короткий адрес для мест, где персональной выдачи нет: промпт агента (модель не воспроизведёт токен дословно), письмо школы, подпись вложения. Клики по нему считаются без привязки к человеку.',
66
+ 'hasMore=true означает, что каталог не дочитан: увеличивай offset. Вывод «такого слага нет» по неполной странице недействителен.',
67
+ 'Архивные ссылки по умолчанию скрыты — чтобы их увидеть, передай include_archived=true. Их слаги ЗАНЯТЫ: создание с тем же слагом даст 409. При include_archived=true архивные идут ПЕРВЫМИ.',
68
+ ]),
69
+ }),
70
+ defineTool({
71
+ name: 'besales_tracked_link_create',
72
+ requiredFields: ['name', 'slug', 'target_url'],
73
+ defaultWorkspaceId: true,
74
+ schema: z
75
+ .object({
76
+ workspace_id: uuidSchema.optional(),
77
+ name: z.string().trim().min(1).max(NAME_MAX_LENGTH),
78
+ slug: slugSchema,
79
+ target_url: targetUrlSchema,
80
+ product_code: z.string().max(PRODUCT_CODE_MAX_LENGTH).optional(),
81
+ })
82
+ .strict(),
83
+ createRequest: (input) => ({
84
+ method: 'POST',
85
+ path: `/workspaces/${input.workspace_id}/tracked-links`,
86
+ body: compactObject({
87
+ name: input.name,
88
+ slug: input.slug,
89
+ targetUrl: input.target_url,
90
+ productCode: input.product_code,
91
+ }),
92
+ }),
93
+ enhanceResponse: (response) => withInstructions(response, [
94
+ 'Ссылка заведена. Возьми из ответа поле placeholder и вставь его в тексты ДОСЛОВНО — сырой URL мимо каталога не трекается, а собранный вручную токен с неточным слагом вырежется, и клиент получит фразу без ссылки.',
95
+ 'Слаг НЕИЗМЕНЯЕМ: он уходит в отправленные сообщения. Выбирай осмысленный и стабильный (например shzh-w1-15), переименовать потом можно только name.',
96
+ '409 означает, что слаг занят — возможно, АРХИВНОЙ ссылкой. Тогда не выдумывай новый слаг: найди её через besales_tracked_link_list(include_archived=true) и восстанови besales_tracked_link_update(restore=true).',
97
+ ]),
98
+ }),
99
+ defineTool({
100
+ name: 'besales_tracked_link_update',
101
+ requiredFields: ['link_id'],
102
+ defaultWorkspaceId: true,
103
+ schema: z
104
+ .object({
105
+ workspace_id: uuidSchema.optional(),
106
+ link_id: uuidSchema,
107
+ name: z.string().trim().min(1).max(NAME_MAX_LENGTH).optional(),
108
+ target_url: targetUrlSchema.optional(),
109
+ product_code: z.string().max(PRODUCT_CODE_MAX_LENGTH).optional(),
110
+ restore: z.literal(true).optional(),
111
+ })
112
+ .strict()
113
+ .refine((input) => input.name !== undefined ||
114
+ input.target_url !== undefined ||
115
+ input.product_code !== undefined ||
116
+ input.restore !== undefined, { message: 'Передай хотя бы одно поле для изменения' }),
117
+ createRequest: (input) => ({
118
+ method: 'PATCH',
119
+ path: `/workspaces/${input.workspace_id}/tracked-links/${input.link_id}`,
120
+ body: compactObject({
121
+ name: input.name,
122
+ targetUrl: input.target_url,
123
+ productCode: input.product_code,
124
+ restore: input.restore,
125
+ }),
126
+ }),
127
+ enhanceResponse: (response) => withInstructions(response, [
128
+ '⚠️ Правка target_url действует ТОЛЬКО ВПЕРЁД, и это три разных случая. Персональные коды, УЖЕ ушедшие людям, остаются на СТАРОМ адресе: каждая выдача хранит снимок адреса на момент отправки, и резолвер читает именно его. Новые выдачи получат новый адрес. Анонимный staticUrl переключается на новый адрес сразу.',
129
+ 'Отсюда следствие: правкой target_url НЕЛЬЗЯ перенаправить тех, кому ссылка уже отправлена, — для этого нужна новая ссылка в новом тексте. Снимок ещё и делает отчёты доказуемыми: видно, куда вело конкретное сообщение.',
130
+ 'Поля ответа сюда не копируются: slug неизменяем, а archivedAt не поле записи — снятие из архива это restore=true, архивация — besales_tracked_link_archive. Счётчики (issued/clicked/...) и placeholder/staticUrl только читаются.',
131
+ ]),
132
+ }),
133
+ defineTool({
134
+ name: 'besales_tracked_link_archive',
135
+ requiredFields: ['link_id', 'explicit_confirmation'],
136
+ defaultWorkspaceId: true,
137
+ schema: z
138
+ .object({
139
+ workspace_id: uuidSchema.optional(),
140
+ link_id: uuidSchema,
141
+ explicit_confirmation: z.literal(true),
142
+ })
143
+ .strict(),
144
+ createRequest: (input) => ({
145
+ method: 'DELETE',
146
+ path: `/workspaces/${input.workspace_id}/tracked-links/${input.link_id}`,
147
+ }),
148
+ enhanceResponse: (response) => withInstructions(response, [
149
+ 'Это АРХИВАЦИЯ, а не удаление: hard-delete у ссылок отсутствует в принципе. Код уже лежит в отправленных людям сообщениях, и удаление превратило бы живую ссылку в 404.',
150
+ 'Архивная ссылка перестаёт подставляться в новые тексты, но её код продолжает резолвиться, клики продолжают считаться, а слаг остаётся занятым.',
151
+ 'Восстановление — besales_tracked_link_update(restore=true). explicit_confirmation=true передавай только после явного подтверждения пользователя.',
152
+ ]),
153
+ }),
154
+ defineTool({
155
+ name: 'besales_tracked_link_breakdown',
156
+ requiredFields: ['link_id'],
157
+ defaultWorkspaceId: true,
158
+ schema: z
159
+ .object({
160
+ workspace_id: uuidSchema.optional(),
161
+ link_id: uuidSchema,
162
+ days: daysSchema.optional(),
163
+ })
164
+ .strict(),
165
+ createRequest: (input) => ({
166
+ method: 'GET',
167
+ path: `/workspaces/${input.workspace_id}/tracked-links/${input.link_id}/breakdown${buildListQuery({ days: input.days })}`,
168
+ }),
169
+ enhanceResponse: (response) => withInstructions(response, [
170
+ 'Разрез одной ссылки по источникам выдачи: WAVE (волна), FOLLOWUP (дожим), CAMPAIGN (кампания), GREETING (приветствие), AGENT (обычный ответ агента).',
171
+ 'sourceId у волны пуст: welcome запускается переходом сделки, а не джобом импорта, поэтому разрез отвечает «после волны», но не «после какой именно».',
172
+ 'Оплата атрибутируется last-touch по ближайшему клику, поэтому суммы по разным ссылкам не складываются в общую выручку дважды.',
173
+ ]),
174
+ }),
175
+ defineTool({
176
+ name: 'besales_tracked_link_clicks',
177
+ requiredFields: ['link_id'],
178
+ defaultWorkspaceId: true,
179
+ schema: z
180
+ .object({
181
+ workspace_id: uuidSchema.optional(),
182
+ link_id: uuidSchema,
183
+ days: daysSchema.optional(),
184
+ page: z.number().int().min(1).optional(),
185
+ page_size: z.number().int().min(1).max(PAGE_SIZE_MAX).optional(),
186
+ })
187
+ .strict(),
188
+ createRequest: (input) => ({
189
+ method: 'GET',
190
+ path: `/workspaces/${input.workspace_id}/tracked-links/${input.link_id}/clicks${buildListQuery({ days: input.days, page: input.page, pageSize: input.page_size })}`,
191
+ }),
192
+ enhanceResponse: (response) => withInstructions(response, [
193
+ 'Лента переходов живых людей: контакт, сообщение, источник, время. Клики-предпросмотры мессенджеров отсеяны и сюда не попадают.',
194
+ '⚠️ Анонимные переходы по staticUrl В ЛЕНТЕ ЕСТЬ — строками, где chatUserId, contactName, messageId, issuedAt и sourceKind равны null. Это не битый ответ: у анонимного кода человека нет по построению. Не считай такие строки людьми; их суммарное число отдельно лежит в каталоге полем anonymousClicks.',
195
+ '⚠️ total — это число КЛИКОВ (включая повторные и анонимные), а поле clicked в каталоге — число ВЫДАЧ с первым живым кликом. Величины разные, сходиться не обязаны и по-разному отвечают на «сколько человек перешло».',
196
+ 'Для следующей страницы увеличивай page; максимальный page_size — 200.',
197
+ ]),
198
+ }),
199
+ ];
200
+ //# sourceMappingURL=link-tracking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-tracking.js","sourceRoot":"","sources":["../../../src/tools/definitions/link-tracking.ts"],"names":[],"mappings":"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"}
@@ -613,6 +613,7 @@ export declare const platformSetupTools: (import("./shared.js").ToolDefinition<{
613
613
  create_missing_deal_offer_id?: number | null | undefined;
614
614
  create_missing_deal_funnel_id?: number | null | undefined;
615
615
  create_missing_deal_stage_id?: number | null | undefined;
616
+ revenue_offer_ids?: number[] | undefined;
616
617
  } | undefined;
617
618
  }> | import("./shared.js").ToolDefinition<{
618
619
  audit_log_id: string;
@@ -800,6 +800,7 @@ export declare const platformGetcourseSettingsSchema: z.ZodObject<{
800
800
  create_missing_deal_offer_id: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
801
801
  create_missing_deal_funnel_id: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
802
802
  create_missing_deal_stage_id: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
803
+ revenue_offer_ids: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
803
804
  }, z.core.$strict>;
804
805
  export declare const knowledgeTableSourceSchema: z.ZodObject<{
805
806
  kind: z.ZodEnum<{
@@ -427,6 +427,10 @@ export const pipelineAutoFinishSchema = z.object({
427
427
  // Platform settings typed sections (Group C.2 + Increment 4c per-channel)
428
428
  export const platformAccessControlSchema = z
429
429
  .object({
430
+ // У Instagram это НАМЕРЕНИЕ держать канал включённым, а не факт работы: эффективная
431
+ // активность = намерение И подтверждённая подписка на вебхуки. Прочитав `is_active` обратно
432
+ // через `besales_platform_context_get`, можно увидеть `false` при успешно сохранённом `true` —
433
+ // это не отказ операции, а неподтверждённая подписка (лечится resubscribe).
430
434
  is_active: z.boolean().optional(),
431
435
  is_ai_enabled: z.boolean().optional(),
432
436
  is_user_filter_enabled: z.boolean().optional(),
@@ -605,6 +609,10 @@ export const platformGetcourseSettingsSchema = z
605
609
  create_missing_deal_offer_id: z.number().int().positive().nullable().optional(),
606
610
  create_missing_deal_funnel_id: z.number().int().positive().nullable().optional(),
607
611
  create_missing_deal_stage_id: z.number().int().positive().nullable().optional(),
612
+ // Офферы, оплата заказов с которыми засчитывается нам независимо от автора заказа (агент
613
+ // или менеджер руками). Пустой массив = выключить. Набор действует на ШКОЛУ целиком.
614
+ // Меняет денежную атрибуцию задним числом → dangerous, требует explicit_confirmation.
615
+ revenue_offer_ids: z.array(z.number().int().positive()).optional(),
608
616
  })
609
617
  .strict();
610
618
  // Knowledge tables (Group A.5)
@@ -969,6 +977,9 @@ export function mapPlatformGetcourseSettingsInput(input) {
969
977
  createMissingDealOfferId: input.create_missing_deal_offer_id,
970
978
  createMissingDealFunnelId: input.create_missing_deal_funnel_id,
971
979
  createMissingDealStageId: input.create_missing_deal_stage_id,
980
+ // Офферы зачёта выручки: пустой массив — валидное значение «выключить», поэтому передаём
981
+ // как есть; undefined отстрипнет compactObject.
982
+ revenueOfferIds: input.revenue_offer_ids,
972
983
  });
973
984
  }
974
985
  //# sourceMappingURL=shared.js.map