@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
|
@@ -0,0 +1,868 @@
|
|
|
1
|
+
// Образец ОТГРУЖЕННОГО вызова и ответ ветки v2 на него.
|
|
2
|
+
//
|
|
3
|
+
// ⭐ Сверка «примет ли v2 то, что шлёт отгруженный клиент» честна, только если образец — ЗАКОННЫЙ вызов отгруженного
|
|
4
|
+
// клиента. Полный набор полей его схемы им часто не является: поля бывают взаимоисключающими, и такой набор отвергает
|
|
5
|
+
// сам отгруженный инструмент. Поэтому образец обязан пройти отгруженный ФИЛЬТР (схему zod и конструктор запроса), а
|
|
6
|
+
// уже потом — фильтр ветки v2.
|
|
7
|
+
//
|
|
8
|
+
// ⛔ Отвергнутые формы решения: «снимать поля, пока v2 не примет» прятала бы настоящие отказы v2 — снятие делает образец
|
|
9
|
+
// зелёным ровно там, где клиенту откажут; рукописный реестр взаимоисключений — вторая копия правил, которая протухнет и
|
|
10
|
+
// не видит случаев, где v2 строже отгруженного.
|
|
11
|
+
//
|
|
12
|
+
// Модуль — чистые функции: фильтры и реестры приходят параметрами, выгрузка их собирает.
|
|
13
|
+
import { schemaConstraints } from './registry-diff.mjs';
|
|
14
|
+
|
|
15
|
+
/** Закрытый перечень причин «образец невозможен» уровня случая. */
|
|
16
|
+
export const SHIPPED_SAMPLE_IMPOSSIBLE_REASONS = Object.freeze([
|
|
17
|
+
'SHIPPED_REJECTS_EVERY_SET',
|
|
18
|
+
'V2_REQUIRES_FIELD_UNKNOWN_TO_SHIPPED',
|
|
19
|
+
'SHIPPED_DEFINITION_UNAVAILABLE',
|
|
20
|
+
// Проверенное значение, задающее вариант, отгруженная схема не принимает (объявлено строкой ограничения): такой
|
|
21
|
+
// формы у отгруженного клиента нет, сверять нечего. Сервер объясняет её строкой ограничения, а не строкой реестра.
|
|
22
|
+
'V2_ONLY_VARIANT',
|
|
23
|
+
'SHIPPED_COMBINATION_LIMIT',
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Почему путь снят с образца: `DECLARED` — ветка v2 отвергает его, и отказ объявлен реестром отставания;
|
|
28
|
+
* `SHIPPED_FORBIDS` — отгруженный инструмент не принимает поле в этой форме варианта даже с заведомо годным значением;
|
|
29
|
+
* `VALUE_NOT_FOUND` — годного значения подобрать не из чего (видимая причина, сервер требует строку реестра);
|
|
30
|
+
* `SHIPPED_REJECTS_KNOWN` — проверенное значение варианта отгруженный фильтр не принимает, и законный вызов того же
|
|
31
|
+
* маршрута собран без него (форма ветки v2, которую отгруженный клиент не пошлёт).
|
|
32
|
+
*/
|
|
33
|
+
export const SHIPPED_SAMPLE_OMISSIONS = Object.freeze([
|
|
34
|
+
'DECLARED',
|
|
35
|
+
'SHIPPED_FORBIDS',
|
|
36
|
+
'VALUE_NOT_FOUND',
|
|
37
|
+
'SHIPPED_REJECTS_KNOWN',
|
|
38
|
+
'SEARCH_LIMIT_EXCEEDED',
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
/** Предел перебора подмножеств снимаемых путей. */
|
|
42
|
+
export const SUBSET_LIMIT = 3;
|
|
43
|
+
const ALTERNATIVE_CANDIDATE_LIMIT = 512;
|
|
44
|
+
const MAX_ROUNDS = 12;
|
|
45
|
+
|
|
46
|
+
const isObject = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
47
|
+
|
|
48
|
+
/** Путь проблемы zod в нотации реестра отставания: индексы массива — `[]`. */
|
|
49
|
+
export function normalizeIssuePath(segments) {
|
|
50
|
+
let out = '';
|
|
51
|
+
for (const segment of segments ?? []) {
|
|
52
|
+
if (typeof segment === 'number') out += '[]';
|
|
53
|
+
else out += out === '' ? String(segment) : `.${String(segment)}`;
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function messageOf(error) {
|
|
59
|
+
return error instanceof Error ? error.message : String(error);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Пути отказа фильтра; `path: null` — отказ без пути (правило корня, исключение конструктора запроса). */
|
|
63
|
+
export function refusalPaths(error) {
|
|
64
|
+
if (!Array.isArray(error?.issues)) return [{ path: null, message: messageOf(error) }];
|
|
65
|
+
const out = [];
|
|
66
|
+
const visit = (issue, prefix = []) => {
|
|
67
|
+
const segments = [...prefix, ...(issue.path ?? [])];
|
|
68
|
+
// Zod 4: `errors` — массив веток; Zod 3: `unionErrors` — массив ZodError.
|
|
69
|
+
const branches = issue.code === 'invalid_union'
|
|
70
|
+
? issue.errors ?? issue.unionErrors?.map((branch) => branch.issues)
|
|
71
|
+
: undefined;
|
|
72
|
+
if (Array.isArray(branches)) {
|
|
73
|
+
for (const branch of branches) {
|
|
74
|
+
for (const nested of branch) visit(nested, segments);
|
|
75
|
+
}
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const base = normalizeIssuePath(segments);
|
|
79
|
+
if (issue.code === 'unrecognized_keys' && Array.isArray(issue.keys)) {
|
|
80
|
+
for (const key of issue.keys) out.push({ path: base === '' ? key : `${base}.${key}`, message: issue.message });
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
out.push({ path: base === '' ? null : base, message: issue.message });
|
|
84
|
+
};
|
|
85
|
+
for (const issue of error.issues) visit(issue);
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Фильтр определения: разбор схемой, подстановка рабочей области по умолчанию, конструктор запроса. */
|
|
90
|
+
export function filterOf(definition, workspaceId) {
|
|
91
|
+
return (args) => {
|
|
92
|
+
try {
|
|
93
|
+
let input = definition.schema.parse(args);
|
|
94
|
+
if (definition.defaultWorkspaceId && input.workspace_id === undefined) {
|
|
95
|
+
input = definition.schema.parse({ ...input, workspace_id: workspaceId });
|
|
96
|
+
}
|
|
97
|
+
return { ok: true, request: definition.createRequest(input) };
|
|
98
|
+
} catch (error) {
|
|
99
|
+
return { ok: false, refusals: refusalPaths(error) };
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** `a[].b` → `['a', '[]', 'b']`. */
|
|
105
|
+
export function splitPath(path) {
|
|
106
|
+
const out = [];
|
|
107
|
+
for (const part of path.split('.')) {
|
|
108
|
+
const match = /^(.*?)((?:\[\])*)$/u.exec(part);
|
|
109
|
+
if (match[1] !== '') out.push(match[1]);
|
|
110
|
+
for (let index = 0; index < match[2].length / 2; index += 1) out.push('[]');
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Значения по пути; массив раскрывается по всем элементам. */
|
|
116
|
+
export function valuesAt(value, path) {
|
|
117
|
+
let current = [value];
|
|
118
|
+
for (const segment of splitPath(path)) {
|
|
119
|
+
const next = [];
|
|
120
|
+
for (const node of current) {
|
|
121
|
+
if (segment === '[]') {
|
|
122
|
+
if (Array.isArray(node)) next.push(...node);
|
|
123
|
+
} else if (isObject(node) && Object.hasOwn(node, segment)) {
|
|
124
|
+
next.push(node[segment]);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
current = next;
|
|
128
|
+
}
|
|
129
|
+
return current;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function hasPath(value, path) {
|
|
133
|
+
return valuesAt(value, path).length > 0;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function rewriteAt(value, path, leaf) {
|
|
137
|
+
const segments = splitPath(path);
|
|
138
|
+
const visit = (node, index) => {
|
|
139
|
+
const segment = segments[index];
|
|
140
|
+
const last = index === segments.length - 1;
|
|
141
|
+
if (segment === '[]') {
|
|
142
|
+
if (!Array.isArray(node)) return node;
|
|
143
|
+
return last ? leaf(node, undefined) : node.map((item) => visit(item, index + 1));
|
|
144
|
+
}
|
|
145
|
+
if (!isObject(node)) return node;
|
|
146
|
+
if (last) return leaf(node, segment);
|
|
147
|
+
if (!Object.hasOwn(node, segment)) return node;
|
|
148
|
+
const child = visit(node[segment], index + 1);
|
|
149
|
+
const out = { ...node };
|
|
150
|
+
if (child === undefined) delete out[segment];
|
|
151
|
+
else out[segment] = child;
|
|
152
|
+
return out;
|
|
153
|
+
};
|
|
154
|
+
return visit(value, 0);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Копия без пути; путь внутри массива снимается у каждого элемента, путь `x[]` снимает сам массив. */
|
|
158
|
+
export function withoutPath(value, path) {
|
|
159
|
+
return rewriteAt(value, path, (node, key) => {
|
|
160
|
+
if (key === undefined) return undefined;
|
|
161
|
+
if (!Object.hasOwn(node, key)) return node;
|
|
162
|
+
const out = { ...node };
|
|
163
|
+
delete out[key];
|
|
164
|
+
return out;
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Копия с заданным значением по пути (у каждого элемента массива на пути). */
|
|
169
|
+
export function withValueAt(value, path, leafValue) {
|
|
170
|
+
return rewriteAt(value, path, (node, key) => (key === undefined ? node : { ...node, [key]: leafValue }));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Единица покрывает путь отказа, если она и есть этот путь или его предок. */
|
|
174
|
+
export function coversPath(unit, path) {
|
|
175
|
+
return path === unit || path.startsWith(`${unit}.`) || path.startsWith(`${unit}[]`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Пути связаны, если один — предок другого или они равны. */
|
|
179
|
+
export function relatedPaths(a, b) {
|
|
180
|
+
return a === b || b.startsWith(`${a}.`) || b.startsWith(`${a}[]`) || a.startsWith(`${b}.`) || a.startsWith(`${b}[]`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Корни путей, которые генератор добавил поверх проверенных аргументов: самый короткий путь, которого нет у проверенных,
|
|
185
|
+
* при том что его родитель у них есть. Снимается корень целиком — снятие одних листьев оставляло бы пустые элементы
|
|
186
|
+
* массива, которые фильтр читает как заданное поле. Порядок — порядок полей схемы.
|
|
187
|
+
*/
|
|
188
|
+
export function generatedRoots(full, known, prefix = '') {
|
|
189
|
+
const roots = [];
|
|
190
|
+
if (isObject(full)) {
|
|
191
|
+
for (const [key, value] of Object.entries(full)) {
|
|
192
|
+
const path = prefix === '' ? key : `${prefix}.${key}`;
|
|
193
|
+
if (!isObject(known) || !Object.hasOwn(known, key)) roots.push(path);
|
|
194
|
+
else roots.push(...generatedRoots(value, known[key], path));
|
|
195
|
+
}
|
|
196
|
+
} else if (Array.isArray(full) && Array.isArray(known) && full.length > 0 && known.length > 0) {
|
|
197
|
+
roots.push(...generatedRoots(full[0], known[0], `${prefix}[]`));
|
|
198
|
+
}
|
|
199
|
+
return roots;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** Снимаемые единицы проверенных аргументов для поиска альтернатив: поля верхнего уровня и листья вложенных. */
|
|
203
|
+
export function knownUnits(known) {
|
|
204
|
+
if (!isObject(known)) return [];
|
|
205
|
+
const units = [];
|
|
206
|
+
const leaves = (value, prefix) => {
|
|
207
|
+
if (isObject(value) && Object.keys(value).length > 0) {
|
|
208
|
+
return Object.entries(value).flatMap(([key, child]) => leaves(child, `${prefix}.${key}`));
|
|
209
|
+
}
|
|
210
|
+
if (Array.isArray(value) && value.length > 0 && isObject(value[0])) return leaves(value[0], `${prefix}[]`);
|
|
211
|
+
return [prefix];
|
|
212
|
+
};
|
|
213
|
+
for (const [key, value] of Object.entries(known)) {
|
|
214
|
+
units.push(key);
|
|
215
|
+
if (isObject(value) || (Array.isArray(value) && isObject(value[0]))) {
|
|
216
|
+
units.push(...leaves(value, key).filter((path) => path !== key));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return units;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** Листья сгенерированного корня: корень остаётся, а ошибочный вложенный ключ можно снять. */
|
|
223
|
+
function leavesUnder(value, path) {
|
|
224
|
+
const [root] = valuesAt(value, path);
|
|
225
|
+
if (!isObject(root)) return [];
|
|
226
|
+
const walk = (node, prefix) => {
|
|
227
|
+
if (isObject(node)) {
|
|
228
|
+
return Object.entries(node).flatMap(([key, child]) => walk(child, `${prefix}.${key}`));
|
|
229
|
+
}
|
|
230
|
+
if (Array.isArray(node) && isObject(node[0])) return walk(node[0], `${prefix}[]`);
|
|
231
|
+
return [prefix];
|
|
232
|
+
};
|
|
233
|
+
return walk(root, path);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Подмножества по возрастанию размера до `maxSize`, внутри размера — в порядке списка. */
|
|
237
|
+
export function* subsetsUpTo(items, maxSize) {
|
|
238
|
+
for (let size = 1; size <= Math.min(maxSize, items.length); size += 1) {
|
|
239
|
+
const index = Array.from({ length: size }, (_, position) => position);
|
|
240
|
+
for (;;) {
|
|
241
|
+
yield index.map((position) => items[position]);
|
|
242
|
+
let cursor = size - 1;
|
|
243
|
+
while (cursor >= 0 && index[cursor] === items.length - size + cursor) cursor -= 1;
|
|
244
|
+
if (cursor < 0) break;
|
|
245
|
+
index[cursor] += 1;
|
|
246
|
+
for (let next = cursor + 1; next < size; next += 1) index[next] = index[next - 1] + 1;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** Пустая добавка и затем ограниченные группы, без создания полного списка сочетаний в памяти. */
|
|
252
|
+
function* additionsUpTo(items, maxSize) {
|
|
253
|
+
yield [];
|
|
254
|
+
yield* subsetsUpTo(items, maxSize);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Законный вызов снятием единиц `removable`; `keep` и связанные с ним единицы не трогаются. Сначала снимаются единицы,
|
|
259
|
+
* которые называют пути отказа; отказ без пути — перебор подмножеств по возрастанию размера до {@link SUBSET_LIMIT}.
|
|
260
|
+
*
|
|
261
|
+
* @returns {{ args: unknown, removed: string[], verdict: { ok: true } } | null}
|
|
262
|
+
*/
|
|
263
|
+
export function findLegalCall(start, removable, accept, keep = []) {
|
|
264
|
+
let current = start;
|
|
265
|
+
const removed = [];
|
|
266
|
+
for (let round = 0; round < MAX_ROUNDS; round += 1) {
|
|
267
|
+
const verdict = accept(current);
|
|
268
|
+
if (verdict.ok) return { args: current, removed, verdict };
|
|
269
|
+
const present = removable.filter(
|
|
270
|
+
(unit) =>
|
|
271
|
+
!removed.includes(unit) && hasPath(current, unit) && !keep.some((kept) => coversPath(unit, kept)),
|
|
272
|
+
);
|
|
273
|
+
// Снимается только поле отказа или его предок: отказ правила ОБЪЕКТА (путь — сам объект) не называет, какое из
|
|
274
|
+
// вложенных полей лишнее, и снятие всех разом ломало бы вызов; такой отказ решает перебор.
|
|
275
|
+
const guided = present.filter((unit) =>
|
|
276
|
+
verdict.refusals.some((refusal) => refusal.path !== null && coversPath(unit, refusal.path)),
|
|
277
|
+
);
|
|
278
|
+
if (guided.length > 0) {
|
|
279
|
+
for (const unit of guided) current = withoutPath(current, unit);
|
|
280
|
+
removed.push(...guided);
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
for (const subset of subsetsUpTo(present, SUBSET_LIMIT)) {
|
|
284
|
+
const candidate = subset.reduce((args, unit) => withoutPath(args, unit), current);
|
|
285
|
+
const attempt = accept(candidate);
|
|
286
|
+
if (attempt.ok) return { args: candidate, removed: [...removed, ...subset], verdict: attempt };
|
|
287
|
+
}
|
|
288
|
+
return present.length > SUBSET_LIMIT
|
|
289
|
+
? { limitExceeded: present, args: current, removed, verdict }
|
|
290
|
+
: null;
|
|
291
|
+
}
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/** Форма маршрута: метод и адрес без строки запроса, идентификаторы — заполнителем. */
|
|
296
|
+
export function routeShape(request) {
|
|
297
|
+
const path = request.path
|
|
298
|
+
.split('?')[0]
|
|
299
|
+
.split('/')
|
|
300
|
+
.map((segment) => (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu.test(segment) || /^\d+$/u.test(segment) ? ':id' : segment))
|
|
301
|
+
.join('/');
|
|
302
|
+
return `${request.method ?? 'POST'} ${path}`;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Законный вызов НАРАЩИВАНИЕМ: от базы (проверенные аргументы) единицы `units` добавляются по одной в порядке списка;
|
|
307
|
+
* единица остаётся, если фильтр принимает набор и маршрут отгруженного вызова не меняется (поле, переключающее
|
|
308
|
+
* маршрут — идентификатор правки вместо создания, — задаёт ДРУГОЙ вариант). Перебор подмножеств тут не нужен:
|
|
309
|
+
* отчёт, чей фильтр запрещает десяток параметров разом, собирается за один проход.
|
|
310
|
+
*
|
|
311
|
+
* @returns {{ args: unknown, removed: string[], verdict: { ok: true } } | null} `null` — база сама незаконна
|
|
312
|
+
*/
|
|
313
|
+
export function growLegalCall(base, source, units, accept) {
|
|
314
|
+
const baseVerdict = accept(base);
|
|
315
|
+
if (!baseVerdict.ok) return null;
|
|
316
|
+
const shape = routeShape(baseVerdict.request);
|
|
317
|
+
const fullVerdict = accept(source);
|
|
318
|
+
if (fullVerdict.ok && routeShape(fullVerdict.request) === shape) {
|
|
319
|
+
return { args: source, removed: [], verdict: fullVerdict };
|
|
320
|
+
}
|
|
321
|
+
let current = base;
|
|
322
|
+
let verdict = baseVerdict;
|
|
323
|
+
const removed = [];
|
|
324
|
+
for (const unit of units) {
|
|
325
|
+
const [value] = valuesAt(source, unit);
|
|
326
|
+
const candidate = value === undefined ? current : withValueAt(current, unit, value);
|
|
327
|
+
const attempt = accept(candidate);
|
|
328
|
+
if (attempt.ok && routeShape(attempt.request) === shape) {
|
|
329
|
+
current = candidate;
|
|
330
|
+
verdict = attempt;
|
|
331
|
+
} else {
|
|
332
|
+
removed.push(unit);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
// Некоторые необязательные поля законны лишь ВМЕСТЕ. Добавляем принятые группы до
|
|
336
|
+
// неподвижной точки. Остаток разбирается поимённо поиском альтернатив с явным пределом.
|
|
337
|
+
let pending = removed;
|
|
338
|
+
for (;;) {
|
|
339
|
+
let accepted = null;
|
|
340
|
+
for (const subset of subsetsUpTo(pending, SUBSET_LIMIT)) {
|
|
341
|
+
const candidate = subset.reduce((args, unit) => {
|
|
342
|
+
const [value] = valuesAt(source, unit);
|
|
343
|
+
return value === undefined ? args : withValueAt(args, unit, value);
|
|
344
|
+
}, current);
|
|
345
|
+
const attempt = accept(candidate);
|
|
346
|
+
if (attempt.ok && routeShape(attempt.request) === shape) accepted = { subset, candidate, attempt };
|
|
347
|
+
}
|
|
348
|
+
if (accepted === null) break;
|
|
349
|
+
current = accepted.candidate;
|
|
350
|
+
verdict = accepted.attempt;
|
|
351
|
+
pending = pending.filter((unit) => !accepted.subset.includes(unit));
|
|
352
|
+
}
|
|
353
|
+
return { args: current, removed: pending, verdict };
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const FORMAT_CHECKS = {
|
|
357
|
+
uuid: (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu.test(value),
|
|
358
|
+
date: (value) => /^\d{4}-\d{2}-\d{2}$/u.test(value),
|
|
359
|
+
'date-time': (value) => !Number.isNaN(Date.parse(value)) && /T/u.test(value),
|
|
360
|
+
email: (value) => /^[^@\s]+@[^@\s]+$/u.test(value),
|
|
361
|
+
uri: (value) => {
|
|
362
|
+
try {
|
|
363
|
+
return Boolean(new URL(value));
|
|
364
|
+
} catch {
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
/** Нарушает ли значение ограничение `kind` узла схемы ветки v2. */
|
|
371
|
+
export function violatesConstraint(kind, constraints, value) {
|
|
372
|
+
if (constraints === undefined) return false;
|
|
373
|
+
switch (kind) {
|
|
374
|
+
case 'enum':
|
|
375
|
+
return constraints.enum !== undefined && !constraints.enum.split('|').includes(String(value));
|
|
376
|
+
case 'pattern':
|
|
377
|
+
try {
|
|
378
|
+
return typeof value === 'string' && !new RegExp(constraints.pattern, 'u').test(value);
|
|
379
|
+
} catch {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
case 'format':
|
|
383
|
+
return typeof value === 'string' && FORMAT_CHECKS[constraints.format]?.(value) === false;
|
|
384
|
+
case 'minimum':
|
|
385
|
+
return typeof value === 'number' && value < constraints.minimum;
|
|
386
|
+
case 'maximum':
|
|
387
|
+
return typeof value === 'number' && value > constraints.maximum;
|
|
388
|
+
case 'minLength':
|
|
389
|
+
return typeof value === 'string' && value.length < constraints.minLength;
|
|
390
|
+
case 'maxLength':
|
|
391
|
+
return typeof value === 'string' && value.length > constraints.maxLength;
|
|
392
|
+
case 'minItems':
|
|
393
|
+
return Array.isArray(value) && value.length < constraints.minItems;
|
|
394
|
+
case 'maxItems':
|
|
395
|
+
return Array.isArray(value) && value.length > constraints.maxItems;
|
|
396
|
+
case 'type': {
|
|
397
|
+
const declared = Array.isArray(constraints.type) ? constraints.type : [constraints.type];
|
|
398
|
+
const actual = Array.isArray(value) ? 'array' : value === null ? 'null' : typeof value;
|
|
399
|
+
const matches = declared.some(
|
|
400
|
+
(type) => type === actual || (type === 'integer' && Number.isInteger(value)) || type === undefined,
|
|
401
|
+
);
|
|
402
|
+
return !matches;
|
|
403
|
+
}
|
|
404
|
+
default:
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Объясняет ли СРАВНЕНИЕ СХЕМ отказ v2 по пути: поля нет в v2, либо ограничение на этом пути отличается и значение
|
|
411
|
+
* образца его действительно нарушает. Строка ограничения, которое значение не нарушает, отказ не объясняет — иначе
|
|
412
|
+
* строка «объясняла» бы чужой класс отказа.
|
|
413
|
+
*
|
|
414
|
+
* @param {ReadonlyArray<{ path: string, kind: string }>} schemaLag строки сравнения схем этого инструмента
|
|
415
|
+
*/
|
|
416
|
+
export function schemaLagExplains(path, args, schemaLag, v2Constraints) {
|
|
417
|
+
for (const row of schemaLag) {
|
|
418
|
+
const [rowPath, kind] = row.path.split('#');
|
|
419
|
+
if (row.kind === 'MISSING_FIELD' && relatedPaths(rowPath, path)) return true;
|
|
420
|
+
if (row.kind === 'CONSTRAINT' && rowPath === path && kind !== 'required') {
|
|
421
|
+
if (valuesAt(args, path).some((value) => violatesConstraint(kind, v2Constraints.get(path), value))) return true;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Ответ ветки v2 на законный отгруженный вызов.
|
|
429
|
+
*
|
|
430
|
+
* Отказ, который объясняет сравнение схем, снимает путь (`DECLARED`). Прочий отказ по пути — наблюдённый отказ,
|
|
431
|
+
* живущий в коде инструмента: путь снимается, а отказ уходит в сверку с реестром (там он обязан быть объявлен строкой
|
|
432
|
+
* `#refused`). Отказ без пути — находка другого рода: отказ обязан жить в схеме с путём. Путь, которого нет в образце,
|
|
433
|
+
* — поле, которое v2 требует, а образец не несёт.
|
|
434
|
+
*/
|
|
435
|
+
export function v2Verdict(args, acceptV2, explains) {
|
|
436
|
+
let current = args;
|
|
437
|
+
const omitted = [];
|
|
438
|
+
const refused = [];
|
|
439
|
+
for (let round = 0; round < MAX_ROUNDS; round += 1) {
|
|
440
|
+
const verdict = acceptV2(current);
|
|
441
|
+
if (verdict.ok) return { request: verdict.request, args: current, omitted, refused };
|
|
442
|
+
const pathless = verdict.refusals.filter((refusal) => refusal.path === null);
|
|
443
|
+
if (pathless.length > 0) return { pathless: pathless.map((refusal) => refusal.message), omitted, refused };
|
|
444
|
+
const absent = [...new Set(verdict.refusals.filter((r) => !hasPath(current, r.path)).map((r) => r.path))];
|
|
445
|
+
if (absent.length > 0) return { absent, omitted, refused };
|
|
446
|
+
for (const path of [...new Set(verdict.refusals.map((refusal) => refusal.path))]) {
|
|
447
|
+
if (!explains(path, current)) {
|
|
448
|
+
refused.push({
|
|
449
|
+
path,
|
|
450
|
+
message: verdict.refusals.find((refusal) => refusal.path === path)?.message ?? '',
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
current = withoutPath(current, path);
|
|
454
|
+
omitted.push({ path, why: 'DECLARED' });
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return { pathless: ['ветка v2 не приняла образец за предел раундов снятия'], omitted, refused };
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Образец отгруженного вызова для пары «инструмент × вариант».
|
|
462
|
+
*
|
|
463
|
+
* @param {object} input
|
|
464
|
+
* @param {unknown} input.full набор: образец отгруженной схемы поверх проверенных аргументов (шаг 0 уже применён)
|
|
465
|
+
* @param {unknown} input.known проверенные аргументы варианта
|
|
466
|
+
* @param {(args: unknown) => object} input.acceptShipped отгруженный фильтр
|
|
467
|
+
* @param {(args: unknown) => object} input.acceptV2 фильтр ветки v2
|
|
468
|
+
* @param {(path: string, args: unknown) => boolean} input.explains объясняет ли сравнение схем отказ v2 по пути
|
|
469
|
+
* @param {Map<string, object>} input.shippedConstraints пути отгруженной схемы с ограничениями (`schemaPathsOf`)
|
|
470
|
+
* @param {(path: string) => unknown[]} input.goodValues заведомо годные значения пути (проверенные аргументы других
|
|
471
|
+
* вариантов того же инструмента)
|
|
472
|
+
* @param {string} [input.mainRouteShape] форма маршрута варианта (`routeShape` его запроса ветки v2)
|
|
473
|
+
*/
|
|
474
|
+
export function buildShippedSample(input) {
|
|
475
|
+
const { full, known, acceptShipped, acceptV2, explains, shippedConstraints, goodValues, mainRouteShape } = input;
|
|
476
|
+
const shippedKnows = (path) => shippedConstraints.has(path);
|
|
477
|
+
const result = { omitted: [], alternatives: [], refused: [], pathless: [], absentKnown: [] };
|
|
478
|
+
const roots = generatedRoots(full, known);
|
|
479
|
+
const base = roots.reduce((args, unit) => withoutPath(args, unit), full);
|
|
480
|
+
// База законна — наращивание (не зависит от числа конфликтов); незаконна (отгруженный требует поле, которого нет у
|
|
481
|
+
// проверенных) — снятие по путям отказа и перебор от полного набора.
|
|
482
|
+
let legal = growLegalCall(base, full, roots, acceptShipped) ?? findLegalCall(full, roots, acceptShipped);
|
|
483
|
+
// Полный набор может требовать снять десятки необязательных полей, хотя база незаконна всего
|
|
484
|
+
// из-за одного-двух проверенных полей v2. Прежде ранний выход скрывал эту короткую правку.
|
|
485
|
+
const initialLimit = legal?.limitExceeded;
|
|
486
|
+
if (initialLimit) legal = null;
|
|
487
|
+
let knownRemoved = [];
|
|
488
|
+
let movedVariant = [];
|
|
489
|
+
if (legal === null) {
|
|
490
|
+
// Сами проверенные аргументы отгруженный фильтр не принимает (форма ветки v2: параметр, запрещённый отгруженным
|
|
491
|
+
// для этого варианта, или значение другой формы). Снимается наименьшее число проверенных полей — перебором, без
|
|
492
|
+
// снятия по пути отказа: путь отказа здесь часто указывает на поле, ЗАДАЮЩЕЕ вариант, — и маршрут ветки v2
|
|
493
|
+
// обязан остаться маршрутом варианта.
|
|
494
|
+
const acceptSameRoute = (args) => {
|
|
495
|
+
const shipped = acceptShipped(args);
|
|
496
|
+
if (!shipped.ok) return shipped;
|
|
497
|
+
const v2 = v2Verdict(args, acceptV2, explains);
|
|
498
|
+
const same = v2.request !== undefined && (mainRouteShape === undefined || routeShape(v2.request) === mainRouteShape);
|
|
499
|
+
return same ? shipped : { ok: false, refusals: [{ path: null, message: 'другой маршрут' }] };
|
|
500
|
+
};
|
|
501
|
+
for (const subset of subsetsUpTo(knownUnits(known), SUBSET_LIMIT)) {
|
|
502
|
+
const reduced = subset.reduce((args, unit) => withoutPath(args, unit), base);
|
|
503
|
+
if (!acceptSameRoute(reduced).ok) {
|
|
504
|
+
const shipped = acceptShipped(reduced);
|
|
505
|
+
const v2 = shipped.ok ? v2Verdict(reduced, acceptV2, explains) : null;
|
|
506
|
+
if (v2?.request !== undefined && mainRouteShape !== undefined &&
|
|
507
|
+
routeShape(v2.request) !== mainRouteShape && movedVariant.length === 0) movedVariant = subset;
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
legal = growLegalCall(reduced, full, roots, acceptShipped);
|
|
511
|
+
if (legal?.limitExceeded) {
|
|
512
|
+
result.impossible = { reason: 'SHIPPED_COMBINATION_LIMIT', paths: legal.limitExceeded,
|
|
513
|
+
message: `Проверить нельзя: законная комбинация может требовать более ${SUBSET_LIMIT} полей` };
|
|
514
|
+
return result;
|
|
515
|
+
}
|
|
516
|
+
knownRemoved = subset;
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
if (legal === null) {
|
|
521
|
+
if (movedVariant.length > 0) {
|
|
522
|
+
result.impossible = { reason: 'V2_ONLY_VARIANT', paths: movedVariant };
|
|
523
|
+
return result;
|
|
524
|
+
}
|
|
525
|
+
if (initialLimit) {
|
|
526
|
+
result.impossible = { reason: 'SHIPPED_COMBINATION_LIMIT', paths: initialLimit,
|
|
527
|
+
message: `Проверить нельзя: законная комбинация может требовать более ${SUBSET_LIMIT} полей` };
|
|
528
|
+
return result;
|
|
529
|
+
}
|
|
530
|
+
const refusals = acceptShipped(full).refusals ?? [];
|
|
531
|
+
result.impossible = {
|
|
532
|
+
reason: 'SHIPPED_REJECTS_EVERY_SET',
|
|
533
|
+
paths: [...new Set(refusals.map((refusal) => refusal.path ?? '(корень)'))].sort(),
|
|
534
|
+
};
|
|
535
|
+
return result;
|
|
536
|
+
}
|
|
537
|
+
let main = v2Verdict(legal.args, acceptV2, explains);
|
|
538
|
+
if (main.absent) {
|
|
539
|
+
const unknown = main.absent.filter((path) => !shippedKnows(path));
|
|
540
|
+
if (unknown.length > 0) {
|
|
541
|
+
result.impossible = { reason: 'V2_REQUIRES_FIELD_UNKNOWN_TO_SHIPPED', paths: unknown.sort() };
|
|
542
|
+
return result;
|
|
543
|
+
}
|
|
544
|
+
// Поле, которое v2 требует, отгруженный знает, но первый законный набор его снял (конфликт с соседним полем):
|
|
545
|
+
// ищется законный набор, который это поле СОХРАНЯЕТ. Не нашёлся — отгруженный клиент законно шлёт вызов без
|
|
546
|
+
// поля, которое v2 требует: находка.
|
|
547
|
+
const keeping = findLegalCall(full, roots, acceptShipped, main.absent);
|
|
548
|
+
if (keeping?.limitExceeded) {
|
|
549
|
+
result.impossible = { reason: 'SHIPPED_COMBINATION_LIMIT', paths: keeping.limitExceeded,
|
|
550
|
+
message: `Проверить нельзя: сохранение обязательного поля требует перебора более ${SUBSET_LIMIT} полей` };
|
|
551
|
+
return result;
|
|
552
|
+
}
|
|
553
|
+
const retried = keeping === null ? null : v2Verdict(keeping.args, acceptV2, explains);
|
|
554
|
+
if (keeping === null || retried.absent) {
|
|
555
|
+
result.refused.push(...main.refused);
|
|
556
|
+
result.absentKnown.push(...main.absent);
|
|
557
|
+
return result;
|
|
558
|
+
}
|
|
559
|
+
legal = keeping;
|
|
560
|
+
main = retried;
|
|
561
|
+
}
|
|
562
|
+
if (main.pathless) {
|
|
563
|
+
result.refused.push(...main.refused);
|
|
564
|
+
result.pathless.push(...main.pathless);
|
|
565
|
+
return result;
|
|
566
|
+
}
|
|
567
|
+
result.request = main.request;
|
|
568
|
+
result.originalRequest = acceptShipped(legal.args).request;
|
|
569
|
+
result.omitted.push(...knownRemoved.map((path) => ({ path, why: 'SHIPPED_REJECTS_KNOWN' })));
|
|
570
|
+
result.omitted.push(...main.omitted);
|
|
571
|
+
result.refused.push(...main.refused);
|
|
572
|
+
|
|
573
|
+
// Шаг 3: для каждого снятого на шаге 1 пути — законный вызов, который его СОДЕРЖИТ.
|
|
574
|
+
const removable = [...generatedRoots(full, known), ...knownUnits(known)];
|
|
575
|
+
const acceptAlternative = (args) => {
|
|
576
|
+
const shipped = acceptShipped(args);
|
|
577
|
+
return shipped;
|
|
578
|
+
};
|
|
579
|
+
for (const path of legal.removed) {
|
|
580
|
+
const found = findAlternative(full, legal.args, path, removable, acceptAlternative, {
|
|
581
|
+
values: goodValues(path),
|
|
582
|
+
shippedConstraints,
|
|
583
|
+
});
|
|
584
|
+
if (found.kind === 'FOUND') {
|
|
585
|
+
const v2 = v2Verdict(found.args, acceptV2, explains);
|
|
586
|
+
if (v2.absent) {
|
|
587
|
+
result.absentKnown.push(...v2.absent);
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
if (v2.pathless) {
|
|
591
|
+
result.pathless.push(...v2.pathless);
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
594
|
+
result.refused.push(...v2.refused);
|
|
595
|
+
result.alternatives.push({ forPath: path, request: v2.request, omitted: v2.omitted });
|
|
596
|
+
continue;
|
|
597
|
+
}
|
|
598
|
+
result.omitted.push({ path, why: found.kind,
|
|
599
|
+
...(found.kind === 'SEARCH_LIMIT_EXCEEDED'
|
|
600
|
+
? { message: `Проверить нельзя: перебор альтернатив превысил ${SUBSET_LIMIT} поля` } : {}) });
|
|
601
|
+
}
|
|
602
|
+
return result;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Законный вызов, содержащий путь: сначала со значением генератора, затем с каждым годным значением других вариантов.
|
|
607
|
+
* Соседние снятые поля добавляются группами: отдельный путь может быть законен лишь вместе с ними.
|
|
608
|
+
* Не нашёлся при заведомо годном значении (значение генератора проходит ВСЕ объявленные ограничения отгруженной схемы
|
|
609
|
+
* на этом пути, либо значение взято у другого варианта) — отгруженный не принимает поле в этой форме; годного значения
|
|
610
|
+
* нет — подобрать не из чего.
|
|
611
|
+
*/
|
|
612
|
+
function findAlternative(full, mainArgs, path, removable, accept, good) {
|
|
613
|
+
const units = [...new Set([
|
|
614
|
+
...removable.filter((unit) => !coversPath(unit, path)),
|
|
615
|
+
...leavesUnder(full, path),
|
|
616
|
+
])];
|
|
617
|
+
// Альтернатива строится от главного образца с возвращённым путём — ближайший законный вызов, а не произвольный.
|
|
618
|
+
const [generatorValue] = valuesAt(full, path);
|
|
619
|
+
// JSON-реестр иногда описывает массив id как string[], а исполняемый Zod требует UUID.
|
|
620
|
+
// Значение одноимённого одиночного id уже прошло отгруженный фильтр в основном вызове.
|
|
621
|
+
const siblingId = Object.entries(mainArgs).find(([key, value]) =>
|
|
622
|
+
path.endsWith(`${key}s`) && typeof value === 'string' && FORMAT_CHECKS.uuid(value))?.[1];
|
|
623
|
+
const values = [
|
|
624
|
+
...(Array.isArray(generatorValue) && siblingId !== undefined ? [[siblingId]] : []),
|
|
625
|
+
generatorValue,
|
|
626
|
+
...good.values,
|
|
627
|
+
];
|
|
628
|
+
const missing = [...new Set(removable.filter((unit) =>
|
|
629
|
+
unit !== path && !relatedPaths(unit, path) && !hasPath(mainArgs, unit) && hasPath(full, unit)))];
|
|
630
|
+
// Поля отчётов часто законны только при другом значении переключателя (`mode=range`,
|
|
631
|
+
// `report=recipients`). Перебор подмножеств 15 соседних полей сначала расходовал лимит на
|
|
632
|
+
// нерелевантные сочетания. Пробуем каждый объявленный enum отдельно, затем пары enum;
|
|
633
|
+
// отсутствующие обязательные поля добавляем по адресным отказам отгруженного фильтра.
|
|
634
|
+
const enumChoices = [...good.shippedConstraints]
|
|
635
|
+
.filter(([key, constraint]) => key !== path && !key.includes('.') && !key.includes('[]') &&
|
|
636
|
+
typeof constraint.enum === 'string')
|
|
637
|
+
.map(([key, constraint]) => [key, constraint.enum.split('|').filter(Boolean)])
|
|
638
|
+
.sort((left, right) => left[1].length - right[1].length);
|
|
639
|
+
const minimalBase = Object.fromEntries(Object.entries(mainArgs)
|
|
640
|
+
.filter(([key]) => good.shippedConstraints.get(key)?.required === true));
|
|
641
|
+
const enumBases = [minimalBase, mainArgs, ...enumChoices
|
|
642
|
+
.filter(([key]) => hasPath(mainArgs, key))
|
|
643
|
+
.map(([key]) => withoutPath(mainArgs, key))];
|
|
644
|
+
const tryDirected = (candidate) => {
|
|
645
|
+
let current = candidate;
|
|
646
|
+
const seen = new Set();
|
|
647
|
+
for (let round = 0; round < MAX_ROUNDS; round += 1) {
|
|
648
|
+
const signature = JSON.stringify(current);
|
|
649
|
+
if (seen.has(signature)) break;
|
|
650
|
+
seen.add(signature);
|
|
651
|
+
const verdict = accept(current);
|
|
652
|
+
if (verdict.ok) return { kind: 'FOUND', args: current, verdict };
|
|
653
|
+
const required = [...new Set((verdict.refusals ?? [])
|
|
654
|
+
.map((refusal) => refusal.path)
|
|
655
|
+
.filter((missingPath) => missingPath !== null && !hasPath(current, missingPath) &&
|
|
656
|
+
hasPath(full, missingPath)))];
|
|
657
|
+
if (required.length > 0) {
|
|
658
|
+
current = required.reduce((args, missingPath) =>
|
|
659
|
+
withValueAt(args, missingPath, valuesAt(full, missingPath)[0]), current);
|
|
660
|
+
continue;
|
|
661
|
+
}
|
|
662
|
+
const conflicting = [...new Set((verdict.refusals ?? [])
|
|
663
|
+
.map((refusal) => refusal.path)
|
|
664
|
+
.filter((refusedPath) => refusedPath !== null && hasPath(current, refusedPath) &&
|
|
665
|
+
!relatedPaths(refusedPath, path)))];
|
|
666
|
+
if (conflicting.length === 0) break;
|
|
667
|
+
current = conflicting.reduce((args, refusedPath) => withoutPath(args, refusedPath), current);
|
|
668
|
+
}
|
|
669
|
+
return null;
|
|
670
|
+
};
|
|
671
|
+
for (const value of values) {
|
|
672
|
+
if (value === undefined) continue;
|
|
673
|
+
for (const base of enumBases) {
|
|
674
|
+
const start = withValueAt(base, path, value);
|
|
675
|
+
const direct = tryDirected(start);
|
|
676
|
+
if (direct !== null) return direct;
|
|
677
|
+
for (const [firstPath, firstValues] of enumChoices) {
|
|
678
|
+
for (const firstValue of firstValues) {
|
|
679
|
+
const first = withValueAt(start, firstPath, firstValue);
|
|
680
|
+
const oneChange = tryDirected(first);
|
|
681
|
+
if (oneChange !== null) return oneChange;
|
|
682
|
+
for (const [secondPath, secondValues] of enumChoices) {
|
|
683
|
+
if (secondPath === firstPath) continue;
|
|
684
|
+
for (const secondValue of secondValues) {
|
|
685
|
+
const twoChanges = tryDirected(withValueAt(first, secondPath, secondValue));
|
|
686
|
+
if (twoChanges !== null) return twoChanges;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
const scopeChoices = path === 'value'
|
|
694
|
+
? (good.shippedConstraints.get('scope')?.enum ?? '').split('|').filter(Boolean)
|
|
695
|
+
: [];
|
|
696
|
+
const bases = [mainArgs, ...scopeChoices.map((scope) => withValueAt(mainArgs, 'scope', scope))];
|
|
697
|
+
let tried = 0;
|
|
698
|
+
let limitExceeded = false;
|
|
699
|
+
for (const value of values) {
|
|
700
|
+
if (value === undefined) continue;
|
|
701
|
+
for (const base of bases) {
|
|
702
|
+
for (const subset of additionsUpTo(missing, SUBSET_LIMIT)) {
|
|
703
|
+
tried += 1;
|
|
704
|
+
if (tried > ALTERNATIVE_CANDIDATE_LIMIT) return { kind: 'SEARCH_LIMIT_EXCEEDED' };
|
|
705
|
+
const candidate = subset.reduce((args, unit) => {
|
|
706
|
+
const [siblingValue] = valuesAt(full, unit);
|
|
707
|
+
return withValueAt(args, unit, siblingValue);
|
|
708
|
+
}, withValueAt(base, path, value));
|
|
709
|
+
const found = findLegalCall(candidate, units, accept, [path]);
|
|
710
|
+
if (found?.limitExceeded) limitExceeded = true;
|
|
711
|
+
else if (found !== null) return { kind: 'FOUND', args: found.args, verdict: found.verdict };
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
if (limitExceeded || missing.length > SUBSET_LIMIT) return { kind: 'SEARCH_LIMIT_EXCEEDED' };
|
|
716
|
+
return { kind: good.values.length > 0 ? 'SHIPPED_FORBIDS' : 'VALUE_NOT_FOUND' };
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
const CHECKED_CONSTRAINTS = [
|
|
720
|
+
'type',
|
|
721
|
+
'enum',
|
|
722
|
+
'pattern',
|
|
723
|
+
'format',
|
|
724
|
+
'minimum',
|
|
725
|
+
'maximum',
|
|
726
|
+
'minLength',
|
|
727
|
+
'maxLength',
|
|
728
|
+
'minItems',
|
|
729
|
+
'maxItems',
|
|
730
|
+
];
|
|
731
|
+
|
|
732
|
+
/** Проходит ли значение на пути (и всё под ним) объявленные ограничения отгруженной схемы. */
|
|
733
|
+
export function satisfiesShippedSchema(args, path, shippedConstraints) {
|
|
734
|
+
for (const [constraintPath, constraints] of shippedConstraints) {
|
|
735
|
+
if (!coversPath(path, constraintPath)) continue;
|
|
736
|
+
for (const value of valuesAt(args, constraintPath)) {
|
|
737
|
+
for (const kind of CHECKED_CONSTRAINTS) {
|
|
738
|
+
if (constraints[kind] !== undefined && violatesConstraint(kind, constraints, value)) return false;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
return true;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
/**
|
|
746
|
+
* Шаг 0: проверенные аргументы как отгруженный вызов. Поля, которых у отгруженного нет (только ветки v2), снимаются —
|
|
747
|
+
* отгруженный клиент их не пошлёт никогда; внутри свободного объекта (у отгруженного без перечня свойств) ничего не
|
|
748
|
+
* снимается. Исключение — новый переключатель, для которого исполнение v2
|
|
749
|
+
* доказывает смену метода/адреса: его сохраняем, чтобы получить явную невозможность образца.
|
|
750
|
+
*
|
|
751
|
+
* @param {Map<string, object>} shippedPaths пути отгруженной схемы (`schemaPathsOf`)
|
|
752
|
+
*/
|
|
753
|
+
export function prepareKnownArgs(known, shippedPaths, variant = {}) {
|
|
754
|
+
const shippedKeys = [...shippedPaths.keys()];
|
|
755
|
+
const prune = (value, prefix) => {
|
|
756
|
+
if (Array.isArray(value)) return value.map((item) => prune(item, `${prefix}[]`));
|
|
757
|
+
if (!isObject(value)) return value;
|
|
758
|
+
if (prefix !== '' && !shippedKeys.some((path) => path.startsWith(`${prefix}.`))) return value;
|
|
759
|
+
const out = {};
|
|
760
|
+
for (const [key, child] of Object.entries(value)) {
|
|
761
|
+
const path = prefix === '' ? key : `${prefix}.${key}`;
|
|
762
|
+
if (shippedPaths.has(path)) out[key] = prune(child, path);
|
|
763
|
+
}
|
|
764
|
+
return out;
|
|
765
|
+
};
|
|
766
|
+
let prepared = prune(known, '');
|
|
767
|
+
// Неизвестный отгруженной схеме переключатель маршрута нельзя тихо снять:
|
|
768
|
+
// после снятия проверялся бы export вместо inbox. Обычные новые фильтры
|
|
769
|
+
// по-прежнему снимаются. Сохраняем только доказанный прогоном v2 переключатель.
|
|
770
|
+
if (variant.acceptV2 !== undefined) {
|
|
771
|
+
const canonical = variant.acceptV2(known);
|
|
772
|
+
if (canonical.ok && routeShape(canonical.request) === variant.mainRouteShape) {
|
|
773
|
+
for (const path of generatedRoots(known, prepared)) {
|
|
774
|
+
const reduced = variant.acceptV2(prepared);
|
|
775
|
+
const candidate = withValueAt(prepared, path, valuesAt(known, path)[0]);
|
|
776
|
+
const restored = variant.acceptV2(candidate);
|
|
777
|
+
if (reduced.ok && routeShape(reduced.request) !== variant.mainRouteShape &&
|
|
778
|
+
restored.ok && routeShape(restored.request) === variant.mainRouteShape) {
|
|
779
|
+
prepared = candidate;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
return prepared;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* Проверенные значения, которые нарушают ограничение ОТГРУЖЕННОЙ схемы на пути, где сравнение схем объявило
|
|
789
|
+
* расхождение: форма ветки v2, которую отгруженный клиент послать не может.
|
|
790
|
+
*/
|
|
791
|
+
export function knownPathsViolatingShipped(known, schemaLag, shippedConstraints) {
|
|
792
|
+
const paths = new Set();
|
|
793
|
+
for (const row of schemaLag) {
|
|
794
|
+
const [path, kind] = row.path.split('#');
|
|
795
|
+
if (row.kind !== 'CONSTRAINT' || kind === 'required') continue;
|
|
796
|
+
if (valuesAt(known, path).some((value) => violatesConstraint(kind, shippedConstraints.get(path), value))) {
|
|
797
|
+
paths.add(path);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
return [...paths].sort();
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/** Копия проверенных аргументов со значениями генератора на путях. */
|
|
804
|
+
export function withGeneratedValues(known, generated, paths) {
|
|
805
|
+
let out = known;
|
|
806
|
+
for (const path of paths) {
|
|
807
|
+
const [replacement] = valuesAt(generated, path);
|
|
808
|
+
if (replacement !== undefined) out = withValueAt(out, path, replacement);
|
|
809
|
+
}
|
|
810
|
+
return out;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/** Подтверждённое ограничение отгруженной схемы различается даже после предела перебора. */
|
|
814
|
+
export function resolveShippedVariant({ outcome, known, generated, schemaLag, shippedConstraints, build, mainRouteShape }) {
|
|
815
|
+
if (!['SHIPPED_REJECTS_EVERY_SET', 'SHIPPED_COMBINATION_LIMIT'].includes(outcome.impossible?.reason)) return outcome;
|
|
816
|
+
const violating = knownPathsViolatingShipped(known, schemaLag, shippedConstraints);
|
|
817
|
+
if (violating.length === 0) return outcome;
|
|
818
|
+
// build исполняет оба фильтра. Подмена enum допустима только для того же метода и адреса;
|
|
819
|
+
// новый отчёт с отдельным адресом остаётся вариантом, которого отгруженный клиент не имеет.
|
|
820
|
+
const retry = build(withGeneratedValues(known, generated, violating));
|
|
821
|
+
if (retry.request !== undefined && routeShape(retry.request) === mainRouteShape) return retry;
|
|
822
|
+
return {
|
|
823
|
+
...retry,
|
|
824
|
+
request: undefined,
|
|
825
|
+
omitted: [],
|
|
826
|
+
alternatives: [],
|
|
827
|
+
refused: [],
|
|
828
|
+
pathless: [],
|
|
829
|
+
absentKnown: [],
|
|
830
|
+
impossible: { reason: 'V2_ONLY_VARIANT', paths: violating },
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/** Путь в отгруженной JSON-схеме инструмента. */
|
|
835
|
+
export function schemaPathsOf(jsonSchema, label) {
|
|
836
|
+
return schemaConstraints(jsonSchema ?? {}, '', new Map(), 0, jsonSchema ?? {}, label);
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
/** Новые переключатели маршрута имеют точные решения; собравшийся вариант снимает прежнее объяснение. */
|
|
840
|
+
export function variantExtensionFindings({ tools, extensions, shippedSchemas, localSchemas, variantsByTool }) {
|
|
841
|
+
const findings = [];
|
|
842
|
+
const caseKey = (tool) => tool.variant === undefined ? tool.name : `${tool.name}@${tool.variant}`;
|
|
843
|
+
for (const tool of tools) {
|
|
844
|
+
if (tool.shippedSampleImpossible?.reason !== 'V2_ONLY_VARIANT') continue;
|
|
845
|
+
const shipped = shippedSchemas.get(tool.name);
|
|
846
|
+
for (const path of tool.shippedSampleImpossible.paths) {
|
|
847
|
+
if (shipped?.properties?.[path] !== undefined) continue;
|
|
848
|
+
const key = `${caseKey(tool)}::${path}`;
|
|
849
|
+
if (extensions[key]?.kind !== 'V2_ONLY_VARIANT') findings.push(`${key}: нет решения о новом переключателе`);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
for (const [key, row] of Object.entries(extensions)) {
|
|
853
|
+
if (row.kind !== 'V2_ONLY_VARIANT') continue;
|
|
854
|
+
const [variantKey, path] = key.split('::');
|
|
855
|
+
const tool = tools.find((item) => caseKey(item) === variantKey);
|
|
856
|
+
const shipped = shippedSchemas.get(tool?.name);
|
|
857
|
+
const current = localSchemas.get(tool?.name);
|
|
858
|
+
const canonical = variantsByTool.get(tool?.name)?.find((item) => item.variant === tool.variant)?.args;
|
|
859
|
+
if (tool?.shippedSampleImpossible?.reason !== 'V2_ONLY_VARIANT' ||
|
|
860
|
+
!tool.shippedSampleImpossible.paths.includes(path) ||
|
|
861
|
+
shipped?.additionalProperties !== false || shipped.properties?.[path] !== undefined ||
|
|
862
|
+
typeof row.variantValue !== 'string' || canonical?.[path] !== row.variantValue ||
|
|
863
|
+
!current?.properties?.[path]?.enum?.includes(row.variantValue)) {
|
|
864
|
+
findings.push(`${key}: строка нового переключателя протухла или не подтверждена схемами и вариантом`);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
return findings;
|
|
868
|
+
}
|