@besales/mcp 0.48.0 → 1.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +109 -164
- package/README.onprem.ru.md +35 -0
- package/bin/besales-mcp-http.js +9 -0
- package/bin/besales-onprem.mjs +19 -0
- package/dist/auth/connection-store.d.ts +3 -1
- package/dist/auth/connection-store.js +9 -3
- package/dist/auth/connection-store.js.map +1 -1
- package/dist/auth/oauth-client.d.ts +20 -27
- package/dist/auth/oauth-client.js +31 -23
- package/dist/auth/oauth-client.js.map +1 -1
- package/dist/auth/session-workspace.d.ts +1 -0
- package/dist/auth/session-workspace.js +10 -1
- package/dist/auth/session-workspace.js.map +1 -1
- package/dist/auth/token-storage.js +4 -1
- package/dist/auth/token-storage.js.map +1 -1
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +80 -19
- package/dist/cli.js.map +1 -1
- package/dist/constants/crm-catalog.d.ts +2 -0
- package/dist/constants/crm-catalog.js +3 -0
- package/dist/constants/crm-catalog.js.map +1 -0
- package/dist/http/api-client.d.ts +52 -7
- package/dist/http/api-client.js +131 -66
- package/dist/http/api-client.js.map +1 -1
- package/dist/http/feedback-document-error.d.ts +26 -0
- package/dist/http/feedback-document-error.js +22 -0
- package/dist/http/feedback-document-error.js.map +1 -0
- package/dist/http-server.d.ts +15 -0
- package/dist/http-server.js +378 -0
- package/dist/http-server.js.map +1 -0
- package/dist/instructions/server-instructions.d.ts +1 -1
- package/dist/instructions/server-instructions.js +6 -3
- package/dist/instructions/server-instructions.js.map +1 -1
- package/dist/network/constants.d.ts +25 -0
- package/dist/network/constants.js +26 -0
- package/dist/network/constants.js.map +1 -0
- package/dist/network/failed-verifications.d.ts +10 -0
- package/dist/network/failed-verifications.js +37 -0
- package/dist/network/failed-verifications.js.map +1 -0
- package/dist/network/http-test-fixture.d.ts +3 -0
- package/dist/network/http-test-fixture.js +86 -0
- package/dist/network/http-test-fixture.js.map +1 -0
- package/dist/network/known-keys.d.ts +10 -0
- package/dist/network/known-keys.js +38 -0
- package/dist/network/known-keys.js.map +1 -0
- package/dist/network/memory-credentials.d.ts +11 -0
- package/dist/network/memory-credentials.js +24 -0
- package/dist/network/memory-credentials.js.map +1 -0
- package/dist/network/rate-limit.d.ts +23 -0
- package/dist/network/rate-limit.js +62 -0
- package/dist/network/rate-limit.js.map +1 -0
- package/dist/network/rejected-keys.d.ts +9 -0
- package/dist/network/rejected-keys.js +39 -0
- package/dist/network/rejected-keys.js.map +1 -0
- package/dist/network/request-work.d.ts +8 -0
- package/dist/network/request-work.js +20 -0
- package/dist/network/request-work.js.map +1 -0
- package/dist/network/successful-keys.d.ts +17 -0
- package/dist/network/successful-keys.js +47 -0
- package/dist/network/successful-keys.js.map +1 -0
- package/dist/resources/concepts/external-execution.md +3 -1
- package/dist/resources/concepts/feedback-sheets.md +21 -12
- package/dist/resources/concepts/icp.md +8 -1
- package/dist/resources/concepts/sandbox.md +15 -6
- package/dist/resources/concepts/triggers.md +8 -0
- package/dist/resources/concepts/workbook-classification.md +104 -50
- package/dist/resources/docs/agent-behavior.md +51 -382
- package/dist/resources/docs/campaigns.md +9 -16
- package/dist/resources/docs/crm-integration.md +39 -523
- package/dist/resources/docs/files-and-uploads.md +23 -286
- package/dist/resources/docs/follow-up.md +47 -12
- package/dist/resources/docs/knowledge-base.md +52 -521
- package/dist/resources/docs/pipeline-builder.md +26 -216
- package/dist/resources/docs/pipeline-settings-deep.md +27 -220
- package/dist/resources/docs/platforms.md +106 -519
- package/dist/resources/docs/prompt-anatomy.md +39 -285
- package/dist/resources/docs/routing.md +8 -16
- package/dist/resources/docs/trigger-actions.md +40 -24
- package/dist/resources/docs/variables.md +8 -12
- package/dist/resources/operator-edit.contract.d.ts +7 -0
- package/dist/resources/operator-edit.contract.js +8 -0
- package/dist/resources/operator-edit.contract.js.map +1 -0
- package/dist/resources/registry.js +20 -4
- package/dist/resources/registry.js.map +1 -1
- package/dist/resources/workflows/audit-dialogues.md +23 -12
- package/dist/resources/workflows/compare-models.md +6 -6
- package/dist/resources/workflows/connect-crm-from-scratch.md +15 -12
- package/dist/resources/workflows/connect-datasource-from-scratch.md +5 -4
- package/dist/resources/workflows/extract-from-document.md +8 -6
- package/dist/resources/workflows/operator-edit.md +15 -0
- package/dist/resources/workflows/platform-setup-from-scratch.md +59 -70
- package/dist/resources/workflows/production-readiness-check.md +25 -15
- package/dist/schemas/mcp-tools.json +9902 -4734
- package/dist/schemas/voice-actions-api.contract.json +248 -0
- package/dist/shared/constants/broadcast-reason.constants.d.ts +2 -0
- package/dist/shared/constants/broadcast-reason.constants.js +47 -0
- package/dist/shared/constants/broadcast-reason.constants.js.map +1 -0
- package/dist/shared/constants/crm-stage-pool.constants.d.ts +2 -0
- package/dist/shared/constants/crm-stage-pool.constants.js +3 -0
- package/dist/shared/constants/crm-stage-pool.constants.js.map +1 -0
- package/dist/shared/constants/followup-note.constants.d.ts +3 -0
- package/dist/shared/constants/followup-note.constants.js +4 -0
- package/dist/shared/constants/followup-note.constants.js.map +1 -0
- package/dist/shared/constants/followup-session.constants.d.ts +2 -0
- package/dist/shared/constants/followup-session.constants.js +3 -0
- package/dist/shared/constants/followup-session.constants.js.map +1 -0
- package/dist/shared/constants/funnel-analytics.constants.d.ts +2 -0
- package/dist/shared/constants/funnel-analytics.constants.js +3 -0
- package/dist/shared/constants/funnel-analytics.constants.js.map +1 -0
- package/dist/shared/constants/message-filter.constants.d.ts +7 -0
- package/dist/shared/constants/message-filter.constants.js +8 -0
- package/dist/shared/constants/message-filter.constants.js.map +1 -0
- package/dist/shared/utils/trigger-repeat-mapping.d.ts +72 -0
- package/dist/shared/utils/trigger-repeat-mapping.js +53 -0
- package/dist/shared/utils/trigger-repeat-mapping.js.map +1 -0
- package/dist/skills/besales-dialogue-audit/SKILL.md +31 -20
- package/dist/tasks/task-state-store.js +5 -0
- package/dist/tasks/task-state-store.js.map +1 -1
- package/dist/tools/annotations.js +4 -3
- package/dist/tools/annotations.js.map +1 -1
- package/dist/tools/definitions/agent-design.d.ts +78 -21
- package/dist/tools/definitions/agent-design.js +246 -90
- package/dist/tools/definitions/agent-design.js.map +1 -1
- package/dist/tools/definitions/agent-model-notice.d.ts +5 -0
- package/dist/tools/definitions/agent-model-notice.js +31 -0
- package/dist/tools/definitions/agent-model-notice.js.map +1 -0
- package/dist/tools/definitions/agent-models.d.ts +5 -0
- package/dist/tools/definitions/agent-models.js +26 -0
- package/dist/tools/definitions/agent-models.js.map +1 -0
- package/dist/tools/definitions/agent-settings.d.ts +85 -0
- package/dist/tools/definitions/agent-settings.js +187 -0
- package/dist/tools/definitions/agent-settings.js.map +1 -0
- package/dist/tools/definitions/ai-costs.d.ts +12 -0
- package/dist/tools/definitions/ai-costs.js +142 -0
- package/dist/tools/definitions/ai-costs.js.map +1 -0
- package/dist/tools/definitions/api-tool-request.d.ts +15 -0
- package/dist/tools/definitions/api-tool-request.js +2 -0
- package/dist/tools/definitions/api-tool-request.js.map +1 -0
- package/dist/tools/definitions/behavior-shipped-form.d.ts +182 -0
- package/dist/tools/definitions/behavior-shipped-form.js +185 -0
- package/dist/tools/definitions/behavior-shipped-form.js.map +1 -0
- package/dist/tools/definitions/broadcast-alerts.d.ts +6 -0
- package/dist/tools/definitions/broadcast-alerts.js +72 -0
- package/dist/tools/definitions/broadcast-alerts.js.map +1 -0
- package/dist/tools/definitions/broadcast-analytics.d.ts +102 -18
- package/dist/tools/definitions/broadcast-analytics.js +732 -431
- package/dist/tools/definitions/broadcast-analytics.js.map +1 -1
- package/dist/tools/definitions/broadcast-scheduler.d.ts +10 -13
- package/dist/tools/definitions/broadcast-scheduler.js +196 -154
- package/dist/tools/definitions/broadcast-scheduler.js.map +1 -1
- package/dist/tools/definitions/call-operator-task.schema.d.ts +11 -0
- package/dist/tools/definitions/call-operator-task.schema.js +30 -0
- package/dist/tools/definitions/call-operator-task.schema.js.map +1 -0
- package/dist/tools/definitions/contacts.d.ts +3 -0
- package/dist/tools/definitions/contacts.js +57 -31
- package/dist/tools/definitions/contacts.js.map +1 -1
- package/dist/tools/definitions/crm-platform.d.ts +28 -41
- package/dist/tools/definitions/crm-platform.js +235 -368
- package/dist/tools/definitions/crm-platform.js.map +1 -1
- package/dist/tools/definitions/crm-stage-pool.messages.d.ts +39 -0
- package/dist/tools/definitions/crm-stage-pool.messages.js +43 -0
- package/dist/tools/definitions/crm-stage-pool.messages.js.map +1 -0
- package/dist/tools/definitions/crm-stage-pool.schema.d.ts +64 -0
- package/dist/tools/definitions/crm-stage-pool.schema.js +109 -0
- package/dist/tools/definitions/crm-stage-pool.schema.js.map +1 -0
- package/dist/tools/definitions/custom-reports.d.ts +12 -0
- package/dist/tools/definitions/custom-reports.js +68 -0
- package/dist/tools/definitions/custom-reports.js.map +1 -0
- package/dist/tools/definitions/datasource.d.ts +2 -1
- package/dist/tools/definitions/datasource.js +28 -13
- package/dist/tools/definitions/datasource.js.map +1 -1
- package/dist/tools/definitions/dialog-review-findings-response.d.ts +14 -0
- package/dist/tools/definitions/dialog-review-findings-response.js +23 -0
- package/dist/tools/definitions/dialog-review-findings-response.js.map +1 -0
- package/dist/tools/definitions/dialog-review-safe-response.d.ts +5 -0
- package/dist/tools/definitions/dialog-review-safe-response.js +152 -0
- package/dist/tools/definitions/dialog-review-safe-response.js.map +1 -0
- package/dist/tools/definitions/dialog-review.d.ts +49 -0
- package/dist/tools/definitions/dialog-review.js +383 -0
- package/dist/tools/definitions/dialog-review.js.map +1 -0
- package/dist/tools/definitions/dialogue-analysis.d.ts +46 -0
- package/dist/tools/definitions/dialogue-analysis.js +170 -0
- package/dist/tools/definitions/dialogue-analysis.js.map +1 -0
- package/dist/tools/definitions/dialogue-audit-agent-scope.d.ts +7 -0
- package/dist/tools/definitions/dialogue-audit-agent-scope.js +41 -0
- package/dist/tools/definitions/dialogue-audit-agent-scope.js.map +1 -0
- package/dist/tools/definitions/dialogue-audit-alias.d.ts +67 -0
- package/dist/tools/definitions/dialogue-audit-alias.js +559 -0
- package/dist/tools/definitions/dialogue-audit-alias.js.map +1 -0
- package/dist/tools/definitions/dialogue-export.d.ts +7 -1
- package/dist/tools/definitions/dialogue-export.js +89 -32
- package/dist/tools/definitions/dialogue-export.js.map +1 -1
- package/dist/tools/definitions/dialogue-followup-state.d.ts +9 -0
- package/dist/tools/definitions/dialogue-followup-state.js +69 -0
- package/dist/tools/definitions/dialogue-followup-state.js.map +1 -0
- package/dist/tools/definitions/edit-version-transport.test-fixtures.d.ts +13 -0
- package/dist/tools/definitions/edit-version-transport.test-fixtures.js +292 -0
- package/dist/tools/definitions/edit-version-transport.test-fixtures.js.map +1 -0
- package/dist/tools/definitions/edit-version.d.ts +5 -0
- package/dist/tools/definitions/edit-version.js +20 -0
- package/dist/tools/definitions/edit-version.js.map +1 -0
- package/dist/tools/definitions/external-api-channels.js +17 -15
- package/dist/tools/definitions/external-api-channels.js.map +1 -1
- package/dist/tools/definitions/feedback-document-access.d.ts +16 -0
- package/dist/tools/definitions/feedback-document-access.js +63 -0
- package/dist/tools/definitions/feedback-document-access.js.map +1 -0
- package/dist/tools/definitions/fixtures/shipped-setup-status.d.ts +3 -0
- package/dist/tools/definitions/fixtures/shipped-setup-status.js +9 -0
- package/dist/tools/definitions/fixtures/shipped-setup-status.js.map +1 -0
- package/dist/tools/definitions/follow-up-config.d.ts +79 -0
- package/dist/tools/definitions/follow-up-config.js +258 -0
- package/dist/tools/definitions/follow-up-config.js.map +1 -0
- package/dist/tools/definitions/follow-up-funnel-test.d.ts +4 -3
- package/dist/tools/definitions/follow-up-funnel-test.js +65 -52
- package/dist/tools/definitions/follow-up-funnel-test.js.map +1 -1
- package/dist/tools/definitions/follow-up-promotion-instructions.d.ts +2 -0
- package/dist/tools/definitions/follow-up-promotion-instructions.js +7 -0
- package/dist/tools/definitions/follow-up-promotion-instructions.js.map +1 -0
- package/dist/tools/definitions/follow-up-setup.d.ts +4 -28
- package/dist/tools/definitions/follow-up-setup.js +20 -87
- package/dist/tools/definitions/follow-up-setup.js.map +1 -1
- package/dist/tools/definitions/follow-up.d.ts +20 -122
- package/dist/tools/definitions/follow-up.js +200 -357
- package/dist/tools/definitions/follow-up.js.map +1 -1
- package/dist/tools/definitions/followup-getcourse-compat.d.ts +29 -0
- package/dist/tools/definitions/followup-getcourse-compat.js +62 -0
- package/dist/tools/definitions/followup-getcourse-compat.js.map +1 -0
- package/dist/tools/definitions/followup-sequence-config.schema.d.ts +10 -0
- package/dist/tools/definitions/followup-sequence-config.schema.js +14 -0
- package/dist/tools/definitions/followup-sequence-config.schema.js.map +1 -0
- package/dist/tools/definitions/funnel-analytics.d.ts +18 -0
- package/dist/tools/definitions/funnel-analytics.js +150 -0
- package/dist/tools/definitions/funnel-analytics.js.map +1 -0
- package/dist/tools/definitions/getcourse-anchor-refresh.d.ts +5 -9
- package/dist/tools/definitions/getcourse-anchor-refresh.js +26 -31
- package/dist/tools/definitions/getcourse-anchor-refresh.js.map +1 -1
- package/dist/tools/definitions/getcourse-cold-outbound-settings.d.ts +46 -0
- package/dist/tools/definitions/getcourse-cold-outbound-settings.js +37 -0
- package/dist/tools/definitions/getcourse-cold-outbound-settings.js.map +1 -0
- package/dist/tools/definitions/getcourse-cold-welcome.d.ts +14 -29
- package/dist/tools/definitions/getcourse-cold-welcome.js +180 -254
- package/dist/tools/definitions/getcourse-cold-welcome.js.map +1 -1
- package/dist/tools/definitions/getcourse-wave-welcome.messages.d.ts +10 -0
- package/dist/tools/definitions/getcourse-wave-welcome.messages.js +17 -0
- package/dist/tools/definitions/getcourse-wave-welcome.messages.js.map +1 -0
- package/dist/tools/definitions/help.js.map +1 -1
- package/dist/tools/definitions/icp-import-source.d.ts +75 -0
- package/dist/tools/definitions/icp-import-source.js +88 -0
- package/dist/tools/definitions/icp-import-source.js.map +1 -0
- package/dist/tools/definitions/inbound-analytics.d.ts +13 -0
- package/dist/tools/definitions/inbound-analytics.js +55 -0
- package/dist/tools/definitions/inbound-analytics.js.map +1 -0
- package/dist/tools/definitions/instagram-comment.templates.d.ts +19 -0
- package/dist/tools/definitions/instagram-comment.templates.js +34 -0
- package/dist/tools/definitions/instagram-comment.templates.js.map +1 -0
- package/dist/tools/definitions/instagram-settings-compat.d.ts +33 -0
- package/dist/tools/definitions/instagram-settings-compat.js +7 -0
- package/dist/tools/definitions/instagram-settings-compat.js.map +1 -0
- package/dist/tools/definitions/knowledge-chunking.d.ts +42 -0
- package/dist/tools/definitions/knowledge-chunking.js +83 -0
- package/dist/tools/definitions/knowledge-chunking.js.map +1 -0
- package/dist/tools/definitions/knowledge-space-search-fields.d.ts +12 -0
- package/dist/tools/definitions/knowledge-space-search-fields.js +30 -0
- package/dist/tools/definitions/knowledge-space-search-fields.js.map +1 -0
- package/dist/tools/definitions/knowledge.d.ts +24 -14
- package/dist/tools/definitions/knowledge.js +342 -233
- package/dist/tools/definitions/knowledge.js.map +1 -1
- package/dist/tools/definitions/legacy-trigger-repeat.d.ts +17 -0
- package/dist/tools/definitions/legacy-trigger-repeat.js +53 -0
- package/dist/tools/definitions/legacy-trigger-repeat.js.map +1 -0
- package/dist/tools/definitions/link-tracking.d.ts +8 -0
- package/dist/tools/definitions/link-tracking.js +141 -38
- package/dist/tools/definitions/link-tracking.js.map +1 -1
- package/dist/tools/definitions/mailing-variant-content.d.ts +75 -0
- package/dist/tools/definitions/mailing-variant-content.js +73 -0
- package/dist/tools/definitions/mailing-variant-content.js.map +1 -0
- package/dist/tools/definitions/model-comparison.d.ts +1 -0
- package/dist/tools/definitions/model-comparison.js +4 -5
- package/dist/tools/definitions/model-comparison.js.map +1 -1
- package/dist/tools/definitions/money-currency.d.ts +5 -0
- package/dist/tools/definitions/money-currency.js +10 -0
- package/dist/tools/definitions/money-currency.js.map +1 -0
- package/dist/tools/definitions/operator-workflow.d.ts +5 -5
- package/dist/tools/definitions/operator-workflow.js +80 -27
- package/dist/tools/definitions/operator-workflow.js.map +1 -1
- package/dist/tools/definitions/platform-funnel-analytics.d.ts +42 -0
- package/dist/tools/definitions/platform-funnel-analytics.js +72 -0
- package/dist/tools/definitions/platform-funnel-analytics.js.map +1 -0
- package/dist/tools/definitions/platform-getcourse-wave-field-bindings.d.ts +42 -0
- package/dist/tools/definitions/platform-getcourse-wave-field-bindings.js +51 -0
- package/dist/tools/definitions/platform-getcourse-wave-field-bindings.js.map +1 -0
- package/dist/tools/definitions/platform-settings-limits.d.ts +5 -0
- package/dist/tools/definitions/platform-settings-limits.js +6 -0
- package/dist/tools/definitions/platform-settings-limits.js.map +1 -0
- package/dist/tools/definitions/platform-settings-shipped-form.d.ts +37 -0
- package/dist/tools/definitions/platform-settings-shipped-form.js +310 -0
- package/dist/tools/definitions/platform-settings-shipped-form.js.map +1 -0
- package/dist/tools/definitions/platform-settings-update.d.ts +300 -0
- package/dist/tools/definitions/platform-settings-update.js +134 -0
- package/dist/tools/definitions/platform-settings-update.js.map +1 -0
- package/dist/tools/definitions/{getcourse-wave.d.ts → platform-setup-wave.d.ts} +75 -6
- package/dist/tools/definitions/platform-setup-wave.js +1079 -0
- package/dist/tools/definitions/platform-setup-wave.js.map +1 -0
- package/dist/tools/definitions/platform-setup.d.ts +686 -224
- package/dist/tools/definitions/platform-setup.js +313 -752
- package/dist/tools/definitions/platform-setup.js.map +1 -1
- package/dist/tools/definitions/platform-widget-page-rules.schema.d.ts +10 -0
- package/dist/tools/definitions/platform-widget-page-rules.schema.js +7 -0
- package/dist/tools/definitions/platform-widget-page-rules.schema.js.map +1 -0
- package/dist/tools/definitions/prompt-authoring.constants.d.ts +2 -0
- package/dist/tools/definitions/prompt-authoring.constants.js +3 -0
- package/dist/tools/definitions/prompt-authoring.constants.js.map +1 -0
- package/dist/tools/definitions/prompt-batch-controls.d.ts +5 -0
- package/dist/tools/definitions/prompt-batch-controls.js +40 -0
- package/dist/tools/definitions/prompt-batch-controls.js.map +1 -0
- package/dist/tools/definitions/prompt-draft-authoring.d.ts +5 -0
- package/dist/tools/definitions/prompt-draft-authoring.js +24 -0
- package/dist/tools/definitions/prompt-draft-authoring.js.map +1 -0
- package/dist/tools/definitions/prompt-text-projection.d.ts +11 -0
- package/dist/tools/definitions/prompt-text-projection.js +62 -0
- package/dist/tools/definitions/prompt-text-projection.js.map +1 -0
- package/dist/tools/definitions/reviewer-paid.d.ts +19 -0
- package/dist/tools/definitions/reviewer-paid.js +115 -0
- package/dist/tools/definitions/reviewer-paid.js.map +1 -0
- package/dist/tools/definitions/routing-condition-projection.d.ts +31 -0
- package/dist/tools/definitions/routing-condition-projection.js +97 -0
- package/dist/tools/definitions/routing-condition-projection.js.map +1 -0
- package/dist/tools/definitions/schedule-selection-hints.d.ts +3 -0
- package/dist/tools/definitions/schedule-selection-hints.js +42 -0
- package/dist/tools/definitions/schedule-selection-hints.js.map +1 -0
- package/dist/tools/definitions/{scheduled-tasks.d.ts → scheduled-task-alias.d.ts} +12 -10
- package/dist/tools/definitions/scheduled-task-alias.js +207 -0
- package/dist/tools/definitions/scheduled-task-alias.js.map +1 -0
- package/dist/tools/definitions/session.js +3 -3
- package/dist/tools/definitions/session.js.map +1 -1
- package/dist/tools/definitions/setup-init-operation.schema.d.ts +2 -0
- package/dist/tools/definitions/setup-init-operation.schema.js +6 -0
- package/dist/tools/definitions/setup-init-operation.schema.js.map +1 -0
- package/dist/tools/definitions/shared-input.helpers.d.ts +51 -0
- package/dist/tools/definitions/shared-input.helpers.js +323 -0
- package/dist/tools/definitions/shared-input.helpers.js.map +1 -0
- package/dist/tools/definitions/shared.d.ts +396 -307
- package/dist/tools/definitions/shared.js +430 -652
- package/dist/tools/definitions/shared.js.map +1 -1
- package/dist/tools/definitions/shipped-output-names.d.ts +19 -0
- package/dist/tools/definitions/shipped-output-names.js +123 -0
- package/dist/tools/definitions/shipped-output-names.js.map +1 -0
- package/dist/tools/definitions/task-state.d.ts +5 -5
- package/dist/tools/definitions/task-state.js +2 -1
- package/dist/tools/definitions/task-state.js.map +1 -1
- package/dist/tools/definitions/telegram-personal-capacity.d.ts +4 -0
- package/dist/tools/definitions/telegram-personal-capacity.js +18 -0
- package/dist/tools/definitions/telegram-personal-capacity.js.map +1 -0
- package/dist/tools/definitions/telegram-pool-policy.d.ts +8 -0
- package/dist/tools/definitions/telegram-pool-policy.js +56 -0
- package/dist/tools/definitions/telegram-pool-policy.js.map +1 -0
- package/dist/tools/definitions/touch-history-alias.d.ts +4 -0
- package/dist/tools/definitions/touch-history-alias.js +99 -0
- package/dist/tools/definitions/touch-history-alias.js.map +1 -0
- package/dist/tools/definitions/trigger-action-item.schema.d.ts +254 -0
- package/dist/tools/definitions/trigger-action-item.schema.js +121 -0
- package/dist/tools/definitions/trigger-action-item.schema.js.map +1 -0
- package/dist/tools/definitions/trigger-argument.schema.d.ts +14 -0
- package/dist/tools/definitions/trigger-argument.schema.js +23 -0
- package/dist/tools/definitions/trigger-argument.schema.js.map +1 -0
- package/dist/tools/definitions/trigger-claim-alias.d.ts +10 -0
- package/dist/tools/definitions/trigger-claim-alias.js +90 -0
- package/dist/tools/definitions/trigger-claim-alias.js.map +1 -0
- package/dist/tools/definitions/trigger-echo-projection.d.ts +1 -0
- package/dist/tools/definitions/trigger-echo-projection.js +41 -0
- package/dist/tools/definitions/trigger-echo-projection.js.map +1 -0
- package/dist/tools/definitions/trigger-upsert.tool.d.ts +29 -0
- package/dist/tools/definitions/trigger-upsert.tool.js +122 -0
- package/dist/tools/definitions/trigger-upsert.tool.js.map +1 -0
- package/dist/tools/definitions/v15-port-descriptions.d.ts +11 -0
- package/dist/tools/definitions/v15-port-descriptions.js +30 -0
- package/dist/tools/definitions/v15-port-descriptions.js.map +1 -0
- package/dist/tools/definitions/variables.d.ts +44 -0
- package/dist/tools/definitions/variables.js +310 -0
- package/dist/tools/definitions/variables.js.map +1 -0
- package/dist/tools/definitions/voice-action-input.d.ts +9 -0
- package/dist/tools/definitions/voice-action-input.js +141 -0
- package/dist/tools/definitions/voice-action-input.js.map +1 -0
- package/dist/tools/definitions/widget-customization-compat.d.ts +51 -0
- package/dist/tools/definitions/widget-customization-compat.js +23 -0
- package/dist/tools/definitions/widget-customization-compat.js.map +1 -0
- package/dist/tools/definitions.d.ts +1089 -636
- package/dist/tools/definitions.js +57 -27
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/description-localization.js +3 -2
- package/dist/tools/description-localization.js.map +1 -1
- package/dist/tools/follow-up-request.d.ts +19 -0
- package/dist/tools/follow-up-request.js +81 -0
- package/dist/tools/follow-up-request.js.map +1 -0
- package/dist/tools/followup-revision.d.ts +4 -0
- package/dist/tools/followup-revision.js +29 -0
- package/dist/tools/followup-revision.js.map +1 -0
- package/dist/tools/incomplete-list.js +4 -0
- package/dist/tools/incomplete-list.js.map +1 -1
- package/dist/tools/inventory.js +47 -11
- package/dist/tools/inventory.js.map +1 -1
- package/dist/tools/operation-recovery.d.ts +4 -0
- package/dist/tools/operation-recovery.js +33 -0
- package/dist/tools/operation-recovery.js.map +1 -0
- package/dist/tools/registry-mapping-primary.cases.d.ts +2 -0
- package/dist/tools/registry-mapping-primary.cases.js +753 -0
- package/dist/tools/registry-mapping-primary.cases.js.map +1 -0
- package/dist/tools/registry-mapping-secondary.cases.d.ts +2 -0
- package/dist/tools/registry-mapping-secondary.cases.js +626 -0
- package/dist/tools/registry-mapping-secondary.cases.js.map +1 -0
- package/dist/tools/registry-mapping.cases.d.ts +8 -0
- package/dist/tools/registry-mapping.cases.js +13 -0
- package/dist/tools/registry-mapping.cases.js.map +1 -0
- package/dist/tools/registry-test-fixtures.d.ts +50 -0
- package/dist/tools/registry-test-fixtures.js +120 -0
- package/dist/tools/registry-test-fixtures.js.map +1 -0
- package/dist/tools/registry.d.ts +2 -0
- package/dist/tools/registry.js +82 -52
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/result.d.ts +1 -0
- package/dist/tools/result.js +90 -22
- package/dist/tools/result.js.map +1 -1
- package/dist/tools/toolsets.d.ts +16 -0
- package/dist/tools/toolsets.js +25 -1
- package/dist/tools/toolsets.js.map +1 -1
- package/dist/types/api-contract.gen.d.ts +105875 -22205
- package/dist/utils/credential-signatures.d.ts +3 -0
- package/dist/utils/credential-signatures.js +10 -0
- package/dist/utils/credential-signatures.js.map +1 -0
- package/dist/utils/data-source-column-name.d.ts +10 -0
- package/dist/utils/data-source-column-name.js +36 -0
- package/dist/utils/data-source-column-name.js.map +1 -0
- package/dist/utils/logger.d.ts +1 -0
- package/dist/utils/logger.js +32 -8
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/secret-read-boundary.d.ts +2 -0
- package/dist/utils/secret-read-boundary.js +88 -0
- package/dist/utils/secret-read-boundary.js.map +1 -0
- package/dist/utils/setup-secrets.d.ts +4 -0
- package/dist/utils/setup-secrets.js +40 -0
- package/dist/utils/setup-secrets.js.map +1 -0
- package/docs/agent-models.md +9 -0
- package/docs/agent-usage-guide.full.ru.md +1 -4
- package/docs/analytics-contract.md +67 -0
- package/docs/async-operation-lifecycle.md +10 -3
- package/docs/connect.ru.md +144 -4
- package/docs/contact-company-profile.md +17 -0
- package/docs/contact-recommendations.md +7 -0
- package/docs/crm-stage-pool.md +73 -0
- package/docs/customer-data.md +61 -0
- package/docs/edit-versions.md +36 -0
- package/docs/failure-semantics.md +17 -9
- package/docs/help-navigation.md +15 -42
- package/docs/host-compatibility-matrix.md +72 -93
- package/docs/host-setup.md +33 -35
- package/docs/invariants/public-api-and-contracts.md +16 -0
- package/docs/legacy-action-settings.md +21 -0
- package/docs/main-transfer-dependencies.md +38 -0
- package/docs/mcp2-parity.md +50 -0
- package/docs/modules/analytics.md +79 -0
- package/docs/modules/besales-mcp.md +137 -0
- package/docs/modules/dialog-review-report.md +38 -0
- package/docs/modules/dialogue-followup-adapters.md +39 -0
- package/docs/modules/message-reactions.md +17 -0
- package/docs/modules/network-mode.md +284 -0
- package/docs/modules/prompt-batch-and-dialogue-controls.md +69 -0
- package/docs/pii-policy.md +21 -16
- package/docs/response-size-budget.md +197 -7
- package/docs/secret-read-boundary.md +102 -0
- package/docs/task-state.md +7 -2
- package/docs/telegram-pool-policy.md +19 -0
- package/docs/tool-effects.md +15 -9
- package/docs/tool-surface.md +205 -4
- package/docs/voice-actions-and-inbox.md +95 -0
- package/docs/wazzup-instagram-comments.md +26 -0
- package/package.json +17 -10
- package/scripts/api-source-loader.spec.mjs +40 -0
- package/scripts/audit-bridge-controls.mjs +129 -0
- package/scripts/audit-revert-mutants.mjs +107 -0
- package/scripts/bridge-api-input.spec.mjs +98 -0
- package/scripts/bridge-controls-mutants.mjs +130 -0
- package/scripts/brx3-shipped-refusals.spec.mjs +149 -0
- package/scripts/brx4-ledger-payload.spec.mjs +127 -0
- package/scripts/build-onprem.mjs +108 -0
- package/scripts/connect-clients.sh +141 -0
- package/scripts/connect-scopes.mjs +166 -0
- package/scripts/connect-scopes.spec.mjs +83 -0
- package/scripts/emit-personal-link-carrier.mjs +31 -0
- package/scripts/emit-setup-init-schemas.mjs +24 -0
- package/scripts/emit-tool-routes.mjs +878 -0
- package/scripts/emit-tool-routes.spec.mjs +300 -0
- package/scripts/followup-registry.spec.mjs +107 -0
- package/scripts/generate-crm-rotation-contract.mjs +98 -0
- package/scripts/generate-followup-session-contract.mjs +16 -0
- package/scripts/icp-import-verification/mutate.cjs +70 -0
- package/scripts/import-secret-read-contract.mjs +53 -0
- package/scripts/install-claude-desktop.js +1 -1
- package/scripts/lag-decisions.mjs +37 -0
- package/scripts/lag-decisions.spec.mjs +87 -0
- package/scripts/lag-open-task-owners.mjs +46 -0
- package/scripts/lag-open-task-owners.spec.mjs +78 -0
- package/scripts/lag-payload.mjs +10 -0
- package/scripts/legacy-action-lag.spec.mjs +22 -0
- package/scripts/legacy-field-refusal.shipped.spec.mjs +99 -0
- package/scripts/mcpcompat-handler.mjs +34 -0
- package/scripts/mcpcompat-loader.mjs +36 -0
- package/scripts/mcpcompat-typescript.mjs +3 -0
- package/scripts/platform-settings-shipped-parity.spec.mjs +89 -0
- package/scripts/prbr-round1-recheck.spec.mjs +605 -0
- package/scripts/prbr-shipped-compatibility.spec.mjs +276 -0
- package/scripts/publish-mcp-registry.mjs +98 -0
- package/scripts/publish-mcp-registry.spec.mjs +68 -0
- package/scripts/record-scheduled-task-call.mjs +26 -0
- package/scripts/refusal-probe.mjs +17 -0
- package/scripts/refusal-probe.spec.mjs +29 -0
- package/scripts/registry-diff.mjs +502 -0
- package/scripts/registry-diff.spec.mjs +787 -0
- package/scripts/request-query.mjs +43 -0
- package/scripts/request-query.spec.mjs +82 -0
- package/scripts/route-lag.mjs +23 -0
- package/scripts/route-lag.spec.mjs +30 -0
- package/scripts/sample-args.mjs +174 -0
- package/scripts/sample-args.spec.mjs +160 -0
- package/scripts/shipped-definitions.mjs +197 -0
- package/scripts/shipped-definitions.spec.mjs +57 -0
- package/scripts/shipped-inputs.spec.mjs +136 -0
- package/scripts/shipped-recordings.spec.mjs +19 -0
- package/scripts/shipped-ref.d.mts +5 -0
- package/scripts/shipped-ref.mjs +20 -0
- package/scripts/shipped-ref.spec.mjs +59 -0
- package/scripts/shipped-registry.mjs +107 -0
- package/scripts/shipped-sample-candidates.mjs +69 -0
- package/scripts/shipped-sample-candidates.spec.mjs +117 -0
- package/scripts/shipped-sample.mjs +868 -0
- package/scripts/shipped-sample.spec.mjs +451 -0
- package/scripts/sync-agent-model-schemas.mjs +72 -0
- package/scripts/sync-api-contracts.mjs +85 -0
- package/scripts/sync-api-contracts.spec.mjs +144 -0
- package/scripts/sync-contract-ledger-schemas.mjs +161 -0
- package/scripts/sync-crm-stage-pool-schema.mjs +85 -0
- package/scripts/sync-prompt-draft-schema.mjs +92 -0
- package/scripts/sync-route-map.mjs +94 -0
- package/scripts/sync-schema-descriptions.mjs +25 -0
- package/scripts/sync-tool-inventory.mjs +27 -0
- package/scripts/sync-trigger-argument-schema.mjs +121 -0
- package/scripts/sync-trigger-repeat-mapping.mjs +12 -0
- package/scripts/sync-voice-actions-contract.mjs +67 -0
- package/scripts/sync-voice-actions-tools.mjs +97 -0
- package/scripts/sync-wave-id-schemas.mjs +55 -0
- package/scripts/tool-input-schemas.mjs +46 -0
- package/scripts/tool-input-schemas.spec.mjs +50 -0
- package/scripts/verify-followup-adapters.mjs +235 -0
- package/scripts/verify-secret-read-import.mjs +84 -0
- package/scripts/verify-secret-read-mutants.mjs +143 -0
- package/dist/tools/definitions/analytics.d.ts +0 -40
- package/dist/tools/definitions/analytics.js +0 -233
- package/dist/tools/definitions/analytics.js.map +0 -1
- package/dist/tools/definitions/broadcast-mailing-variants.d.ts +0 -41
- package/dist/tools/definitions/broadcast-mailing-variants.js +0 -194
- package/dist/tools/definitions/broadcast-mailing-variants.js.map +0 -1
- package/dist/tools/definitions/dialogue-audit.d.ts +0 -38
- package/dist/tools/definitions/dialogue-audit.js +0 -292
- package/dist/tools/definitions/dialogue-audit.js.map +0 -1
- package/dist/tools/definitions/getcourse-wave.js +0 -519
- package/dist/tools/definitions/getcourse-wave.js.map +0 -1
- package/dist/tools/definitions/operational-overview.d.ts +0 -3
- package/dist/tools/definitions/operational-overview.js +0 -23
- package/dist/tools/definitions/operational-overview.js.map +0 -1
- package/dist/tools/definitions/scheduled-tasks.js +0 -173
- package/dist/tools/definitions/scheduled-tasks.js.map +0 -1
- package/dist/tools/definitions/trigger-contact-claims.d.ts +0 -7
- package/dist/tools/definitions/trigger-contact-claims.js +0 -60
- package/dist/tools/definitions/trigger-contact-claims.js.map +0 -1
- package/dist/tools/registry-mapping-cases-core.d.ts +0 -38
- package/dist/tools/registry-mapping-cases-core.js +0 -963
- package/dist/tools/registry-mapping-cases-core.js.map +0 -1
- package/dist/tools/registry-mapping-cases-platform.d.ts +0 -2
- package/dist/tools/registry-mapping-cases-platform.js +0 -1052
- package/dist/tools/registry-mapping-cases-platform.js.map +0 -1
- package/dist/tools/trigger-action-config.d.ts +0 -49
- package/dist/tools/trigger-action-config.js +0 -171
- package/dist/tools/trigger-action-config.js.map +0 -1
- package/docs/evals/model-behavior-scorecard.md +0 -80
- package/docs/operation-coverage.md +0 -114
- package/scripts/mock-credentials.js +0 -77
- package/scripts/model-eval-server.js +0 -213
- package/scripts/run-model-eval.js +0 -447
|
@@ -1,11 +1,18 @@
|
|
|
1
|
+
import { SCHEDULE_GUARD_HINT, SCHEDULE_BLOCKS_HINT } from './v15-port-descriptions.js';
|
|
2
|
+
// Планировщик рассылок: расписания (список, сохранение, удаление, история прогонов) и правило отбора
|
|
3
|
+
// сегмента. Схемы инструментов — ровно отгруженные: клиент, зовущий инструмент сегодня, не получает
|
|
4
|
+
// отказа ни на одном поле, которое принимал. Тело запроса — в именах сервера v2 (канал, а не
|
|
5
|
+
// «площадка»). «Запустить сейчас» и настройка источника фактов мосту не выставлены намеренно, как и у
|
|
6
|
+
// отгруженного: необратимая массовая отправка и адрес источника остаются за человеком.
|
|
1
7
|
import { z } from 'zod';
|
|
8
|
+
import { SCHEDULE_EXCLUSION_HINT, SCHEDULE_POOL_GROUPS_HINT, SCHEDULE_CLOSE_CLASS_HINT } from './schedule-selection-hints.js';
|
|
2
9
|
import { buildListQuery, compactObject, defineTool, getcourseTransportEnum, uuidSchema, withInstructions, } from './shared.js';
|
|
3
|
-
/** Границы — паритет с DTO бэкенда: расхождение даёт 400 уже после подтверждения
|
|
10
|
+
/** Границы расписания — паритет с DTO бэкенда: расхождение даёт 400 уже после подтверждения человеком. */
|
|
4
11
|
const DAILY_CAP_MAX = 5000;
|
|
5
12
|
/**
|
|
6
|
-
* ⚠️⚠️ 78, а не 365.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
13
|
+
* ⚠️⚠️ 78, а не 365. Сервер выводит потолок из потолка окна источника фактов (92 дня) минус запас
|
|
14
|
+
* окна регистраций (14 дней): значение 79…365 проходило бы схему и падало 400 уже ПОСЛЕ того, как
|
|
15
|
+
* человек подтвердил расписание.
|
|
9
16
|
*/
|
|
10
17
|
const FRESHNESS_DAYS_MAX = 78;
|
|
11
18
|
const COOLDOWN_DAYS_MAX = 365;
|
|
@@ -13,21 +20,22 @@ const COOLDOWN_DAYS_MAX = 365;
|
|
|
13
20
|
const RESEND_PAUSE_DAYS_MAX = 365;
|
|
14
21
|
/** Потолок списка «не писать после сегментов» — серверный `SCHEDULE_SKIP_AFTER_SEGMENTS_MAX`. */
|
|
15
22
|
const SKIP_AFTER_SEGMENTS_MAX = 20;
|
|
23
|
+
/** Потолок списка каналов — отгруженный. */
|
|
24
|
+
const CHANNELS_MAX = 20;
|
|
16
25
|
/**
|
|
17
26
|
* Окно свежести строк без расписания в «Пуле сегодня» — паритет с `POOL_TODAY_WINDOW_DAYS_MAX`
|
|
18
27
|
* бэкенда: окно регистраций строки (окно + 14 суток запаса) обязано уложиться в общую выкачку
|
|
19
28
|
* (30 суток). Больше сервер отвергнет 400.
|
|
20
29
|
*/
|
|
21
30
|
const POOL_TODAY_WINDOW_DAYS_MAX = 16;
|
|
22
|
-
|
|
23
|
-
/** Локальная дата площадки — та же форма, что у бэкенда. */
|
|
31
|
+
/** Локальная дата канала — та же форма, что у бэкенда. */
|
|
24
32
|
const LOCAL_DATE_RE = /^\d{4}-\d{2}-\d{2}$/u;
|
|
25
33
|
/**
|
|
26
34
|
* Списки фильтров — ОДНА форма у расписания и у «Пула сегодня»: `'all'` либо список кодов.
|
|
27
35
|
*
|
|
28
|
-
* ⚠️ Границы повторяют DTO бэкенда (`
|
|
29
|
-
* `
|
|
30
|
-
*
|
|
36
|
+
* ⚠️ Границы повторяют DTO бэкенда (`SCHEDULE_FUNNELS_MAX`, `SCHEDULE_PRODUCT_CODES_MAX`,
|
|
37
|
+
* `SCHEDULE_FILTER_VALUE_MAX_LENGTH`): разойдясь, схема пропускала бы то, что сервер отвергает 400-м,
|
|
38
|
+
* и модель узнавала бы о границе из ошибки.
|
|
31
39
|
*/
|
|
32
40
|
const FILTER_VALUE_MAX_LENGTH = 64;
|
|
33
41
|
const FUNNELS_MAX = 100;
|
|
@@ -37,29 +45,42 @@ const filterListSchema = (max) => z.union([
|
|
|
37
45
|
z.array(z.string().trim().min(1).max(FILTER_VALUE_MAX_LENGTH)).max(max),
|
|
38
46
|
]);
|
|
39
47
|
const orderSchema = z.enum(['FRESH_FIRST', 'OLD_FIRST']);
|
|
48
|
+
/** Потолок ожидания после эфира — серверный `SEGMENT_WAIT_AFTER_SERIES_END_HOURS_MAX`. */
|
|
49
|
+
const WAIT_HOURS_MAX = 720;
|
|
50
|
+
/** Границы приоритета — серверные `SEGMENT_RULE_PRIORITY_MIN/MAX`. */
|
|
51
|
+
const PRIORITY_MIN = -1000;
|
|
52
|
+
const PRIORITY_MAX = 1000;
|
|
53
|
+
/**
|
|
54
|
+
* Суток с прошлого касания: снизу — граница владельца (повтор снимает единственную вечную защиту
|
|
55
|
+
* от второго сообщения), сверху — окно свежести расписания. Паритет серверных констант: ниже 30
|
|
56
|
+
* сервер откажет уже после подтверждения человеком.
|
|
57
|
+
*/
|
|
58
|
+
const REPEAT_TOUCH_MIN_DAYS = 30;
|
|
59
|
+
const REPEAT_TOUCH_MAX_DAYS = 78;
|
|
60
|
+
/** Потолок длины имени прошлого касания — отгруженный; сервер принимает и длиннее. */
|
|
61
|
+
const PREVIOUS_SEGMENT_KEY_MAX_LENGTH = 64;
|
|
62
|
+
const triStateSchema = z.enum(['YES', 'NO', 'ANY']);
|
|
63
|
+
/**
|
|
64
|
+
* Откуда берётся момент входа человека в сегмент.
|
|
65
|
+
*
|
|
66
|
+
* ⚠️ Схема СТРОГАЯ: значение, объявленное на сервере и забытое здесь, отвергается ещё до запроса.
|
|
67
|
+
*/
|
|
40
68
|
const anchorSchema = z.enum([
|
|
41
69
|
'SERIES_END',
|
|
42
70
|
'SERIES_END_PLUS_WAIT',
|
|
43
71
|
// Конец ПЕРВОГО эфира серии плюс ожидание — для сегментов, которые пишут пропустившему первый
|
|
44
|
-
// эфир,
|
|
45
|
-
// на расписании (guard_active_series), иначе отправку остановит правило площадки.
|
|
72
|
+
// эфир, пока серия идёт. Требует признака attendedFirstSession.
|
|
46
73
|
'FIRST_SESSION_END_PLUS_WAIT',
|
|
47
74
|
'LAST_APPLICATION',
|
|
48
75
|
'REGISTRATION',
|
|
49
|
-
// ⚠️ Схема СТРОГАЯ: значение, объявленное на сервере и забытое здесь, отвергается ещё до
|
|
50
|
-
// запроса — фича остаётся недостижимой через MCP, и выглядит это как ошибка ввода агента.
|
|
51
76
|
'PRELIST',
|
|
52
77
|
]);
|
|
53
|
-
/**
|
|
54
|
-
* Тело расписания. `funnels`/`product_codes` принимают литерал `all` — обычный пользователь
|
|
55
|
-
* говорит «шли по всем воронкам», и заставлять его перечислять их руками незачем.
|
|
56
|
-
*/
|
|
57
78
|
/**
|
|
58
79
|
* Пара «дни недели + числа месяца» запрещена.
|
|
59
80
|
*
|
|
60
|
-
* ⚠️ Правило продублировано в схеме намеренно:
|
|
61
|
-
* узнавала бы об этом из 400-й — а описание инструмента для того и существует, чтобы правило
|
|
62
|
-
* известно ДО вызова.
|
|
81
|
+
* ⚠️ Правило продублировано в схеме намеренно: сервер и проверка таблицы её тоже не пускают, но
|
|
82
|
+
* модель узнавала бы об этом из 400-й — а описание инструмента для того и существует, чтобы правило
|
|
83
|
+
* было известно ДО вызова.
|
|
63
84
|
*/
|
|
64
85
|
const assertSingleRepeatAxis = (input, ctx) => {
|
|
65
86
|
if ((input.days_of_week?.length ?? 0) > 0 && (input.days_of_month?.length ?? 0) > 0) {
|
|
@@ -71,6 +92,11 @@ const assertSingleRepeatAxis = (input, ctx) => {
|
|
|
71
92
|
});
|
|
72
93
|
}
|
|
73
94
|
};
|
|
95
|
+
/**
|
|
96
|
+
* Тело расписания. `funnels`/`product_codes` принимают литерал `all` — человек говорит «шли по всем
|
|
97
|
+
* воронкам», и заставлять его перечислять их руками незачем. Имена полей входа — отгруженные
|
|
98
|
+
* (`platform_id`, `handoff_from_school_platforms`): клиент зовёт инструмент ими уже сегодня.
|
|
99
|
+
*/
|
|
74
100
|
const scheduleShape = {
|
|
75
101
|
workspace_id: uuidSchema.optional(),
|
|
76
102
|
segment_id: uuidSchema,
|
|
@@ -81,46 +107,56 @@ const scheduleShape = {
|
|
|
81
107
|
product_codes: filterListSchema(PRODUCTS_MAX).optional(),
|
|
82
108
|
freshness_days: z.number().int().min(1).max(FRESHNESS_DAYS_MAX),
|
|
83
109
|
window_mode: z.enum(['ROLLING', 'FIXED']).optional(),
|
|
84
|
-
// ⚠️ ЛОКАЛЬНАЯ дата
|
|
110
|
+
// ⚠️ ЛОКАЛЬНАЯ дата канала, а не момент: момент сдвинул бы границу на смещение пояса.
|
|
85
111
|
window_from_local: z.string().regex(LOCAL_DATE_RE, 'window_from_local: дата ГГГГ-ММ-ДД').optional(),
|
|
86
112
|
// Конец ВКЛЮЧИТЕЛЬНО: «по 20 августа» берёт весь двадцатый день.
|
|
87
113
|
window_to_local: z.string().regex(LOCAL_DATE_RE, 'window_to_local: дата ГГГГ-ММ-ДД').optional(),
|
|
88
114
|
time_local: z
|
|
89
115
|
.string()
|
|
90
|
-
.regex(/^([01]\d|2[0-3]):([0-5]\d)$/, 'time_local: формат HH:mm в
|
|
116
|
+
.regex(/^([01]\d|2[0-3]):([0-5]\d)$/, 'time_local: формат HH:mm в часовом поясе канала'),
|
|
91
117
|
days_of_week: z.array(z.number().int().min(1).max(7)).max(7).optional(),
|
|
92
|
-
// ⚠️ Взаимоисключимо с `days_of_week
|
|
93
|
-
//
|
|
94
|
-
// запрет продублирован схемой (см. `superRefine` ниже). Число больше длины месяца попадает на
|
|
95
|
-
// ПОСЛЕДНИЙ его день.
|
|
118
|
+
// ⚠️ Взаимоисключимо с `days_of_week` (см. `assertSingleRepeatAxis`). Число больше длины месяца
|
|
119
|
+
// попадает на ПОСЛЕДНИЙ его день.
|
|
96
120
|
days_of_month: z.array(z.number().int().min(1).max(31)).max(31).optional(),
|
|
97
121
|
daily_cap: z.number().int().min(1).max(DAILY_CAP_MAX),
|
|
98
122
|
selection_order: orderSchema,
|
|
99
123
|
cooldown_days: z.number().int().min(0).max(COOLDOWN_DAYS_MAX),
|
|
100
|
-
handoff_from_school_platforms: z
|
|
101
|
-
|
|
102
|
-
|
|
124
|
+
handoff_from_school_platforms: z
|
|
125
|
+
.boolean()
|
|
126
|
+
.optional()
|
|
127
|
+
.describe('ПЕРЕДАЧА: забирать людей с ДРУГИХ каналов GetCourse той же школы В ДОПОЛНЕНИЕ к своим. Требует сегмента-ПРОДОЛЖЕНИЯ (снят «только нетронутым», без правила повтора), заполненного домена школы у канала расписания и хотя бы одного другого канала GetCourse в школе. Не передано — у существующего остаётся как было.'),
|
|
128
|
+
resend_segment_on_new_registration: z
|
|
129
|
+
.boolean()
|
|
130
|
+
.optional()
|
|
131
|
+
.describe('ПОВТОР СЕГМЕНТА ПО НОВОЙ РЕГИСТРАЦИИ. Включено — человеку, которому этим сегментом уже писали, расписание напишет снова, только если он зарегистрировался ПОСЛЕ нашей последней отправки этого сегмента и с неё прошла пауза resend_segment_pause_days. Действует у продолжения без previousSegment; продукт регистрации повтор не ограничивает. У «только нетронутым» сохраняется право первого сообщения. Не передано — у нового расписания включено, у существующего остаётся как было.'),
|
|
132
|
+
resend_segment_pause_days: z
|
|
133
|
+
.number()
|
|
134
|
+
.int()
|
|
135
|
+
.min(0)
|
|
136
|
+
.max(RESEND_PAUSE_DAYS_MAX)
|
|
137
|
+
.optional()
|
|
138
|
+
.describe('Пауза повтора сегмента: сутки по 24 часа от нашей последней отправки этого сегмента человеку. Не передано — у нового расписания 3, у существующего остаётся как было.'),
|
|
103
139
|
channels: z
|
|
104
|
-
// ⚠️ Канон транспортов, а не свободная строка: иначе про допустимые значения модель узнаёт
|
|
105
|
-
//
|
|
140
|
+
// ⚠️ Канон транспортов, а не свободная строка: иначе про допустимые значения модель узнаёт из
|
|
141
|
+
// 400-й ошибки.
|
|
106
142
|
.array(getcourseTransportEnum)
|
|
107
|
-
.max(
|
|
143
|
+
.max(CHANNELS_MAX)
|
|
108
144
|
.optional()
|
|
109
|
-
.describe('Каналы «Первого сообщения» (GetCourseTransport)
|
|
145
|
+
.describe('Каналы «Первого сообщения» (GetCourseTransport), первый — канал денег. [] — каналы рассылки; не передано — у существующего остаётся как было. Канал вне способов отправки канала GetCourse отвергается: школа не сужает набор, а отвергает его ЦЕЛИКОМ.'),
|
|
110
146
|
channels_mode: z
|
|
111
147
|
.enum(['RESTRICT', 'ADD'])
|
|
112
148
|
.optional()
|
|
113
|
-
.describe(
|
|
149
|
+
.describe('⚠️ УСТАРЕЛ и на поведение НЕ влияет. Список channels значит «слать сюда», судьбу КАЖДОГО канала решает сервер. Поле принимается ради обратной совместимости и игнорируется.'),
|
|
114
150
|
guard_active_series: z
|
|
115
151
|
.enum(['OFF', 'WARN', 'BLOCK'])
|
|
116
152
|
.nullable()
|
|
117
153
|
.optional()
|
|
118
|
-
.describe('Перекрытие предохранителя «идёт вебинарная серия» ДЛЯ ЭТОГО расписания. Не передано — у существующего остаётся как было; null — вернуть под настройку
|
|
154
|
+
.describe('Перекрытие предохранителя «идёт вебинарная серия» ДЛЯ ЭТОГО расписания. Не передано — у существующего остаётся как было; null — вернуть под настройку канала. Перекрывается только правило серии: заявка и оплата остаются настройкой канала.'),
|
|
119
155
|
skip_after_segment_ids: z
|
|
120
156
|
.array(uuidSchema)
|
|
121
157
|
.max(SKIP_AFTER_SEGMENTS_MAX)
|
|
122
158
|
.optional()
|
|
123
|
-
.describe('НЕ ПИСАТЬ ПОСЛЕ
|
|
159
|
+
.describe('НЕ ПИСАТЬ ПОСЛЕ СЕГМЕНТОВ: идентификаторы сегментов справочника (segment.id из besales_broadcast_schedule_list). Не передано — у существующего остаётся как было; [] — снять правило. Запрет действует, если текст указанного сегмента ушёл не раньше регистрации, по которой человек попал в пул; регистрация новее касания открывает человека снова, неизвестная регистрация запрет не снимает. Сам сегмент расписания, чужие идентификаторы и сегменты со служебным именем сервер отвергает.'),
|
|
124
160
|
is_enabled: z.boolean().optional(),
|
|
125
161
|
};
|
|
126
162
|
/** `all` — это «фильтра нет», то есть ПУСТОЙ список на стороне API. */
|
|
@@ -129,10 +165,18 @@ const toList = (value) => {
|
|
|
129
165
|
return undefined;
|
|
130
166
|
return value === 'all' ? [] : value;
|
|
131
167
|
};
|
|
168
|
+
/**
|
|
169
|
+
* Вход инструмента — в тело сервера v2.
|
|
170
|
+
*
|
|
171
|
+
* ⚠️ `compactObject` режет только `undefined`: поле, которого нет во входе, не уходит вовсе и у
|
|
172
|
+
* существующего расписания остаётся как было, а `null` у `guard_active_series` доезжает и снимает
|
|
173
|
+
* перекрытие — «не передал» обязано отличаться от «верни под канал».
|
|
174
|
+
*/
|
|
132
175
|
const toScheduleBody = (input) => compactObject({
|
|
133
176
|
segmentId: input.segment_id,
|
|
134
177
|
mailingVariantId: input.mailing_variant_id,
|
|
135
|
-
|
|
178
|
+
// Имя v2 — канал; отгруженное имя поля входа сохранено ради клиента.
|
|
179
|
+
channelId: input.platform_id,
|
|
136
180
|
mechanism: input.mechanism,
|
|
137
181
|
funnels: toList(input.funnels),
|
|
138
182
|
productCodes: toList(input.product_codes),
|
|
@@ -146,26 +190,20 @@ const toScheduleBody = (input) => compactObject({
|
|
|
146
190
|
dailyCap: input.daily_cap,
|
|
147
191
|
selectionOrder: input.selection_order,
|
|
148
192
|
cooldownDays: input.cooldown_days,
|
|
149
|
-
|
|
193
|
+
handoffFromSchoolChannels: input.handoff_from_school_platforms,
|
|
150
194
|
resendSegmentOnNewRegistration: input.resend_segment_on_new_registration,
|
|
151
195
|
resendSegmentPauseDays: input.resend_segment_pause_days,
|
|
152
|
-
|
|
153
|
-
// ⚠️ Режим передаётся ТОЛЬКО если его задал вызывающий:
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
channelsMode: input.channels_mode,
|
|
157
|
-
// ⚠️ `null` проходит намеренно (`compactObject` режет только `undefined`): им снимают
|
|
158
|
-
// перекрытие, и «не передал» обязано отличаться от «верни под площадку».
|
|
196
|
+
transports: input.channels,
|
|
197
|
+
// ⚠️ Режим передаётся ТОЛЬКО если его задал вызывающий: сервер его принимает и игнорирует, а
|
|
198
|
+
// подставленный мостом он выглядел бы решением, которого никто не принимал.
|
|
199
|
+
transportsMode: input.channels_mode,
|
|
159
200
|
guardActiveSeries: input.guard_active_series,
|
|
160
201
|
skipAfterSegmentIds: input.skip_after_segment_ids,
|
|
161
202
|
isEnabled: input.is_enabled,
|
|
162
203
|
});
|
|
163
204
|
/**
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
* ⚠️⚠️ Расписание отправляет ЖИВЫМ ЛЮДЯМ без участия человека в момент отправки. Поэтому
|
|
167
|
-
* создание всегда даёт выключенное расписание, а включение — отдельное действие после
|
|
168
|
-
* предпросмотра.
|
|
205
|
+
* ⚠️⚠️ Расписание отправляет ЖИВЫМ ЛЮДЯМ без участия человека в момент отправки. Поэтому создание
|
|
206
|
+
* по умолчанию даёт выключенное расписание, а включение — отдельное действие после предпросмотра.
|
|
169
207
|
*/
|
|
170
208
|
export const broadcastSchedulerTools = [
|
|
171
209
|
defineTool({
|
|
@@ -179,8 +217,9 @@ export const broadcastSchedulerTools = [
|
|
|
179
217
|
path: `/workspaces/${input.workspace_id}/broadcast-schedules`,
|
|
180
218
|
}),
|
|
181
219
|
enhanceResponse: (response) => withInstructions(response, [
|
|
182
|
-
'isEnabled=false означает, что расписание заведено, но
|
|
220
|
+
'isEnabled=false означает, что расписание заведено, но проход планировщика его не берёт. Это нормальное состояние сразу после создания.',
|
|
183
221
|
'Смотри на segment.priority: при пересечении сегментов побеждает больший — это и решает, какой текст получит человек, попавший под два правила.',
|
|
222
|
+
'Расписаний, убранных в архив (удалённых при существующей истории прогонов), в списке нет; их история читается besales_broadcast_schedule_runs по идентификатору.',
|
|
184
223
|
]),
|
|
185
224
|
}),
|
|
186
225
|
defineTool({
|
|
@@ -199,41 +238,31 @@ export const broadcastSchedulerTools = [
|
|
|
199
238
|
],
|
|
200
239
|
defaultWorkspaceId: true,
|
|
201
240
|
schema: z.object(scheduleShape).strict().superRefine(assertSingleRepeatAxis),
|
|
241
|
+
// Предпросмотр нового расписания: журнала подач у него нет. Предпросмотр правки существующего с его
|
|
242
|
+
// журналом — отдельный маршрут сервера; отгруженный инструмент его не зовёт.
|
|
202
243
|
createRequest: (input) => ({
|
|
203
244
|
method: 'POST',
|
|
204
245
|
path: `/workspaces/${input.workspace_id}/broadcast-schedules/preview`,
|
|
205
246
|
body: toScheduleBody(input),
|
|
206
247
|
}),
|
|
207
248
|
enhanceResponse: (response) => withInstructions(response, [
|
|
208
|
-
'Ничего не отправляет и не пишет: считает ТЕМ ЖЕ
|
|
209
|
-
|
|
210
|
-
'
|
|
211
|
-
'⚠️⚠️
|
|
212
|
-
'
|
|
213
|
-
'⚠️
|
|
214
|
-
'
|
|
215
|
-
'⚠️⚠️
|
|
216
|
-
'⚠️⚠️
|
|
217
|
-
'
|
|
218
|
-
'⚠️⚠️ noMessengerVerdict — школа УЖЕ отвечала, что привязанных мессенджеров у человека нет, и ответ ещё в силе. Карточки в базе у него нет, импорт её не заведёт, поэтому подача сожгла бы слот дневного потолка и слот импортов воркспейса и закрылась бы исходом NOT_IMPORTED на 7 суток. Срок доверия ответу задаётся у ПЛОЩАДКИ (getcourse_settings.reachability_ttl_days, по умолчанию 7 суток; 0 — спрашивать школу каждый раз). Срок считается СУТКАМИ площадки: ответ в силе, пока он новее полуночи дня запуска минус срок — поэтому «Пул сегодня» и любой запуск того же дня отвечают одинаково, как бы ни задержался отбор (с 17.09.2026); когда срок истечёт, человек снова уедет в импорт и школу спросят заново. Чинится почтой, но включать её надо В ДВУХ местах: в allowlist ПЛОЩАДКИ (getcourse_settings.available_transports) и в каналах рассылки/расписания. Гейт сужает каналы по allowlist площадки, поэтому Email только в рассылке ничего не меняет — и это верно: канал вне allowlist школа не отправит никогда.',
|
|
219
|
-
'⚠️⚠️ alreadyInDialog — с человеком идёт ЖИВОЙ разговор: он написал нам после нашей последней реплики (агента, оператора, дожима или процесса школы из импортированной истории). Сам такой разговор не закроется, пока ему не ответят. Это ЗЕРКАЛО отказа доставки SKIPPED_ALREADY_IN_DIALOG, поэтому место в дневном лимите он не занимает. С 16.09.2026 одинаково у сегмента «только нетронутым» и у продолжения. Расписанием не чинится.',
|
|
220
|
-
'⚠️⚠️ dialogQuiet — пауза тишины разговора, а не отсев навсегда: наша последняя реплика в чате моложе трёх суток (по 24 часа), человек после неё не писал. После трёх суток тишины разговор закрыт, и человек вернётся в пул сам — в том числе тот, кто когда-то нам отвечал (решение 16.09.2026). Доставка отказывает таким тем же SKIPPED_ALREADY_IN_DIALOG. «Пул сегодня» считает эту причину на момент запуска авторассылки.',
|
|
221
|
-
'⚠️⚠️ skipAfterSegment — человеку уже ушёл текст одного из сегментов списка skip_after_segment_ids расписания, и он не регистрировался после того касания (например, получил «пропустил первый эфир», и «не смогли прийти» по той же серии ему не уходит). Новая регистрация открывает его снова. У расписания без списка число всегда 0.',
|
|
222
|
-
'⚠️⚠️ resendPause — этим сегментом человеку уже писали, после нашей отправки он зарегистрировался снова, и расписание повторит ему сегмент (resend_segment_on_new_registration), но пауза resend_segment_pause_days ещё идёт. Вернётся в пул сам. При выключенном флаге такие люди стоят в alreadySentSegment.',
|
|
223
|
-
'resendOpened — НЕ отсев: сколько из selectedCount пустил повтор сегмента по новой регистрации (этим сегментом им уже писали, они зарегистрировались снова, и пауза прошла). Это мера самого правила: при выключенном флаге всегда 0.',
|
|
224
|
-
'⚠️⚠️ guardActiveSeries / guardPaid / guardActiveApplication / guardUnverified — ЗЕРКАЛО предохранителя школы (идёт вебинарная серия; guardPaid — купил продукт после регистрации, по которой человек попал в сегмент; guardActiveApplication — после этой регистрации есть действующая заявка на продукт, с человеком работает менеджер; заявки и оплаты до этой регистрации не держат; оплата и заявка сразу — guardPaid; прежний ключ guardActiveDeal приходит только от старого бэкенда; не смог проверить — нет в данных школы или продукт оплаты не определён). Отбор спрашивает те же правила площадки, что и отправка, поэтому остановленные не занимают дневной лимит, и его добирают следующие по очереди. ⚠️ Числа считаются только до набора дневного лимита: кто после — в overCap, их предохранитель не судил. Серия проходит сама: человек вернётся в отбор, как только она закончится. Режимы правил — в настройках площадки (getcourse_settings.guard_*).',
|
|
225
|
-
'⚠️⚠️ guardUnchecked — НЕ отсев: предохранитель не проверил столько отобранных (cause=SOURCE_UNAVAILABLE — источник фактов не ответил, error — текст отказа; OUT_OF_TIME — проверка не уложилась в время запроса). Они стоят в selectedCount без проверки серии, заявки и оплаты; перед отправкой их проверит сама отправка, и часть отсеется. Скажи пользователю, что число «уйдёт» в этот раз завышено. На крупной площадке предпросмотр почти всегда отвечает OUT_OF_TIME (выкачка кандидатов съедает почти весь предел ответа) — полная картина с проверкой в besales_broadcast_pool_today. После отказа источника проверка минуту не повторяется: повторный вызов сразу вернёт SOURCE_UNAVAILABLE.',
|
|
226
|
-
'⚠️⚠️ notHandoffCandidate — у расписания с передачей (включён handoff_from_school_platforms) человек живёт на ДРУГОЙ площадке той же школы, но переносить его нельзя: его серия там ещё идёт (последний шаг календарной цепочки источника впереди или у контакта нет даты эфира) либо у него карточки сразу на нескольких других площадках. Свои люди площадки и люди без карточек сюда НЕ попадают: с 15.09.2026 расписание берёт их как обычно. У обычного расписания число всегда ноль.',
|
|
227
|
-
'⚠️⚠️ handoffReturn — этого человека передача уже увозила С ЭТОЙ площадки, и в рамках текущего входа в сегмент он сюда не вернётся: иначе два расписания-продолжения возили бы его по кругу, а каждый переезд необратим. Новый вход спустя время запрету не подлежит. Ненулевое число означает, что у вас кольцо из двух расписаний передачи — это настройка, а не сбой данных.',
|
|
228
|
-
'⚠️⚠️ handoffBlocked — карточку не перенесли: предохранитель школы (идёт серия, действующая заявка, оплата), контактом занят человек (бан, оператор, мьют), личность занята другим процессом либо на площадке-адресате уже есть карточка с этим внешним id (последнее — навсегда). Такие люди в подачу не идут и получают исход HANDOFF_BLOCKED в журнале подач.',
|
|
229
|
-
'factsAsOf — момент, на который источник считает данные полными. Сильно устаревшее значение означает, что аналитика отстаёт и «не пришедшие» могут быть ложными.',
|
|
230
|
-
'⚠️⚠️ overlaps — контакты, которых за последнюю неделю подавали ДРУГИЕ расписания. Непустой список означает, что человек рискует получить несколько сообщений: разведи расписания кулдауном или приоритетом сегментов, прежде чем включать.',
|
|
231
|
-
'byProduct показывает, как отбор разложится по продуктам — крон нарежет подачу этими же блоками.',
|
|
249
|
+
'Ничего не отправляет и не пишет: считает ТЕМ ЖЕ отбором, что прогон, — тот же источник фактов, те же исключения, тот же предохранитель последним.',
|
|
250
|
+
SCHEDULE_EXCLUSION_HINT,
|
|
251
|
+
'⚠️ Отсев источника фактов — до пула: unverifiable — «нечем проверить» (нет назначения серии, серия идёт, нет длительности), а НЕ «не подходит»; missingIdentifiers — строки без номера ученика или заказа; lowCoverageSkipped — серии воронок с недостоверной явкой (воронки — в lowCoverageFunnels); truncatedSeriesSkipped — выборка упёрлась в потолок строк: чинится сужением окна, воронок или продуктов; productNotInList — продукт строки не из списка продуктов расписания.',
|
|
252
|
+
'⚠️⚠️ guardActiveSeries / guardPaid / guardActiveApplication / guardUnverified — зеркало предохранителя школы (идёт серия; купил после регистрации; живая заявка у менеджера; не смог проверить). Остановленные не занимают дневной потолок — его добирают следующие по очереди. guardUnchecked — НЕ отсев: столько отобранных предохранитель не проверил (cause=SOURCE_UNAVAILABLE — реестр не ответил; OUT_OF_TIME — проверка не уложилась во время запроса); их проверит сама отправка, и часть отсеется. На крупном канале предпросмотр почти всегда отвечает OUT_OF_TIME — полная картина с проверкой в besales_broadcast_pool_today.',
|
|
253
|
+
'factsUnknown — НЕ отсев: отобранные, про которых школу о мессенджерах ещё не спрашивали, — без этого числа ноль в notReachable читался бы как «все достижимы». needsImport — скольким прогон сначала заведёт карточку или адрес на канале: они уйдут не сразу. resendOpened — скольким повтор сегмента по новой регистрации открыт.',
|
|
254
|
+
'⚠️ allChannelsUnserved=true — ни один выбранный способ отправки канал GetCourse не обслуживает: недостижимы будут все, и чинится это настройкой канала, а не правилом сегмента.',
|
|
255
|
+
'factsAsOf — момент, на который источник считает данные полными; attendanceIngestAt=null — сигнала о приёме посещаемости нет. truncated=true — снимок упёрся в бюджет строк, показан не весь пул.',
|
|
256
|
+
'⚠️⚠️ overlaps — скольких из отобранных за последние 7 суток уже подавали ДРУГИЕ включённые расписания той же школы. Непустой список — риск нескольких сообщений одному человеку: разведи расписания приоритетом сегментов или тихим периодом, прежде чем включать.',
|
|
257
|
+
'⚠️⚠️ warnings — цена этой настройки, которую получит и сохранение: перескажи человеку каждое message своими словами ДО сохранения. SENIOR_FUNNELS_WIDER — у включённого расписания старшего сегмента того же канала список воронок шире: регистраций человека в его воронках этот отбор не видит, и приоритет пересечение не разведёт — человек может получить текст этого сегмента вместо старшего или оба. Предложи сделать списки funnels у этих расписаний одинаковыми.',
|
|
258
|
+
'byProduct показывает, как отбор разложится по продуктам: прогон нарежет подачу этими же блоками.',
|
|
232
259
|
]),
|
|
233
260
|
}),
|
|
234
261
|
defineTool({
|
|
235
262
|
name: 'besales_broadcast_schedule_upsert',
|
|
236
263
|
sideEffect: 'write',
|
|
264
|
+
// Без schedule_id вызов СОЗДАЁТ расписание: при неизвестном исходе повтор завёл бы второе.
|
|
265
|
+
verifyWithToolName: 'besales_broadcast_schedule_list',
|
|
237
266
|
requiredFields: [
|
|
238
267
|
'segment_id',
|
|
239
268
|
'mailing_variant_id',
|
|
@@ -262,16 +291,21 @@ export const broadcastSchedulerTools = [
|
|
|
262
291
|
body: toScheduleBody(input),
|
|
263
292
|
},
|
|
264
293
|
enhanceResponse: (response) => withInstructions(response, [
|
|
265
|
-
'
|
|
266
|
-
'
|
|
294
|
+
'Почему люди отсеялись — зови besales_broadcast_schedule_preview: в его подсказке объяснена каждая причина.',
|
|
295
|
+
'⚠️ Создавай расписание ПОСЛЕ предпросмотра и без is_enabled=true: включённое расписание начнёт слать живым людям на ближайшем проходе планировщика (каждые 5 минут).',
|
|
296
|
+
'time_local — в часовом поясе КАНАЛА, не в UTC и не в твоём. Сутки расписания тоже считаются по нему.',
|
|
267
297
|
'daily_cap — потолок ОТОБРАННЫХ за прогон, а не доставленных: часть отсеется гейтами отправки уже внутри пакета. Историческую разницу смотри в besales_broadcast_schedule_runs.',
|
|
268
298
|
'Пустые funnels/product_codes (или "all") означают «без фильтра», а не «ничего»: это правило пустого списка во всей платформе.',
|
|
299
|
+
'Поле, которого нет во входе, у существующего расписания остаётся как было; пустой список, false и null у guard_active_series — это значение, а не «не менять».',
|
|
300
|
+
'⚠️ Отказ сохранения — 400 с полем field (какое поле поправить) и объяснением в message: перескажи объяснение человеку своими словами и не повторяй вызов без правки названного поля.',
|
|
301
|
+
'⚠️⚠️ warnings — расписание СОХРАНЕНО, но у настройки есть цена: перескажи человеку каждое message своими словами. SENIOR_FUNNELS_WIDER — у включённого расписания старшего сегмента того же канала список воронок шире: приоритет не разведёт пересечение, человек может получить текст этого сегмента вместо старшего или оба. Предложи выровнять funnels у этих расписаний; повторять сохранение ради предупреждения не нужно.',
|
|
269
302
|
'Механизм пока только cold_welcome: у волны свой контракт запуска, и планировщик не притворяется, что умеет её ставить в расписание.',
|
|
270
303
|
]),
|
|
271
304
|
}),
|
|
272
305
|
defineTool({
|
|
273
306
|
name: 'besales_broadcast_schedule_delete',
|
|
274
307
|
sideEffect: 'write',
|
|
308
|
+
verifyWithToolName: 'besales_broadcast_schedule_list',
|
|
275
309
|
requiredFields: ['schedule_id', 'explicit_confirmation'],
|
|
276
310
|
defaultWorkspaceId: true,
|
|
277
311
|
schema: z
|
|
@@ -286,7 +320,9 @@ export const broadcastSchedulerTools = [
|
|
|
286
320
|
path: `/workspaces/${input.workspace_id}/broadcast-schedules/${input.schedule_id}`,
|
|
287
321
|
}),
|
|
288
322
|
enhanceResponse: (response) => withInstructions(response, [
|
|
289
|
-
'
|
|
323
|
+
'outcome=DELETED — прогонов у расписания не было, оно удалено. outcome=ARCHIVED — прогоны были: расписание выключено и убрано из списка, история прогонов и журнал подач остались (история читается besales_broadcast_schedule_runs).',
|
|
324
|
+
'⚠️ Журнал подач читается по расписанию: новое расписание того же сегмента его не видит. От второго сообщения человека держат журнал касаний («этим сегментом уже слали») и право первого касания, а не архив.',
|
|
325
|
+
'409 — идёт прогон этого расписания: дождись его конца (besales_broadcast_schedule_runs) и повтори.',
|
|
290
326
|
'Если нужно просто остановить рассылку — не удаляй, а сохрани расписание с is_enabled=false.',
|
|
291
327
|
]),
|
|
292
328
|
}),
|
|
@@ -302,39 +338,42 @@ export const broadcastSchedulerTools = [
|
|
|
302
338
|
limit: z.number().int().min(1).max(100).optional(),
|
|
303
339
|
})
|
|
304
340
|
.strict(),
|
|
305
|
-
// ⚠️⚠️ Параметр клеится в
|
|
306
|
-
//
|
|
307
|
-
// сколько бы прогонов агент ни просил, приходило серверных 30, без ошибки и без признака
|
|
308
|
-
// усечения. Тот же приём уже применён в `follow-up.ts` и `platform-setup.ts`.
|
|
341
|
+
// ⚠️⚠️ Параметр клеится в ПУТЬ: у транспорта (`ApiToolRequest`) поля `query` нет, и отдельное
|
|
342
|
+
// поле выбрасывалось бы молча — сервер отдавал бы свои 30 без признака усечения.
|
|
309
343
|
createRequest: (input) => ({
|
|
310
344
|
method: 'GET',
|
|
311
345
|
path: `/workspaces/${input.workspace_id}/broadcast-schedules/${input.schedule_id}/runs` +
|
|
312
346
|
buildListQuery({ limit: input.limit }),
|
|
313
347
|
}),
|
|
314
348
|
enhanceResponse: (response) => withInstructions(response, [
|
|
315
|
-
'periodBucket — локальная ДАТА
|
|
316
|
-
'⚠️ Статусы различают ПРИЧИНЫ, и чинятся они разным: DONE — пакет завершился; WAITING_RESOURCE — прогон отложен, период НЕ
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
'
|
|
321
|
-
'⚠️
|
|
349
|
+
'periodBucket — локальная ДАТА канала: один прогон на сутки, повтор в тот же день невозможен.',
|
|
350
|
+
'⚠️ Статусы различают ПРИЧИНЫ, и чинятся они разным: DONE — пакет завершился; WAITING_RESOURCE — прогон отложен, период НЕ сгорел, waitReason называет чем: WORKSPACE_BUSY_IMPORT — воркспейс занят импортом контактов; WORKSPACE_BUSY_BATCH — пакетом рассылки (ручным или соседнего расписания); FACTS_SOURCE_UNAVAILABLE — не ответил источник фактов; HIGHER_PRIORITY_CLAIMS_UNREADABLE — не прочитались старшие сегменты; CHANNEL_NOT_READY — канал школы не готов: подключение не читается или не отмечен ни один способ отправки (чинится настройкой канала); SEGMENT_RULE_CHANGED — правило сегмента правили, пока шёл отбор (отбор повторится по новому правилу); REPEAT_SEGMENT_PENDING — сегмент-повтор, его шаг отбора ещё не построен; ERASURE_RACE — во время отбора или записи плана стирали данные человека. Прогон повторится ближайшим проходом в тот же день и не раньше времени расписания; не дождался до конца дня — закрывается FAILED с closeReason PERIOD_ENDED_WAITING. SKIPPED и FAILED — период потрачен: closeReason называет причину (RUN_ABANDONED — исполнитель прогона оборвался, а период уже не догнать), closeReasonClass — её род (ANSWER — законный ответ «сегодня некому», FAILURE — отказ, который чинит оператор или настройка, OPERATOR — решение человека, UNKNOWN — перенесённый прогон, причина — текстом прежней системы в error). FAILED без closeReason — отбор отказал по самой настройке (расписания, сегмента или рассылки больше нет) или решение отбора прогона не читается: причина только словами в error. PENDING, RUNNING и IMPORTING — прогон ещё идёт; у IMPORTING тоже бывает waitReason (WORKSPACE_BUSY_IMPORT, WORKSPACE_BUSY_BATCH, CHANNEL_NOT_READY, ERASURE_RACE): карточки заведены, а подача ждёт — не дольше потолка фазы импорта, дальше FAILED с closeReason IMPORT_PHASE_TIMEOUT.',
|
|
351
|
+
SCHEDULE_CLOSE_CLASS_HINT,
|
|
352
|
+
SCHEDULE_GUARD_HINT,
|
|
353
|
+
SCHEDULE_BLOCKS_HINT,
|
|
354
|
+
'error — та же причина ожидания или закрытия СЛОВАМИ на языке воркспейса: человеку пересказывай её, а не ключ waitReason или closeReason.',
|
|
355
|
+
'⚠️ selectedCount — сколько ОТОБРАНО, launchedCount — сколько ПОПЫТОК отправки: доставленные плюс касания, занятые без доказательства доставки. Это не «доставлено»: не называй «доставлено» ни selectedCount, ни launchedCount. Расхождение с selectedCount нормально: часть отсеивают гейты отправки внутри пакета.',
|
|
356
|
+
SCHEDULE_EXCLUSION_HINT,
|
|
357
|
+
'attempts растёт на каждом WAITING_RESOURCE: большое число при WORKSPACE_BUSY_* означает, что воркспейс постоянно занят и расписание стоит передвинуть по времени; при FACTS_SOURCE_UNAVAILABLE чинится источник, при CHANNEL_NOT_READY — настройка канала, а не время.',
|
|
358
|
+
'trigger=RUN_NOW — прогон запущен кнопкой «Запустить сейчас»; actorType и actorId называют, кто её нажал.',
|
|
322
359
|
]),
|
|
323
360
|
}),
|
|
324
361
|
/**
|
|
325
|
-
* «Пул сегодня»: сегменты с правилом на
|
|
362
|
+
* «Пул сегодня»: сегменты с правилом на канале — сколько сейчас в пуле и сколько уйдёт.
|
|
326
363
|
*
|
|
327
|
-
* ⚠️⚠️ Считает СЕРВЕР фоном, одной выкачкой источника фактов на
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
364
|
+
* ⚠️⚠️ Считает СЕРВЕР фоном, одной выкачкой источника фактов на канал: живой предпросмотр боевого
|
|
365
|
+
* расписания в таймаут MCP не укладывается. Вызов заказывает расчёт и сразу отдаёт последний
|
|
366
|
+
* посчитанный; повторный вызов с теми же параметрами — опрос, новый расчёт не заводится. С
|
|
367
|
+
* `local_day` — чтение утреннего расчёта того дня (история), без побочного эффекта.
|
|
331
368
|
*
|
|
332
|
-
* ⚠️⚠️ Побочный эффект ЕСТЬ, и инструмент не читающий: вызов без `local_day` — это POST,
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
* спроса. Идемпотентность объявлена честно: повтор с теми же параметрами переиспользует живой
|
|
336
|
-
*
|
|
369
|
+
* ⚠️⚠️ Побочный эффект ЕСТЬ, и инструмент не читающий: вызов без `local_day` — это POST, который
|
|
370
|
+
* заводит расчёт (строка живёт до двух недель) и снимает прежние неначатые заказы того же
|
|
371
|
+
* заказчика. Пометка «только чтение» отдала бы его хосту в профиль, где вызовы одобряются без
|
|
372
|
+
* спроса. Идемпотентность объявлена честно: повтор с теми же параметрами переиспользует живой и
|
|
373
|
+
* свежий (до 5 минут) расчёт — на этом и держится опрос; исключение — `force`, он и назван
|
|
337
374
|
* «только после своей правки».
|
|
375
|
+
*
|
|
376
|
+
* Схема — отгруженная; тело и строка запроса — в именах сервера v2 (`channelId`, а не `platformId`).
|
|
338
377
|
*/
|
|
339
378
|
defineTool({
|
|
340
379
|
name: 'besales_broadcast_pool_today',
|
|
@@ -348,11 +387,14 @@ export const broadcastSchedulerTools = [
|
|
|
348
387
|
workspace_id: uuidSchema.optional(),
|
|
349
388
|
platform_id: uuidSchema
|
|
350
389
|
.optional()
|
|
351
|
-
.describe('GetCourse
|
|
390
|
+
.describe('Канал GetCourse. Не передан — самый старый по дате создания активный канал GetCourse с расписанием, иначе самый старый активный'),
|
|
352
391
|
window_days: z.number().int().min(1).max(POOL_TODAY_WINDOW_DAYS_MAX).optional(),
|
|
353
392
|
funnels: filterListSchema(FUNNELS_MAX).optional(),
|
|
354
393
|
product_codes: filterListSchema(PRODUCTS_MAX).optional(),
|
|
355
|
-
channels: z
|
|
394
|
+
channels: z
|
|
395
|
+
.array(getcourseTransportEnum)
|
|
396
|
+
.max(getcourseTransportEnum.options.length)
|
|
397
|
+
.optional(),
|
|
356
398
|
cooldown_days: z.number().int().min(0).max(COOLDOWN_DAYS_MAX).optional(),
|
|
357
399
|
// ⚠️ Проверяется только ФОРМА: существование даты сверяет сервер.
|
|
358
400
|
local_day: z.string().regex(LOCAL_DATE_RE, 'local_day: дата ГГГГ-ММ-ДД').optional(),
|
|
@@ -360,7 +402,7 @@ export const broadcastSchedulerTools = [
|
|
|
360
402
|
})
|
|
361
403
|
.strict()
|
|
362
404
|
.superRefine((input, ctx) => {
|
|
363
|
-
// ⚠️ Утренний расчёт дня
|
|
405
|
+
// ⚠️ Утренний расчёт дня посчитан с настройками по умолчанию: фильтры рядом с днём сервер
|
|
364
406
|
// отвергает 400-й, и узнавать об этом из ошибки модель не должна.
|
|
365
407
|
const withSettings = input.window_days !== undefined ||
|
|
366
408
|
input.funnels !== undefined ||
|
|
@@ -372,7 +414,7 @@ export const broadcastSchedulerTools = [
|
|
|
372
414
|
ctx.addIssue({
|
|
373
415
|
code: z.ZodIssueCode.custom,
|
|
374
416
|
path: ['local_day'],
|
|
375
|
-
message: 'local_day читает утренний расчёт дня с настройками по умолчанию — окно, воронки, продукты, каналы,
|
|
417
|
+
message: 'local_day читает утренний расчёт дня с настройками по умолчанию — окно, воронки, продукты, каналы, тихий период и force с ним не передаются',
|
|
376
418
|
});
|
|
377
419
|
}
|
|
378
420
|
}),
|
|
@@ -380,35 +422,41 @@ export const broadcastSchedulerTools = [
|
|
|
380
422
|
? {
|
|
381
423
|
method: 'GET',
|
|
382
424
|
path: `/workspaces/${input.workspace_id}/broadcast-pool` +
|
|
383
|
-
buildListQuery({
|
|
425
|
+
buildListQuery({ channelId: input.platform_id, day: input.local_day }),
|
|
384
426
|
}
|
|
385
427
|
: {
|
|
386
428
|
method: 'POST',
|
|
387
429
|
path: `/workspaces/${input.workspace_id}/broadcast-pool/refresh`,
|
|
388
430
|
body: compactObject({
|
|
389
|
-
|
|
431
|
+
channelId: input.platform_id,
|
|
390
432
|
windowDays: input.window_days,
|
|
391
433
|
funnels: toList(input.funnels),
|
|
392
434
|
products: toList(input.product_codes),
|
|
393
|
-
|
|
435
|
+
transports: input.channels,
|
|
394
436
|
cooldownDays: input.cooldown_days,
|
|
395
437
|
force: input.force,
|
|
396
438
|
}),
|
|
397
439
|
},
|
|
398
440
|
enhanceResponse: (response) => withInstructions(response, [
|
|
399
|
-
'⚠️⚠️ activeRun не пуст — идёт расчёт этих настроек: вызови инструмент ЕЩЁ РАЗ через минуту с теми же параметрами. Повторный вызов — опрос, новый расчёт он не заказывает. run — последний ПОСЧИТАННЫЙ расчёт этих настроек (run.finishedAt — когда); run=null — ещё не считали.',
|
|
400
|
-
'
|
|
401
|
-
'
|
|
402
|
-
'
|
|
403
|
-
'⚠️
|
|
404
|
-
'
|
|
441
|
+
'⚠️⚠️ activeRun не пуст — идёт расчёт этих настроек: вызови инструмент ЕЩЁ РАЗ через минуту с теми же параметрами. Повторный вызов — опрос, новый расчёт он не заказывает. run — последний ПОСЧИТАННЫЙ расчёт этих настроек (run.finishedAt — когда); run=null — ещё не считали. channelId — канал расчёта; channelId=null — у воркспейса нет активного канала GetCourse, считать нечего.',
|
|
442
|
+
'⚠️ 409 с кодом BROADCAST_POOL_WORKSPACE_ORDER_LIMIT — воркспейс уже держит два живых заказа с другими настройками (расчёт идёт по одному на всю систему): дождись готовности одного из них и повтори, НЕ перебирай параметры. factsSource ≠ READY (MISSING — источник фактов не настроен, DISABLED — выключен) — расчёта не будет вовсе. channelServesNoTransports=true — канал не обслуживает ни одного способа отправки: отправлять некуда, чинится настройкой канала.',
|
|
443
|
+
'⚠️⚠️ Строка с расписанием (scheduleId не null) считается ТЕМ ЖЕ путём, что его прогон, на момент ЗАПУСКА этой авторассылки: окно, воронки, продукты, способы отправки, тихий период, «не писать после» и перекрытие предохранителя серии — самого расписания; параметры вызова на неё не действуют. У выключенного расписания (schedule.isEnabled=false) selectedCount значит «уйдёт, если включить». Строка без расписания считается по параметрам вызова (окно window_days, по умолчанию 3 суток; воронки, продукты, способы отправки, тихий период), без рассылки и с потолком одного запуска — это верхняя граница того, что дало бы расписание. Сегмент без правила строки не получает.',
|
|
444
|
+
'Числа строки: poolSize — кандидаты после гейтов источника; selectedCount — отобрано на момент запуска авторассылки строки (у строки без расписания — на момент дня) по тому, что известно сейчас, а не «уже уехало»; отсеяно = poolSize − selectedCount, причины — preview.excludedBy тем же реестром, что у besales_broadcast_schedule_preview; unverifiable, missingIdentifiers, lowCoverageSkipped и соседи там — отсев источника ДО пула, а не причина отбора. preview.guardUnchecked не пуст — предохранитель не проверил столько отобранных: «уйдёт» в этот раз завышено.',
|
|
445
|
+
'⚠️ status: COMPUTED — посчитана; FAILED — не смогли: failure.label — причина словами (её и называй человеку), failure.code — для тебя (SCHEDULE_NOT_FOUND, SEGMENT_NOT_FOUND, MAILING_VARIANT_NOT_FOUND, CHANNEL_NOT_READY — отказ по самой настройке; RULE_MISSING, RULE_UNREADABLE, SOURCE_FAILED, ATTENDANCE_INGEST_* и соседи — отказ правила или источника фактов; HIGHER_PRIORITY_CLAIMS_UNREADABLE — не прочитались расписания старших сегментов; UNEXPECTED — непредвиденный сбой; LEGACY_REASON — причина, записанная прежней системой, её текст в label), числа null, а не 0; SKIPPED — не считается по правилу, skip.reason и skip.label: REPEAT_NEEDS_SCHEDULE (повторному касанию нужно окно не короче skip.minDays — считается только расписанием), WINDOW_NOT_SHARED (окно регистраций skip.registrationDays суток шире общей выборки skip.maxDays — только расписанием), CHANNEL_SERVES_NO_TRANSPORTS (канал не обслуживает ни одного способа отправки), DEADLINE (расчёт упёрся в потолок skip.deadlineMinutes минут раньше, чем дошёл до строки).',
|
|
446
|
+
'⚠️ preview.truncated=true — «не весь пул»: выборка источника не дошла до начала окна строки, самые давние регистрации не рассмотрены. Совет «сузить свежесть» здесь не годится: выборку обрезал общий расчёт канала.',
|
|
447
|
+
'failedRun — отказ (status=FAILED) или замена новым заказом того же заказчика (status=CANCELLED, reason.code=REPLACED_BY_NEW_ORDER), случившиеся ПОСЛЕ показанного расчёта; reason.label — причина словами (HEARTBEAT_LOST — расчёт прервался, DEADLINE — упёрся в потолок времени, FACTS_SOURCE — не ответил источник фактов).',
|
|
405
448
|
'force=true — только после своей правки расписания, правила сегмента или запуска: без него свежий (до 5 минут) расчёт тех же настроек переиспользуется (запуск дня, отобравший людей или завершившийся после расчёта, переиспользование сбрасывает сам). Опрашивая идущий расчёт, force НЕ передавай — он заказал бы ещё один расчёт следом. local_day — утренний расчёт того дня (история «сегмент × день»), всегда с настройками по умолчанию.',
|
|
406
|
-
'⚠️ Вызов с ДРУГИМИ параметрами снимает твой прежний неначатый заказ (расчёт идёт по одному на всю систему). Сравнивая
|
|
407
|
-
'Суммы по строкам не называй «людьми»: человек в двух сегментах посчитан дважды. Поимённых списков в ответе нет — людей смотрят в интерфейсе, под правом на
|
|
408
|
-
'⚠️⚠️ «Сколько уйдёт сегодня» — это run.dayBreakdown, а не суммы poolSize/selectedCount: runDay (ГГГГ-ММ-ДД) — сегодня, пока хоть один запуск дня не завершился, потом — день ближайшего запуска; с local_day — сам этот день со всеми его запусками; scheduleIds — авторассылки этого дня; notComputedScheduleIds — авторассылки дня, которые не посчитались: числа дня без них неполные, так и скажи; ⚠️ план дня (registrations, people, excluded, selected, carriedOver, matured) — только по запускам, которые людей ещё не отбирали: ranScheduleIds — авторассылки дня, чей запуск уже прошёл: их итог — только facts.bySchedule, а у их сегментов run.rows[].dayBreakdown = null (списки после запуска планом не являются); с local_day непустой ranScheduleIds значит, что утренний расчёт досчитался уже после этих запусков, — так и скажи; allRan=true — все запуски дня уже прошли, плана нет: числа плана нулевые, не называй их, итог дня — facts; ranDuringComputation=true — запуск прошёл во время расчёта, числа неточны: без local_day повтори вызов с теми же параметрами (прошедший запуск сам сбрасывает переиспользование расчёта), с local_day утро не пересчитывается — оговори неточность; runTimes — время запусков дня по часам
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
'
|
|
449
|
+
'⚠️ Вызов с ДРУГИМИ параметрами снимает твой прежний неначатый заказ (расчёт идёт по одному на всю систему). Сравнивая каналы или окна, дождись готовности одной комбинации и только потом заказывай следующую — иначе ни одна не досчитается.',
|
|
450
|
+
'Суммы по строкам не называй «людьми»: человек в двух сегментах посчитан дважды. Поимённых списков в ответе нет — людей смотрят в интерфейсе, под правом на контакты (membersAvailable — список строки ещё хранится: только у двух последних расчётов канала и не дольше суток).',
|
|
451
|
+
'⚠️⚠️ «Сколько уйдёт сегодня» — это run.dayBreakdown, а не суммы poolSize/selectedCount: runDay (ГГГГ-ММ-ДД) — сегодня, пока хоть один запуск дня не завершился, потом — день ближайшего запуска; с local_day — сам этот день со всеми его запусками; scheduleIds — авторассылки этого дня; notComputedScheduleIds — авторассылки дня, которые не посчитались: числа дня без них неполные, так и скажи; ⚠️ план дня (registrations, people, excluded, selected, carriedOver, matured) — только по запускам, которые людей ещё не отбирали: ranScheduleIds — авторассылки дня, чей запуск уже прошёл: их итог — только facts.bySchedule, а у их сегментов run.rows[].dayBreakdown = null (списки после запуска планом не являются); с local_day непустой ranScheduleIds значит, что утренний расчёт досчитался уже после этих запусков, — так и скажи; allRan=true — все запуски дня уже прошли, плана нет: числа плана нулевые, не называй их, итог дня — facts; ranDuringComputation=true — запуск прошёл во время расчёта, числа неточны: без local_day повтори вызов с теми же параметрами (прошедший запуск сам сбрасывает переиспользование расчёта), с local_day утро не пересчитывается — оговори неточность; runTimes — время запусков дня по часам канала (прошедший — когда прогон отобрал людей, предстоящий — по правилу); ⚠️⚠️ план считается на момент ЗАПУСКА, а не расчёта: кто созреет к запуску по эфиру, который уже закончился, — уже в числах; явка эфира, который ещё идёт, не известна, и таких людей в числах нет, пока эфир не кончится; matured — registrations плюс outsideSegments (null — не посчитано), outsideSegments — созреют к запуску в окне авторассылок дня, которые их рассматривают (их воронки и продукты, не раньше их свежести, по закончившимся эфирам), но не попали в пул ни одной; outsideSegmentsBy — те же по причинам, только ненулевые ({code, label, count}: paid — купил, application — есть живая заявка, attendanceNoMinutes — был на эфире, минут не записано, paidElsewhere — покупал в другой воронке, seriesRunning — серия ещё идёт, other — прочее); одна причина на регистрацию, сумма равна outsideSegments; старые снимки могли относить paidElsewhere и seriesRunning к other; registrations — регистраций, вошедших в сегменты окнами своих авторассылок (от прошлого отбора до запуска; enteredFrom…enteredTo — общий охват этих окон); people — людей из них, КАЖДЫЙ ОДИН раз при любом числе сегментов; people = selected + сумма excluded; carriedOver — уйдут этим запуском люди с прошлых дней; unrecorded — люди без записанной причины (должно быть 0).',
|
|
452
|
+
SCHEDULE_POOL_GROUPS_HINT,
|
|
453
|
+
SCHEDULE_EXCLUSION_HINT,
|
|
454
|
+
'⚠️⚠️ После запуска экран показывает «Уйдёт» числом «Взято запусками» (facts.taken), когда все авторассылки плана (scheduleIds без ranScheduleIds и notComputedScheduleIds) уже отобрали людей и ни одна отобравшая не лежит вне плана; facts.morningPlanned — «Уйдёт» утреннего плана этого дня (selected + carriedOver утреннего расчёта; null — утреннего плана нет), разница с ним — «добавились к запуску» / «отпали к запуску»: авторассылки отбирают людей по очереди, по приоритету, и за это время у части людей кончаются паузы. Называя «Уйдёт» дня, чьи запуски прошли, называй «взято» и разницу с планом. У каждого сегмента то же в run.rows[].dayBreakdown (без matured; null — сегмент не посчитан или запуск его авторассылки уже прошёл), и его runDay может быть другим днём — у авторассылки не на каждый день; у выключенной авторассылки selected — «уйдёт, если включить», в план дня она не входит.',
|
|
455
|
+
'facts — исход запусков дня run.dayBreakdown.runDay по людям, по всем авторассылкам канала: taken — взято (люди журнала подач), sent — отправлено, noMessenger — карточку не завели или ни один канал не доходит, pending — подан в ещё идущий прогон, notSent — не ушло или доставка не подтвердилась; notSentByReason — те же notSent по причинам, список {code, label, count}: один исход на человека, сумма равна notSent; code — исход отказа отправки (SKIPPED_BOT_SILENCED — бот выключен у контакта, SKIPPED_ALREADY_IN_DIALOG — разговор уже идёт, SKIPPED_UNRESOLVED_VARS — переменные текста без значения и соседи, как в outcomes у besales_broadcast_launches_list), без следа отправки — исход журнала подач (UNRESOLVED_AFTER_BATCH — причина не записана: прогоны до 16.09.2026, RELEASED_LAUNCH_FAILED — запуск сорвался); человеку называй label, а не коды; bySchedule — то же по авторассылкам (scheduleId).',
|
|
456
|
+
'facts.scheduleRuns — запуски дня теми же числами, что строка прогона в besales_broadcast_launches_list: headline — «взято · отправлено» (poolMeasured=false — «счётчиков отбора нет», taken=null, и это НЕ ноль), selection — что увидел запуск при отборе (collapsed — несколько регистраций одного человека, excluded — люди ПУЛА по тем же группам, taken — взято, unrecorded — без записанной причины, должно быть 0; selection=null — пул не измерен), delivery — отправка прогона (null — «ещё неизвестно»: не завершён и никого не взял), planned — «Уйдёт» по утреннему плану сегмента на этот день, planDelta — «взято» минус план. Пул запуска — регистрации окна свежести, а не люди дня: суммы пулов разных авторассылок людьми дня не называй. selectedScheduleIds — авторассылки, чей прогон дня уже отобрал людей или завершился (без фильтров): есть там, но нет в run.dayBreakdown.ranScheduleIds — запуск прошёл уже после расчёта, и без local_day план устарел: повтори вызов с теми же параметрами. ⚠️ Под фильтром воронки или продукта план берёт человека с любой регистрацией в них, а факты — по регистрации, с которой ему написали: у людей с регистрациями в разных воронках план и факты расходятся. facts=null — прогонов авторассылок этого дня нет (запуска ещё не было).',
|
|
457
|
+
'⚠️⚠️ «Сколько ушло СЕГОДНЯ» — это todayFacts, а не facts: today — сегодняшний день канала (ГГГГ-ММ-ДД, по его часам), todayFacts — отправка сегодня в том же виде, что facts, какой бы день ни показывал расклад. После всех запусков дня run.dayBreakdown.runDay и facts уже про ЗАВТРА (facts=null — завтра запусков ещё не было), а сегодняшние итоги остаются в todayFacts. Отвечая, разделяй «Сегодня» (todayFacts) и план ближайшего дня (run.dayBreakdown). todayFacts=null — сегодня запусков не было; с local_day today и todayFacts всегда null — у дня истории итог только facts.',
|
|
458
|
+
'days — до 14 дней канала, свежие первыми (для local_day): дни с утренним расчётом (в 8:00 по часам канала, до запусков дня и впереди заказов в очереди; опоздавший виден по непустому ranScheduleIds) и хотя бы одним запуском, а также дни с прошедшим запуском без утреннего расчёта; у такого дня run — null, итог дня по людям — только facts, а отбор каждого запуска — facts.scheduleRuns. Человеку называй сегменты и авторассылки, а не «строки».',
|
|
459
|
+
'Сводка выкачки у run: fetchedRows (строк источника), fetchTruncated (выборка упёрлась в бюджет — у строк она отзывается preview.truncated), registeredFrom/registeredTo (окно регистраций), trigger (MORNING — утренний расчёт истории, ON_DEMAND — заказанный), settings (настройки строк без расписания). options — варианты фильтров (воронки и продукты, встреченные в выкачке). Называй их, объясняя расхождение чисел между днями.',
|
|
412
460
|
]),
|
|
413
461
|
}),
|
|
414
462
|
defineTool({
|
|
@@ -422,57 +470,50 @@ export const broadcastSchedulerTools = [
|
|
|
422
470
|
segment_id: uuidSchema,
|
|
423
471
|
facets: z
|
|
424
472
|
.object({
|
|
425
|
-
attended:
|
|
473
|
+
attended: triStateSchema.optional(),
|
|
426
474
|
/**
|
|
427
|
-
* Был на ПЕРВОМ эфире серии. `NO` — пропустил первый эфир; вопрос отвечается, пока
|
|
428
|
-
*
|
|
429
|
-
* серию и до её конца равна «нечем проверить».
|
|
475
|
+
* Был на ПЕРВОМ эфире серии. `NO` — пропустил первый эфир; вопрос отвечается, пока серия
|
|
476
|
+
* ещё идёт, поэтому с `attended` они не взаимозаменяемы.
|
|
430
477
|
*/
|
|
431
|
-
attendedFirstSession:
|
|
478
|
+
attendedFirstSession: triStateSchema.optional(),
|
|
432
479
|
activeSecondsMin: z.number().int().min(0).optional(),
|
|
433
480
|
activeSecondsMax: z.number().int().min(0).optional(),
|
|
434
481
|
webinarDayMin: z.number().int().min(0).optional(),
|
|
435
482
|
webinarDayMax: z.number().int().min(0).optional(),
|
|
436
|
-
bannerClick:
|
|
437
|
-
|
|
438
|
-
// мы: у нас настройки больше нет. Правила у воронки нет или данные не готовы — строки
|
|
439
|
-
// приходят непроверяемыми, и людям не уйдёт ничего.
|
|
440
|
-
prelist: z.enum(['YES', 'NO', 'ANY']).optional(),
|
|
483
|
+
bannerClick: triStateSchema.optional(),
|
|
484
|
+
prelist: triStateSchema.optional(),
|
|
441
485
|
/**
|
|
442
|
-
* ⚠️⚠️ ПОВТОРНОЕ
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
*
|
|
446
|
-
* ниже 30 суток сервер откажет.
|
|
486
|
+
* ⚠️⚠️ ПОВТОРНОЕ касание: снимает вечный одноразовый claim приветствия — единственную
|
|
487
|
+
* структурную защиту от второго сообщения живому человеку. Имя сегмента сервер
|
|
488
|
+
* переводит в его идентификатор при сохранении: переименование сегмента правило
|
|
489
|
+
* дальше не ломает.
|
|
447
490
|
*/
|
|
448
491
|
previousSegment: z
|
|
449
492
|
.object({
|
|
450
|
-
key: z.string().min(1).max(
|
|
451
|
-
minDaysSince: z
|
|
493
|
+
key: z.string().min(1).max(PREVIOUS_SEGMENT_KEY_MAX_LENGTH),
|
|
494
|
+
minDaysSince: z
|
|
495
|
+
.number()
|
|
496
|
+
.int()
|
|
497
|
+
.min(REPEAT_TOUCH_MIN_DAYS)
|
|
498
|
+
.max(REPEAT_TOUCH_MAX_DAYS),
|
|
452
499
|
})
|
|
453
500
|
.strict()
|
|
454
501
|
.optional(),
|
|
455
502
|
/**
|
|
456
|
-
* Право первого касания:
|
|
457
|
-
*
|
|
458
|
-
* ⚠️⚠️ Молчание значит `true` — это сегодняшнее поведение платформы. Выключение
|
|
459
|
-
* осознанное и снимает ровно одну защиту: человеку, уже получившему от нас холодное
|
|
460
|
-
* приветствие, уйдёт ВТОРОЕ первое сообщение. Остальные гейты (ответ, оператор,
|
|
461
|
-
* тихий период, «этим сегментом уже слали») продолжают работать. Единственный фасет,
|
|
462
|
-
* который меняется и у замороженного сегмента (решение владельца 15.09.2026).
|
|
503
|
+
* Право первого касания: молчание значит `true`. Единственный признак, который
|
|
504
|
+
* меняется и у замороженного сегмента.
|
|
463
505
|
*/
|
|
464
506
|
untouchedOnly: z.boolean().optional(),
|
|
465
|
-
// ⚠️⚠️
|
|
466
|
-
//
|
|
467
|
-
//
|
|
468
|
-
// менеджера, и уже заплатившим. `ANY` допустим, но выбирается ЯВНО.
|
|
507
|
+
// ⚠️⚠️ ОБЯЗАТЕЛЬНЫ: правило целиком из умолчаний подошло бы КАЖДОМУ в окне — рассылка
|
|
508
|
+
// ушла бы и людям с живой заявкой у менеджера, и уже заплатившим. `ANY` допустим, но
|
|
509
|
+
// выбирается ЯВНО.
|
|
469
510
|
application: z.enum(['NONE', 'CANCELLED', 'ANY']),
|
|
470
511
|
payment: z.enum(['NOT_IN_FUNNEL', 'NOT_ANYWHERE', 'ANY']),
|
|
471
512
|
})
|
|
472
513
|
.strict(),
|
|
473
514
|
freshness_anchor: anchorSchema,
|
|
474
515
|
wait_after_series_end_hours: z.number().int().min(0).max(WAIT_HOURS_MAX).optional(),
|
|
475
|
-
priority: z.number().int().min(
|
|
516
|
+
priority: z.number().int().min(PRIORITY_MIN).max(PRIORITY_MAX).optional(),
|
|
476
517
|
})
|
|
477
518
|
.strict(),
|
|
478
519
|
createRequest: (input) => ({
|
|
@@ -486,14 +527,15 @@ export const broadcastSchedulerTools = [
|
|
|
486
527
|
}),
|
|
487
528
|
}),
|
|
488
529
|
enhanceResponse: (response) => withInstructions(response, [
|
|
489
|
-
'⚠️⚠️ 409 означает, что сегментом УЖЕ отправляли и правило заморожено. Это не ошибка доступа: строки отчётов уже несут это имя, и правка правила задним числом сделала бы разрез несопоставимым сам с собой. Заведи НОВЫЙ сегмент. Исключения — priority и facets.untouchedOnly: их меняют и у замороженного сегмента, передав остальное правило (фасеты, freshness_anchor, wait_after_series_end_hours) ТЕМ ЖЕ, что у сегмента
|
|
490
|
-
'
|
|
491
|
-
'
|
|
492
|
-
'
|
|
493
|
-
'
|
|
494
|
-
'
|
|
530
|
+
'⚠️⚠️ 409 означает, что сегментом УЖЕ отправляли и правило заморожено. Это не ошибка доступа: строки отчётов уже несут это имя, и правка правила задним числом сделала бы разрез несопоставимым сам с собой. Заведи НОВЫЙ сегмент. Исключения — priority и facets.untouchedOnly: их меняют и у замороженного сегмента, передав остальное правило (фасеты, freshness_anchor, wait_after_series_end_hours) ТЕМ ЖЕ, что у сегмента сейчас.',
|
|
531
|
+
'warnings в ответе — правка принята, но у неё есть цена: перескажи её человеку своими словами (например, смена права первого касания у сегмента, которым уже отправляли, смешивает в его отчёте первые и повторные сообщения).',
|
|
532
|
+
'attendedFirstSession=NO — «пропустил ПЕРВЫЙ эфир», единственный способ написать человеку, пока его серия идёт. Требует freshness_anchor=FIRST_SESSION_END_PLUS_WAIT (иначе сервер откажет: вход считался бы от конца серии — на 4–5 суток позже), а ни одно расписание сегмента не должно останавливать отправки внутри серии (guard_active_series не BLOCK — иначе сервер откажет, потому что пул отсеивался бы целиком на каждом запуске).',
|
|
533
|
+
'attended=NO проходит только достоверный «не пришёл» — и одни короткие заходы меньше пяти минут в сумме. Человек, про которого данных нет (серия ещё идёт, назначения нет), в сегмент НЕ попадает: отсутствие данных о явке не равно отсутствию человека на эфире.',
|
|
534
|
+
'Секунды, а не минуты: «от 120 минут» — это activeSecondsMin=7200. Человек без известной длительности порог не проходит.',
|
|
535
|
+
'wait_after_series_end_hours — сколько ждать после эфира, прежде чем считать отсутствие явки доказанным. Не передано — 24 часа: к этому моменту явка досчитана примерно у трёх четвертей (6 ч → 56 %, 24 ч → 74 %, 48 ч → 81 %), а недосчитанный человек в отбор не попадает.',
|
|
536
|
+
'application=CANCELLED требует отменённой заявки и отсутствия любой другой: живая, закрытая или незнакомая заявка бьёт отменённую.',
|
|
495
537
|
'payment=NOT_ANYWHERE исключает всех, кто когда-либо успешно платил — включая тех, кому потом сделали возврат: он действительно платил.',
|
|
496
|
-
'untouchedOnly не указан = true: сегмент уходит только тем, кому мы никогда не писали.
|
|
538
|
+
'previousSegment.key — имя сегмента; сервер хранит его идентификатор, и в ответе прошлое касание приходит идентификатором с текущим именем. untouchedOnly не указан = true: сегмент уходит только тем, кому мы никогда не писали. Ставь false осознанно — это разрешает второе первое сообщение человеку, уже получившему от нас холодное приветствие.',
|
|
497
539
|
]),
|
|
498
540
|
}),
|
|
499
541
|
];
|