affinity-sdk 2.0.7__tar.gz → 2.0.8__tar.gz
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.
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/CHANGELOG.md +35 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/PKG-INFO +1 -1
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/_filtering/pushdown.py +104 -8
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/list_cmds.py +87 -49
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/entity_search.py +4 -4
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/query/executor.py +9 -3
- affinity_sdk-2.0.8/affinity/cli/query/pushdown.py +25 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/compare.py +15 -9
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/cli/commands.md +3 -3
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/filtering.md +24 -14
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/query-command.md +4 -4
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/query-language.md +2 -2
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.claude-plugin/plugin.json +1 -1
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.claude-plugin/skills/query-language/SKILL.md +4 -4
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.claude-plugin/skills/query-language/references/filter-operators.md +1 -1
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.registry/commands.generated.json +2 -2
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/CHANGELOG.md +17 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/COMPATIBILITY +8 -2
- affinity_sdk-2.0.8/mcp/VERSION +1 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/resources/data-model/data-model.md +4 -4
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/server.d/requirements.json +1 -1
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/server.d/server.meta.json +1 -1
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/tools/execute-read-command/tool.meta.json +1 -1
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/tools/query/tool.meta.json +1 -1
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/plugins/xaffinity-cli/.claude-plugin/plugin.json +1 -1
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/plugins/xaffinity-cli/hooks/pre-xaffinity.sh +2 -2
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/plugins/xaffinity-cli/hooks/session-setup.sh +1 -1
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/plugins/xaffinity-cli/skills/xaffinity-cli-usage/SKILL.md +2 -2
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/plugins/xaffinity-cli/skills/xaffinity-cli-usage/references/filtering.md +14 -7
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/plugins/xaffinity-cli/skills/xaffinity-cli-usage/references/query-guide.md +15 -8
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/plugins/xaffinity-cli/skills/xaffinity-cli-usage/references/setup.md +1 -1
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/pyproject.toml +1 -1
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cowork/cassettes/company-lists-status.cassette.json +89 -89
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cowork/cassettes/hook-blocks-without-key.cassette.json +66 -66
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cowork/cassettes/no-key-skill-path.cassette.json +105 -104
- affinity_sdk-2.0.8/tests/cowork/cassettes/status-history.cassette.json +469 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_entity_ls_server_filter.py +87 -0
- affinity_sdk-2.0.8/tests/test_cli_list_export_server_filter.py +661 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_query_executor.py +8 -2
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_query_server_filter.py +191 -5
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_compare.py +1 -1
- affinity_sdk-2.0.8/tests/test_compare_entity_ids.py +159 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_list_entries_server_filter.py +81 -1
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_query_pushdown.py +231 -1
- affinity_sdk-2.0.8/tools/search_golden.py +478 -0
- affinity_sdk-2.0.7/affinity/cli/query/pushdown.py +0 -12
- affinity_sdk-2.0.7/mcp/VERSION +0 -1
- affinity_sdk-2.0.7/tests/cowork/cassettes/status-history.cassette.json +0 -462
- affinity_sdk-2.0.7/tests/test_cli_list_export_server_filter.py +0 -336
- affinity_sdk-2.0.7/tests/test_compare_entity_ids.py +0 -102
- affinity_sdk-2.0.7/tools/search_golden.py +0 -238
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.claude-plugin/marketplace.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.cursor/rules/test-data-privacy.mdc +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.editorconfig +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.env.example +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.gitattributes +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.github/CODEOWNERS +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.github/PULL_REQUEST_TEMPLATE/cli_command.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.github/PULL_REQUEST_TEMPLATE/release.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.github/dependabot.yml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.github/labels.yml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.github/mcp-release-install-footer.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.github/pull_request_template.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.github/release-install-footer.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.github/workflows/ci.yml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.github/workflows/cowork-replay.yml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.github/workflows/docs.yml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.github/workflows/labels.yml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.github/workflows/linkcheck.yml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.github/workflows/openapi-validation.yml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.github/workflows/plugin-build.yml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.github/workflows/plugin-release.yml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.github/workflows/release-detect.yml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.github/workflows/release.yml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.github/workflows/version-check.yml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.gitignore +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/.pre-commit-config.yaml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/AGENTS.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/CODE_OF_CONDUCT.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/CONTRIBUTING.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/LICENSE +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/README.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/SECURITY.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/VERSIONING.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/__init__.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/_filtering/__init__.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/_filtering/dates.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/_filtering/filter_text.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/_filtering/search.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/_filtering/where.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/_internal/__init__.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/_internal/keyfile.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/api_versions.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/__init__.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/__main__.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/_update_worker.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/async_utils.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/click_compat.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/__init__.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/_entity_field.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/_entity_files_dump.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/_entity_files_read.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/_entity_files_upload.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/_list_entry_fields.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/_org_reads.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/_search_common.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/_v1_parsing.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/company_cmds.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/completion_cmd.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/config_cmds.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/discovery_cmds.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/entry_cmds.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/field_cmds.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/file_cmds.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/file_url_cmd.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/interaction_cmds.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/note_cmds.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/opportunity_cmds.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/org_cmds.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/org_read_cmds.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/person_cmds.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/query_cmd.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/relationship_strength_cmds.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/reminder_cmds.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/resolve_url_cmd.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/session_cmds.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/task_cmds.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/transcript_cmds.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/version_cmd.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/webhook_cmds.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/commands/whoami_cmd.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/config.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/confirm.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/constants.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/context.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/csv_utils.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/date_utils.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/decorators.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/errors.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/field_utils.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/formatters.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/help_json.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/interaction_utils.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/list_search.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/logging.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/main.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/mcp_limits.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/options.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/paths.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/progress.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/query/__init__.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/query/aggregates.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/query/cursor.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/query/dates.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/query/exceptions.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/query/filter_text.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/query/filters.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/query/models.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/query/output.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/query/parser.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/query/planner.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/query/progress.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/query/schema.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/render.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/resolve.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/resolvers.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/results.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/runner.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/serialization.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/session_cache.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/types.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/cli/update_check.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/client.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/clients/__init__.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/clients/http.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/clients/pipeline.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/criteria.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/downloads.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/exceptions.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/field_resolve_utils.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/field_resolver.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/filters.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/hooks.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/inbound_webhooks.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/models/__init__.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/models/discovery.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/models/entities.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/models/interactions_v2.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/models/org.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/models/pagination.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/models/rate_limit_snapshot.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/models/relationships_v2.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/models/search.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/models/secondary.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/models/transcripts.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/models/types.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/policies.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/progress.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/py.typed +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/__init__.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/_batch_interactions.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/_discovery.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/_dropdown_options.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/_field_listing.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/_field_updates.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/_note_html.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/_org_reads.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/_search.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/_single_field.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/_v2_filters.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/companies.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/lists.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/opportunities.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/org.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/persons.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/rate_limits.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/reminders.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/search.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/tasks.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/transcripts.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/v1_only.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/services/webhooks.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/affinity/types.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/codecov.yml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/cli-development-guide.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/ai-integrations/index.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/changelog.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/cli/index.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/cli/scripting.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/cli-reference.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/examples.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/getting-started.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/glossary.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/affinity-official-mcp-comparison.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/api-versions-and-routing.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/api-versions.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/authentication.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/claude-code-plugins.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/configuration.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/csv-export.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/datetime-handling.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/debugging-hooks.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/errors-and-retries.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/field-types-and-values.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/field-values.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/ids-and-types.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/migrating-to-2.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/models.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/opportunity-associations.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/pagination.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/performance.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/rate-limits.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/sync-vs-async.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/guides/webhooks.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/index.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/mcp/index.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/api-coverage.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/client.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/exceptions.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/filters.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/models.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/services/companies.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/services/lists.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/services/opportunities.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/services/persons.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/services/reminders.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/services/tasks.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/services/teams-users.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/services/transcripts.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/services/v1/auth.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/services/v1/field-values.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/services/v1/fields.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/services/v1/files.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/services/v1/interactions.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/services/v1/notes.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/services/v1/relationships.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/services/webhooks.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/reference/types.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/docs/public/troubleshooting.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/examples/advanced_usage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/examples/async_lifecycle.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/examples/basic_usage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/examples/filter_builder.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/examples/hooks_debugging.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/examples/list_management.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/examples/resolve_helpers.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/examples/task_polling.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.claude-plugin/.gitignore +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.claude-plugin/skills/affinity-mcp-workflows/SKILL.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.claude-plugin/skills/pipeline-history/SKILL.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.claude-plugin/skills/query-language/references/include-expand.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.claude-plugin/skills/query-language/references/output-formats.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.claude-plugin/skills/query-language/references/quantifiers.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.claude-plugin/xaffinity-mcp.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.gitignore +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/VERSION +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/bin/mcp-bash +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/handlers/completion.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/handlers/lifecycle.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/handlers/logging.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/handlers/ping.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/handlers/prompts.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/handlers/resources.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/handlers/roots.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/handlers/tools.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/auth.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/capabilities.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/cli/common.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/cli/health.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/completion.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/core.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/elicitation.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/file_read.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/handler_helpers.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/hash.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/ids.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/io.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/json.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/lock.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/logging.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/meta_env.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/paginate.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/path.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/policy.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/progress-passthrough.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/progress.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/prompts.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/registry.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/require.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/resource_content.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/resource_match.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/resource_providers.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/resources.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/roots.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/rpc.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/runtime.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/spec.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/timeout.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/tools.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/tools_policy.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/ui-templates.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/ui.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/uri.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/lib/validate.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/providers/echo.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/providers/file.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/providers/git.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/providers/https.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/providers/ui.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/sdk/tool-error.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/sdk/tool-sdk.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/sdk/ui-sdk.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.mcp-bash/vendor.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.registry/mcp-commands.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.registry/prompts.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.registry/resource-templates.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.registry/resources.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/.registry/ui-resources.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/Makefile +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/README.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/completions/entity-name.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/completions/list-name.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/completions/status-value.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/docs/DEBUGGING.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/icon.svg +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/lib/cache.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/lib/cli-detect.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/lib/cli-gateway.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/lib/common.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/lib/entity-types.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/lib/field-resolution.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/mcp-bash.lock +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/mcp-publisher.lock +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/mcpb.conf +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/prompts/change-status/change-status.completion.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/prompts/change-status/change-status.meta.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/prompts/change-status/change-status.txt +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/prompts/interaction-brief/interaction-brief.meta.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/prompts/interaction-brief/interaction-brief.txt +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/prompts/log-call/log-call.meta.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/prompts/log-call/log-call.txt +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/prompts/log-interaction-and-update-workflow/log-interaction-and-update-workflow.meta.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/prompts/log-interaction-and-update-workflow/log-interaction-and-update-workflow.txt +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/prompts/log-message/log-message.meta.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/prompts/log-message/log-message.txt +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/prompts/pipeline-review/pipeline-review.completion.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/prompts/pipeline-review/pipeline-review.meta.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/prompts/pipeline-review/pipeline-review.txt +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/prompts/prepare-briefing/prepare-briefing.meta.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/prompts/prepare-briefing/prepare-briefing.txt +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/prompts/warm-intro/warm-intro.meta.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/prompts/warm-intro/warm-intro.txt +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/providers/xaffinity.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/resources/data-model/data-model.meta.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/resources/field-catalogs/field-catalogs.meta.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/resources/field-catalogs/field-catalogs.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/resources/interaction-enums/interaction-enums.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/resources/interaction-enums/interaction-enums.meta.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/resources/me/me.meta.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/resources/me/me.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/resources/me-person-id/me-person-id.meta.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/resources/me-person-id/me-person-id.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/resources/query-guide/query-guide.meta.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/resources/query-guide/query-guide.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/resources/saved-views/saved-views.meta.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/resources/saved-views/saved-views.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/resources/workflow-config/workflow-config.meta.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/resources/workflow-config/workflow-config.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/resources/workflows-guide/workflows-guide.meta.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/resources/workflows-guide/workflows-guide.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/resources/xaffinity-field-catalogs.completion.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/resources/xaffinity-saved-views.completion.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/resources/xaffinity-workflow-config.completion.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/server.d/env.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/server.d/health-checks.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/server.d/policy.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/server.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/test/README.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/test/run.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/test/test_env_update_check.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/test-progress.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/tools/discover-commands/tool.meta.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/tools/discover-commands/tool.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/tools/execute-read-command/tool.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/tools/execute-write-command/tool.meta.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/tools/execute-write-command/tool.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/tools/get-entity-dossier/tool.meta.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/tools/get-entity-dossier/tool.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/tools/get-file-url/tool.meta.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/tools/get-file-url/tool.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/tools/query/tool.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/tools/read-xaffinity-resource/tool.meta.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/tools/read-xaffinity-resource/tool.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/user-config.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/xaffinity-mcp-env.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mcp/xaffinity-mcp.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/mkdocs.yml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/plugins/affinity-sdk/.claude-plugin/plugin.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/plugins/affinity-sdk/.claude-plugin/skills/affinity-python-sdk/SKILL.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/plugins/affinity-sdk/.claude-plugin/skills/affinity-python-sdk/references/services.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/plugins/xaffinity-cli/hooks/guard-env-read.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/plugins/xaffinity-cli/hooks/hooks.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/plugins/xaffinity-cli/skills/xaffinity-cli-usage/references/files-and-writes.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/plugins/xaffinity-cli/skills/xaffinity-cli-usage/references/history-and-activity.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/plugins/xaffinity-cli/skills/xaffinity-cli-usage/references/interactions.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cli/query/test_cursor.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cli/test_company_create_dedup.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cli/test_company_ls_filter_rejection.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cli/test_formatters.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cli/test_output_formats_integration.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cli/test_person_create_dedup.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cli/test_person_ls_filter_rejection.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cli/test_query_filter_rejection.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cli/test_query_list_entry_relationships.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cli/test_query_list_export_parity.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cli/test_query_output_flattening.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cli/test_query_output_truncation.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cli/test_query_unreplied.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/conftest.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cowork/.cowork-redact.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cowork/README.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cowork/fixtures/workdir-key/README.txt +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cowork/fixtures/workdir-key/xaffinity-test-key +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cowork/fixtures/workdir-nokey/README.txt +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cowork/image/Dockerfile +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cowork/image/fakeaff/build_fixtures.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cowork/image/fakeaff/fixtures.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cowork/image/fakeaff/server.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cowork/image/xaffinity-wrapper.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cowork/privacy-allow.txt +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cowork/record.sh +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cowork/scenarios/company-lists-status.yaml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cowork/scenarios/hook-blocks-without-key.yaml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cowork/scenarios/no-key-skill-path.yaml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cowork/scenarios/status-history.yaml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cowork/sessions/no-key.yaml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/cowork/sessions/with-key.yaml +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/field_write_mocks.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/fixtures/openapi/mini_openapi.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/fixtures/webhooks/field_value_updated.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/fixtures/webhooks/list_entry_created.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/fixtures/webhooks/organization_created.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/fixtures/webhooks/organization_merged.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/fixtures/webhooks/person_created.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/fixtures/webhooks/unknown_event.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/integration/README.md +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/integration/conftest.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/integration/setup_query_parity_data.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/integration/test_cli_writes.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/integration/test_query_parity_integration.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/integration/test_sdk_reads.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/integration/test_sdk_writes.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/integration/test_sdk_writes_runner.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/mcp/__init__.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/mcp/test_query_cursor.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/skill-evals/xaffinity-cli-usage-evals/evals.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/spec_guard.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_affinity_client_wrapper_additional_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_api_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_api_key_file_formats.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_api_key_resolution.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_api_version.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_api_version_per_operation.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_async_file_download_streaming.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_batch_get.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_api_version.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_column_limiting.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_command_context.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_commands_must_fix.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_company_cmds_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_company_get.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_config_cmds.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_context_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_csv_export.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_date_params.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_date_utils.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_datetime_formatting.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_datetime_header_offsets.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_domain_linkification.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_dotenv_resolution.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_dropdown_field_writes.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_enriched_field_writes.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_entity_field_writes.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_entity_files_dump.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_entry_field.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_entry_field_batch_writes.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_entry_field_multi_value_limits.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_error_hints.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_error_rendering.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_field_history.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_field_history_bulk.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_field_history_deleted_options.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_field_options_and_transcripts.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_field_utils.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_field_utils_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_field_utils_set_phase.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_field_value_humanization.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_file_url.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_files_ls.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_files_read.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_get_interaction_dates.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_help_json.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_interaction_date_chunking.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_interaction_utils.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_json_safety.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_list_cmds_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_list_expand_interactions.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_list_export_entity_scope.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_list_export_field_refresh.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_list_export_filter_safety.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_list_export_truncation_meta.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_missing_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_multi_value_truncation_warnings.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_no_network_commands.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_notes_writes.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_opportunity_cmds.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_opportunity_cmds_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_org.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_page_size_validation.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_pager_logic.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_person_cmds_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_person_get.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_plugin_manifest.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_pre_xaffinity_hook.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_progress_manager.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_query_aggregates.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_query_cmd.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_query_cmd_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_query_dates.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_query_expand_interactions.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_query_filters.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_query_integration.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_query_must_fix.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_query_parser.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_query_planner.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_query_review_fixes.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_query_safety.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_query_schema.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_rate_limit_rendering.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_rate_limit_throttle.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_reminders.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_render_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_repeated_filter.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_resolve_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_resolve_url_parsing.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_result_summary.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_safe_field_writes.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_search.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_serialization.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_session_cache.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_session_setup_hook.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_set_phase_atomicity.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_types.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_unreplied.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_update_check.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_v1_only_cmds.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_v1_parsing.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_webhooks.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_cli_write_ops.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_client.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_companies_create_dedup.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_companies_filter_rejection.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_duplicates_connections.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_env_helpers.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_exceptions_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_field_resolver.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_field_service_caching.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_field_types_c4.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_field_value_type.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_field_values_get_value.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_file_batch_get.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_file_download_streaming.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_file_list_batch.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_file_upload_helpers_and_validation.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_files_upload_returning_files.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_files_v2.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_filters.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_generate_mcp_command_registry.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_get_fields_filter_includes.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_get_fields_pagination.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_get_first.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_hidden_field_values.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_hooks.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_http_client_additional_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_http_client_remaining_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_inbound_webhooks.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_integration_smoke.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_interaction_date_validation.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_interactions_and_files_regressions.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_isodatetime.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_list_entry_membership_and_opportunities.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_list_term.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_lists_v2_writes.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_mcp_bundle_env_and_completions.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_mcp_cli_version_check.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_mcp_entity_dossier.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_mcp_gateway_limits.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_mcp_history_bulk_gateway.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_mcp_limits.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_mcp_must_fix.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_mcp_query_cli_error.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_mcp_read_truncation.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_mcp_resource_scripts.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_models.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_models_v2_2026_fields.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_multi_value_limits.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_new_features.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_notes_v2.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_notes_v2_writes.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_opportunities_filter_rejection.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_org_reads.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_pagination_iterators.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_persons_create_dedup.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_persons_filter_rejection.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_public_api.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_rate_limit_v2.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_raw_pipeline_hook_events.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_readonly_factory.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_reminder_list_batch.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_reminders_v2.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_requirements_additional.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_review_fixes_sdk_cli.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_sdk_interaction_dates.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_sdk_must_fix.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_search.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_search_v2.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_service_get_parameter_combinations.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_services_batch_get_interaction_dates.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_services_companies_coverage_gaps.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_services_dropdown_options.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_services_entity_field_updates.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_services_entity_list_entries.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_services_field_value_changes.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_services_list_entries_filter_paths.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_services_lists_additional_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_services_lists_coverage_gaps.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_services_opportunities_additional_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_services_opportunities_coverage_gaps.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_services_opportunities_tasks_additional_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_services_pages_cursor_guard.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_services_persons_companies_additional_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_services_rate_limits_coverage_gaps.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_services_v1_only_async.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_services_v1_only_coverage_gaps.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_single_field_v2.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_small_modules_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_spec_guard.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_teams_users_feedback.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_transport_and_readonly_mode.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_v1_association_parsing.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_v1_only_services_additional_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_v1_only_services_more_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_v2_request_bodies.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_validate_openapi_tool.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/test_webhooks_v2.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/unit/__init__.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tests/unit/test_exceptions.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tools/__init__.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tools/api_coverage.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tools/api_coverage.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tools/api_coverage_v1_snapshot.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tools/check_cli_patterns.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tools/generate_mcp_command_registry.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tools/generate_release_notes.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tools/generate_requirements_mapping.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tools/openapi_snapshots/2024-01-01.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tools/openapi_snapshots/2026-07-15.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tools/openapi_snapshots/2026-09-17.json +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tools/sync_mcp_bundle_env.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tools/sync_mcp_registry.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tools/sync_mcp_version.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tools/validate_mcp_registry_metadata.py +0 -0
- {affinity_sdk-2.0.7 → affinity_sdk-2.0.8}/tools/validate_openapi_models.py +0 -0
|
@@ -5,6 +5,41 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.0.8] - 2026-11-05
|
|
9
|
+
|
|
10
|
+
**Exception to semantic versioning** (as 2.0.2–2.0.7): one fix changes which rows a filter
|
|
11
|
+
returns. It is marked **Behavior change**.
|
|
12
|
+
|
|
13
|
+
### Highlights
|
|
14
|
+
|
|
15
|
+
Filters on a person or company field by id (`Owner = 12345`) now run on Affinity's side for list
|
|
16
|
+
entries, so `list export` needs no `--all` and downloads only the matching rows. A number on a
|
|
17
|
+
record reference is its id only.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- **Behavior change (correctness fix). CLI and SDK (`--filter`, `query`, `affinity.filters`,
|
|
22
|
+
`list(filter=...)`): an integer or 1–20 digits on a person, company or other record-reference
|
|
23
|
+
field matches the record's id only.** It no longer matches a record whose name is those digits;
|
|
24
|
+
use `starts_word` to find such a name. (As before, a reference with no name, such as a person
|
|
25
|
+
without a first and last name or a company with an empty name, can't be matched by id.)
|
|
26
|
+
- CLI and SDK: on list entries, `=` and `in` with record ids on a person or company field, and
|
|
27
|
+
`=`, `in`, `has_any` and `has_all` with ids on a multi-person or multi-company field, go to
|
|
28
|
+
Affinity's search. Such filters need no `--all` / `--max-results` and download only matching
|
|
29
|
+
rows. Ids above 2147483647, names, `!=`, enriched person and company fields, and filters on
|
|
30
|
+
`company ls` / `person ls` / `query` from companies or persons are still checked locally.
|
|
31
|
+
- CLI: a `starts_word` with no letter (`starts_word "360"`) is checked locally instead of being
|
|
32
|
+
sent as Affinity's name search. On `list export` it needs a scope flag; on `company ls`,
|
|
33
|
+
`person ls` and `query` from companies or persons it needs another field condition (or use
|
|
34
|
+
`--query`).
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- CLI: `list export --filter` with a filter Affinity's search refuses (for example a key without
|
|
39
|
+
the "Export data from Lists" permission), run without `--all`, `--max-results` or
|
|
40
|
+
`--first-page-only`, exits 2 before any output. It used to print only the first page, as if it
|
|
41
|
+
were the whole result. `details.refused` carries Affinity's reason.
|
|
42
|
+
|
|
8
43
|
## [2.0.7] - 2026-11-03
|
|
9
44
|
|
|
10
45
|
**Exception to semantic versioning** (as 2.0.2–2.0.6): two fixes change which rows a filter
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: affinity-sdk
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.8
|
|
4
4
|
Summary: A modern, strongly-typed Python SDK for the Affinity CRM API
|
|
5
5
|
Project-URL: Homepage, https://github.com/yaniv-golan/affinity-sdk
|
|
6
6
|
Project-URL: Documentation, https://yaniv-golan.github.io/affinity-sdk/latest/
|
|
@@ -21,12 +21,28 @@ from dataclasses import dataclass, field
|
|
|
21
21
|
from datetime import date, datetime, timedelta
|
|
22
22
|
from typing import Any
|
|
23
23
|
|
|
24
|
-
from ..compare import compare_values
|
|
24
|
+
from ..compare import _as_entity_id, compare_values
|
|
25
25
|
from ..criteria import Condition, FilterGroup, filter_value_type
|
|
26
26
|
from ..models.entities import DropdownOption, FieldMetadata
|
|
27
27
|
from .dates import parse_date_value
|
|
28
28
|
from .where import WhereClause
|
|
29
29
|
|
|
30
|
+
# Person and company id conditions (list entries only): ``=`` / ``in`` on a single field,
|
|
31
|
+
# membership and set conditions on a multi field. Always in ALL_MAPPINGS, so the golden keeps
|
|
32
|
+
# testing them; only the pairs that passed it are in VERIFIED.
|
|
33
|
+
REFERENCE_MAPPINGS: frozenset[tuple[str, str]] = frozenset(
|
|
34
|
+
{
|
|
35
|
+
("person", "is-any-of"),
|
|
36
|
+
("company", "is-any-of"),
|
|
37
|
+
("person-multi", "has-any-of"),
|
|
38
|
+
("person-multi", "has-all-of"),
|
|
39
|
+
("company-multi", "has-any-of"),
|
|
40
|
+
("company-multi", "has-all-of"),
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
# Largest record id sent. Affinity's search refused ids above this in the golden run.
|
|
44
|
+
ID_MAX = 2**31 - 1
|
|
45
|
+
|
|
30
46
|
# (filter value type, server operator) pairs confirmed by tools/search_golden.py: no record the
|
|
31
47
|
# local filter keeps was missing from the server's answer (5 lists, about 3,750 entries,
|
|
32
48
|
# 2026-10-13). Enriched fields and shared field names were excluded (they stay local).
|
|
@@ -84,8 +100,16 @@ VERIFIED: frozenset[tuple[str, str]] = frozenset(
|
|
|
84
100
|
("location", "is-not-empty"),
|
|
85
101
|
("location-multi", "is-empty"),
|
|
86
102
|
("location-multi", "is-not-empty"),
|
|
103
|
+
# Fifth run (person and company ids on list entries, 6 lists, about 12,300 entries,
|
|
104
|
+
# 2026-11-04; the single-company field on a list of its own, 12 cases): no record
|
|
105
|
+
# missing from the server's answer.
|
|
106
|
+
*REFERENCE_MAPPINGS,
|
|
87
107
|
}
|
|
88
108
|
)
|
|
109
|
+
# What goes to the company and person search endpoints: the golden run can only check list
|
|
110
|
+
# entries (the company directory can't be downloaded to compute an independent local set), so
|
|
111
|
+
# id conditions on person and company fields stay local there.
|
|
112
|
+
ENTITY_VERIFIED: frozenset[tuple[str, str]] = VERIFIED - REFERENCE_MAPPINGS
|
|
89
113
|
|
|
90
114
|
_NUMBER_OPS = {
|
|
91
115
|
"eq": "is-equal-to",
|
|
@@ -125,6 +149,7 @@ ALL_MAPPINGS: frozenset[tuple[str, str]] = frozenset(
|
|
|
125
149
|
for op in ("has-any-of", "has-all-of", "has-none-of")
|
|
126
150
|
}
|
|
127
151
|
| {("number", "is-not-equal-to")}
|
|
152
|
+
| REFERENCE_MAPPINGS
|
|
128
153
|
)
|
|
129
154
|
|
|
130
155
|
_DROPDOWN = {"dropdown", "ranked-dropdown"}
|
|
@@ -232,12 +257,14 @@ def split_where(
|
|
|
232
257
|
|
|
233
258
|
def _plain_term(value: Any) -> bool:
|
|
234
259
|
"""A name search term Affinity's search matches like ``starts_word``: letters, digits and
|
|
235
|
-
spaces, 3+ characters (a term with punctuation, e.g. a placeholder
|
|
236
|
-
differently; golden test, 2026-10-13
|
|
260
|
+
spaces, 3+ characters with at least one letter (a term with punctuation, e.g. a placeholder
|
|
261
|
+
name "- -", can match differently; golden test, 2026-10-13. A number is never sent: the
|
|
262
|
+
search may read it as a phone number or an id)."""
|
|
237
263
|
return (
|
|
238
264
|
isinstance(value, str)
|
|
239
265
|
and re.fullmatch(r"[^\W_]+(?: [^\W_]+)*", value.strip()) is not None
|
|
240
266
|
and len(value.strip()) >= 3
|
|
267
|
+
and any(c.isalpha() for c in value)
|
|
241
268
|
)
|
|
242
269
|
|
|
243
270
|
|
|
@@ -338,17 +365,24 @@ def _field(w: WhereClause, ctx: _Ctx) -> tuple[FieldMetadata, str] | None:
|
|
|
338
365
|
return meta, vt
|
|
339
366
|
|
|
340
367
|
|
|
341
|
-
def
|
|
342
|
-
|
|
343
|
-
) -> Condition | None:
|
|
368
|
+
def _refused(w: WhereClause, meta: FieldMetadata, vt: str, op: str, ctx: _Ctx) -> bool:
|
|
369
|
+
"""True (with the reason noted) when the pair isn't verified or the field doesn't take it."""
|
|
344
370
|
if (vt, op) not in ctx.verified:
|
|
345
371
|
ctx.local.append(f"{_describe(w)}: {vt} {op} not verified against the API yet")
|
|
346
|
-
return
|
|
372
|
+
return True
|
|
347
373
|
allowed = [
|
|
348
374
|
str(o.get("id")) for o in (meta.filterability or {}).get("operators") or [] if o.get("id")
|
|
349
375
|
]
|
|
350
376
|
if "filterability" in meta.model_fields_set and allowed and op not in allowed:
|
|
351
377
|
ctx.local.append(f"{_describe(w)}: the field doesn't take {op}")
|
|
378
|
+
return True
|
|
379
|
+
return False
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def _emit(
|
|
383
|
+
w: WhereClause, meta: FieldMetadata, vt: str, op: str, value: Any, ctx: _Ctx
|
|
384
|
+
) -> Condition | None:
|
|
385
|
+
if _refused(w, meta, vt, op, ctx):
|
|
352
386
|
return None
|
|
353
387
|
if isinstance(value, list) and not 1 <= len(value) <= MAX_VALUES:
|
|
354
388
|
ctx.local.append(f"{_describe(w)}: needs 1 to {MAX_VALUES} values on the server")
|
|
@@ -399,8 +433,13 @@ def _condition(w: WhereClause, ctx: _Ctx) -> Condition | None:
|
|
|
399
433
|
)
|
|
400
434
|
return None
|
|
401
435
|
if vt in _NAMED and op == "starts_word":
|
|
402
|
-
|
|
436
|
+
if isinstance(value, str) and re.fullmatch(r"[0-9 ]+", value.strip()):
|
|
437
|
+
ctx.local.append(f"{_describe(w)}: a number isn't sent as a name search")
|
|
438
|
+
else:
|
|
439
|
+
ctx.local.append(f"{_describe(w)}: one name search per query (the rest stay local)")
|
|
403
440
|
return None
|
|
441
|
+
if vt in _NAMED and op in ("eq", "in", "has_any", "has_all"):
|
|
442
|
+
return _reference(w, meta, vt, ctx)
|
|
404
443
|
if vt in _DROPDOWN:
|
|
405
444
|
return _dropdown(w, meta, vt, ctx)
|
|
406
445
|
if vt in ("dropdown-multi", "filterable-text-multi"):
|
|
@@ -423,6 +462,63 @@ def _condition(w: WhereClause, ctx: _Ctx) -> Condition | None:
|
|
|
423
462
|
return None
|
|
424
463
|
|
|
425
464
|
|
|
465
|
+
_NAMES_REASON = "names are matched locally (use the id to filter on Affinity's side)"
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def _valid_ids(value: list[Any]) -> list[int] | str:
|
|
469
|
+
"""The deduplicated record ids in ``value`` (order kept), or the reason they stay local:
|
|
470
|
+
any value that isn't id-shaped (a name) first, then any id outside ``1..ID_MAX``."""
|
|
471
|
+
ids = [_as_entity_id(v) for v in value]
|
|
472
|
+
if any(i is None for i in ids):
|
|
473
|
+
return _NAMES_REASON
|
|
474
|
+
valid = [i for i in ids if i is not None]
|
|
475
|
+
if any(not 1 <= i <= ID_MAX for i in valid):
|
|
476
|
+
return "not a valid record id"
|
|
477
|
+
return list(dict.fromkeys(valid))
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
def _reference_operator(op: str, value: Any, multi: bool) -> str | None:
|
|
481
|
+
"""The server operator for a person or company condition, or ``None`` when none answers it
|
|
482
|
+
(locally ``in`` / ``has_any`` / ``has_all`` on a single field or with a scalar are False,
|
|
483
|
+
so pushing them would make the filter complete and return nothing)."""
|
|
484
|
+
as_list = isinstance(value, list)
|
|
485
|
+
if multi:
|
|
486
|
+
if (op in ("in", "has_any") and as_list) or (op == "eq" and not as_list):
|
|
487
|
+
return "has-any-of"
|
|
488
|
+
if op in ("has_all", "eq") and as_list:
|
|
489
|
+
return "has-all-of"
|
|
490
|
+
return None
|
|
491
|
+
if (op == "eq" and not as_list) or (op == "in" and as_list):
|
|
492
|
+
return "is-any-of"
|
|
493
|
+
return None
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def _reference(w: WhereClause, meta: FieldMetadata, vt: str, ctx: _Ctx) -> Condition | None:
|
|
497
|
+
"""Record ids on a person or company field: the server matches the id, the local filter
|
|
498
|
+
(which runs afterwards on every record) matches the id too (a number is an id only)."""
|
|
499
|
+
op, value = w.op or "", w.value
|
|
500
|
+
if meta.type == "enriched":
|
|
501
|
+
# Enriched values were never checked with these mappings, and search reads them
|
|
502
|
+
# differently from the entries API
|
|
503
|
+
ctx.local.append(f"{_describe(w)}: Affinity enriched fields stay local")
|
|
504
|
+
return None
|
|
505
|
+
server_op = None if value is None else _reference_operator(op, value, vt.endswith("-multi"))
|
|
506
|
+
if server_op is None:
|
|
507
|
+
ctx.local.append(f"{_describe(w)}: no server mapping for {vt} {op}")
|
|
508
|
+
return None
|
|
509
|
+
if _refused(w, meta, vt, server_op, ctx):
|
|
510
|
+
return None
|
|
511
|
+
items = value if isinstance(value, list) else [value]
|
|
512
|
+
if any(item is None for item in items):
|
|
513
|
+
ctx.local.append(f"{_describe(w)}: no server mapping for {vt} {op}")
|
|
514
|
+
return None
|
|
515
|
+
ids = _valid_ids(items)
|
|
516
|
+
if isinstance(ids, str):
|
|
517
|
+
ctx.local.append(f"{_describe(w)}: {ids}")
|
|
518
|
+
return None
|
|
519
|
+
return _emit(w, meta, vt, server_op, ids, ctx)
|
|
520
|
+
|
|
521
|
+
|
|
426
522
|
def _dropdown(w: WhereClause, meta: FieldMetadata, vt: str, ctx: _Ctx) -> Condition | None:
|
|
427
523
|
"""Locally a dropdown value is the option's text, so map the text test onto option ids."""
|
|
428
524
|
op, value = w.op, w.value
|
|
@@ -17,14 +17,15 @@ from affinity.exceptions import (
|
|
|
17
17
|
AuthorizationError,
|
|
18
18
|
ClientSideFilterWarning,
|
|
19
19
|
NotFoundError,
|
|
20
|
+
UnsupportedOperationError,
|
|
20
21
|
)
|
|
21
22
|
from affinity.exceptions import ValidationError as SDKValidationError
|
|
22
23
|
from affinity.filters import FilterExpression
|
|
23
24
|
from affinity.filters import parse as parse_filter
|
|
24
25
|
from affinity.models.entities import FieldMetadata, ListCreate, ListEntryWithEntity
|
|
25
|
-
from affinity.models.pagination import FilterStats
|
|
26
|
+
from affinity.models.pagination import FilterStats, PaginatedResponse
|
|
26
27
|
from affinity.models.types import InteractionType, ListType
|
|
27
|
-
from affinity.services.lists import _entry_to_filter_dict
|
|
28
|
+
from affinity.services.lists import ListEntryService, _entry_to_filter_dict
|
|
28
29
|
from affinity.types import (
|
|
29
30
|
AnyFieldId,
|
|
30
31
|
CompanyId,
|
|
@@ -1149,6 +1150,33 @@ def list_export(
|
|
|
1149
1150
|
api_called=True,
|
|
1150
1151
|
)
|
|
1151
1152
|
|
|
1153
|
+
# Open Affinity's search here, before any output: a refusal must not surface halfway
|
|
1154
|
+
# through a CSV (its header is already written) or leave a silent first page.
|
|
1155
|
+
search_body: dict[str, Any] | None = list_search.body if list_search else None
|
|
1156
|
+
opened_page: PaginatedResponse[ListEntryWithEntity] | None = None
|
|
1157
|
+
if search_body is not None and filter_expr and not saved_view and cursor is None:
|
|
1158
|
+
try:
|
|
1159
|
+
opened_page = _open_search(
|
|
1160
|
+
client.lists.entries(list_id),
|
|
1161
|
+
search_body,
|
|
1162
|
+
list(dict.fromkeys([*selected_field_ids, *filter_field_ids])),
|
|
1163
|
+
page_size,
|
|
1164
|
+
)
|
|
1165
|
+
except (
|
|
1166
|
+
AuthorizationError,
|
|
1167
|
+
NotFoundError,
|
|
1168
|
+
SDKValidationError,
|
|
1169
|
+
UnsupportedOperationError,
|
|
1170
|
+
) as e:
|
|
1171
|
+
if not (all_pages or max_results is not None or first_page_only):
|
|
1172
|
+
raise _filter_scope_error(
|
|
1173
|
+
filter_expr, list_search.local if list_search else [], reason=str(e)
|
|
1174
|
+
) from e
|
|
1175
|
+
warnings.append(f"Affinity's search refused the filter, filtering locally: {e}")
|
|
1176
|
+
if server_filter is not None:
|
|
1177
|
+
server_filter["fallback"] = e.message
|
|
1178
|
+
search_body = None
|
|
1179
|
+
|
|
1152
1180
|
# Build expand field data structures from parsed_expand_fields
|
|
1153
1181
|
# - expand_field_ids: list of field IDs for API calls
|
|
1154
1182
|
# - field_id_to_display: dict mapping field ID (str) -> display name (original spec)
|
|
@@ -1338,7 +1366,8 @@ def list_export(
|
|
|
1338
1366
|
state=csv_iter_state,
|
|
1339
1367
|
cache=cache,
|
|
1340
1368
|
filter_progress_callback=filter_callback,
|
|
1341
|
-
search=
|
|
1369
|
+
search=search_body,
|
|
1370
|
+
opened_page=opened_page,
|
|
1342
1371
|
filter_field_ids=filter_field_ids,
|
|
1343
1372
|
):
|
|
1344
1373
|
next_cursor = page_next_cursor
|
|
@@ -1664,7 +1693,6 @@ def list_export(
|
|
|
1664
1693
|
# only reach the JSON envelope, which CSV never builds.
|
|
1665
1694
|
for msg in [
|
|
1666
1695
|
*warnings,
|
|
1667
|
-
*csv_iter_state.get("warnings", []),
|
|
1668
1696
|
*truncation_warnings(
|
|
1669
1697
|
csv_iter_state.get("truncatedFields", []), filtered=bool(filter_expr)
|
|
1670
1698
|
),
|
|
@@ -1776,7 +1804,8 @@ def list_export(
|
|
|
1776
1804
|
state=table_iter_state,
|
|
1777
1805
|
cache=cache,
|
|
1778
1806
|
filter_progress_callback=json_filter_callback,
|
|
1779
|
-
search=
|
|
1807
|
+
search=search_body,
|
|
1808
|
+
opened_page=opened_page,
|
|
1780
1809
|
filter_field_ids=filter_field_ids,
|
|
1781
1810
|
):
|
|
1782
1811
|
next_cursor = page_next_cursor
|
|
@@ -1919,9 +1948,6 @@ def list_export(
|
|
|
1919
1948
|
warnings.append("Results limited by --max-results. Use --all to fetch all results.")
|
|
1920
1949
|
from ..field_utils import truncation_warnings
|
|
1921
1950
|
|
|
1922
|
-
warnings.extend(table_iter_state.get("warnings", []))
|
|
1923
|
-
if server_filter is not None and table_iter_state.get("searchFallback"):
|
|
1924
|
-
server_filter["fallback"] = table_iter_state["searchFallback"]
|
|
1925
1951
|
warnings.extend(
|
|
1926
1952
|
truncation_warnings(
|
|
1927
1953
|
table_iter_state.get("truncatedFields", []), filtered=bool(filter_expr)
|
|
@@ -2115,6 +2141,7 @@ def _iterate_list_entries(
|
|
|
2115
2141
|
cache: Any = None,
|
|
2116
2142
|
filter_progress_callback: Callable[[FilterStats], None] | None = None,
|
|
2117
2143
|
search: dict[str, Any] | None = None,
|
|
2144
|
+
opened_page: PaginatedResponse[ListEntryWithEntity] | None = None,
|
|
2118
2145
|
filter_field_ids: list[str] | None = None,
|
|
2119
2146
|
) -> Any:
|
|
2120
2147
|
"""
|
|
@@ -2196,38 +2223,27 @@ def _iterate_list_entries(
|
|
|
2196
2223
|
else None
|
|
2197
2224
|
)
|
|
2198
2225
|
if decoded is not None or (search is not None and filter_expr):
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
first = entries.find_body(body, cursor=decoded.url)
|
|
2204
|
-
else:
|
|
2205
|
-
assert search is not None and filter_expr
|
|
2206
|
-
body, local_filter = search, parse_filter(filter_expr)
|
|
2207
|
-
first = entries.find_body(body, field_ids=fetch_ids, limit=page_size)
|
|
2208
|
-
except (AuthorizationError, NotFoundError, SDKValidationError) as e:
|
|
2209
|
-
if decoded is not None:
|
|
2210
|
-
raise
|
|
2211
|
-
if state is not None:
|
|
2212
|
-
state.setdefault("warnings", []).append(
|
|
2213
|
-
f"Affinity's search refused the filter, filtering locally: {e}"
|
|
2214
|
-
)
|
|
2215
|
-
state["searchFallback"] = getattr(e, "message", None) or str(e)
|
|
2226
|
+
if decoded is not None:
|
|
2227
|
+
assert decoded.body is not None
|
|
2228
|
+
body, local_filter = decoded.body, decoded.filter
|
|
2229
|
+
first = entries.find_body(body, cursor=decoded.url)
|
|
2216
2230
|
else:
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
+
assert search is not None and filter_expr and opened_page is not None
|
|
2232
|
+
body, local_filter, first = search, parse_filter(filter_expr), opened_page
|
|
2233
|
+
yield from _search_rows(
|
|
2234
|
+
entries,
|
|
2235
|
+
first,
|
|
2236
|
+
body=body,
|
|
2237
|
+
local_filter=local_filter,
|
|
2238
|
+
selected_field_ids=selected_field_ids,
|
|
2239
|
+
field_by_id=field_by_id,
|
|
2240
|
+
key_mode=key_mode,
|
|
2241
|
+
max_results=max_results,
|
|
2242
|
+
all_pages=all_pages,
|
|
2243
|
+
state=state,
|
|
2244
|
+
filter_progress_callback=filter_progress_callback,
|
|
2245
|
+
)
|
|
2246
|
+
return
|
|
2231
2247
|
|
|
2232
2248
|
pages = (
|
|
2233
2249
|
entries.pages(cursor=cursor)
|
|
@@ -2300,17 +2316,39 @@ def _filter_names_list_fields(filter_expr: str) -> bool:
|
|
|
2300
2316
|
return False
|
|
2301
2317
|
|
|
2302
2318
|
|
|
2303
|
-
def
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2319
|
+
def _open_search(
|
|
2320
|
+
entries: ListEntryService,
|
|
2321
|
+
body: dict[str, Any],
|
|
2322
|
+
field_ids: list[str],
|
|
2323
|
+
page_size: int,
|
|
2324
|
+
) -> PaginatedResponse[ListEntryWithEntity]:
|
|
2325
|
+
"""First page of Affinity's list entry search (raises when Affinity refuses the filter)."""
|
|
2326
|
+
return entries.find_body(body, field_ids=field_ids, limit=page_size)
|
|
2327
|
+
|
|
2328
|
+
|
|
2329
|
+
def _filter_scope_error(filter_expr: str, local: list[str], reason: str | None = None) -> CLIError:
|
|
2330
|
+
scope_flags = (
|
|
2331
|
+
"Add --all to scan everything, --max-results N to cap, or --first-page-only to opt in."
|
|
2313
2332
|
)
|
|
2333
|
+
if reason is None:
|
|
2334
|
+
message = (
|
|
2335
|
+
"Part of this --filter runs locally on fetched pages, so without a scope flag the "
|
|
2336
|
+
f"export would silently return only first-page matches. {scope_flags}"
|
|
2337
|
+
)
|
|
2338
|
+
hint = (
|
|
2339
|
+
"details.local names the conditions Affinity's search can't apply; --saved-view "
|
|
2340
|
+
"filters large lists on Affinity's side."
|
|
2341
|
+
)
|
|
2342
|
+
details: dict[str, Any] = {"rejected_filter": str(filter_expr), "local": local}
|
|
2343
|
+
else:
|
|
2344
|
+
message = (
|
|
2345
|
+
f"Affinity's search refused this --filter ({reason}), so it would run locally on "
|
|
2346
|
+
"fetched pages, and without a scope flag the export would silently return only "
|
|
2347
|
+
f"first-page matches. {scope_flags}"
|
|
2348
|
+
)
|
|
2349
|
+
hint = "Affinity refused the search; add --all or --max-results to filter locally."
|
|
2350
|
+
details = {"rejected_filter": str(filter_expr), "local": local, "refused": reason}
|
|
2351
|
+
return CLIError(message, exit_code=2, error_type="usage_error", hint=hint, details=details)
|
|
2314
2352
|
|
|
2315
2353
|
|
|
2316
2354
|
def _search_rows(
|
|
@@ -21,7 +21,7 @@ from affinity.filters import FilterExpression
|
|
|
21
21
|
from affinity.services._search import SEARCH_INCLUDES
|
|
22
22
|
|
|
23
23
|
from .query.models import WhereClause
|
|
24
|
-
from .query.pushdown import fields_index, split_where
|
|
24
|
+
from .query.pushdown import ENTITY_VERIFIED, fields_index, split_where
|
|
25
25
|
|
|
26
26
|
# Record attributes (not fields) a company or person filter can test.
|
|
27
27
|
COMPANY_KEYS = frozenset({"id", "name", "domain", "domains"})
|
|
@@ -57,7 +57,7 @@ def plan_entity_search(service: Any, where: WhereClause) -> EntitySearch:
|
|
|
57
57
|
options[key] = list(service.get_field_dropdown_options(meta.id))
|
|
58
58
|
return options[key]
|
|
59
59
|
|
|
60
|
-
split = split_where(where, fields, options_for, enriched="as-is")
|
|
60
|
+
split = split_where(where, fields, options_for, verified=ENTITY_VERIFIED, enriched="as-is")
|
|
61
61
|
index = fields_index(fields)
|
|
62
62
|
field_ids = sorted(
|
|
63
63
|
{
|
|
@@ -94,8 +94,8 @@ def _paths(where: WhereClause) -> set[str]:
|
|
|
94
94
|
|
|
95
95
|
def entity_filter_dict(entity: Any) -> dict[str, Any]:
|
|
96
96
|
"""What a ``--filter`` on companies or persons sees: field values by name (dropdowns as
|
|
97
|
-
text, persons and companies as names
|
|
98
|
-
attributes."""
|
|
97
|
+
text, persons and companies as names; a number in a filter matches their id) plus the
|
|
98
|
+
record's own attributes."""
|
|
99
99
|
result: dict[str, Any] = {}
|
|
100
100
|
by_id: dict[str, Any] = {}
|
|
101
101
|
for item in getattr(entity, "fields_raw", None) or []:
|
|
@@ -271,7 +271,7 @@ def _normalize_list_entry_fields(
|
|
|
271
271
|
extracted.append(item)
|
|
272
272
|
normalized_fields[field_name] = extracted
|
|
273
273
|
# Handle person reference: {"firstName": "Jane", "lastName": "Doe"}
|
|
274
|
-
# (names carry the record id: a filter
|
|
274
|
+
# (names carry the record id: a number in a filter matches the id only)
|
|
275
275
|
elif isinstance(data, dict) and ("firstName" in data or "lastName" in data):
|
|
276
276
|
normalized_fields[field_name] = entity_name(
|
|
277
277
|
_extract_person_display_name(data), data.get("id")
|
|
@@ -1887,7 +1887,7 @@ class QueryExecutor:
|
|
|
1887
1887
|
would mean reading Affinity's whole directory."""
|
|
1888
1888
|
from affinity.services._search import SEARCH_INCLUDES
|
|
1889
1889
|
|
|
1890
|
-
from .pushdown import fields_index, split_where
|
|
1890
|
+
from .pushdown import ENTITY_VERIFIED, fields_index, split_where
|
|
1891
1891
|
|
|
1892
1892
|
where = ctx.query.where
|
|
1893
1893
|
if entity not in ("companies", "persons"):
|
|
@@ -1915,7 +1915,13 @@ class QueryExecutor:
|
|
|
1915
1915
|
if meta is not None:
|
|
1916
1916
|
field_ids.add(str(meta.id))
|
|
1917
1917
|
self._search_field_ids = sorted(field_ids)
|
|
1918
|
-
split = split_where(
|
|
1918
|
+
split = split_where(
|
|
1919
|
+
where,
|
|
1920
|
+
fields,
|
|
1921
|
+
lambda m: options.get(str(m.id), []),
|
|
1922
|
+
verified=ENTITY_VERIFIED,
|
|
1923
|
+
enriched="as-is",
|
|
1924
|
+
)
|
|
1919
1925
|
if split.errors:
|
|
1920
1926
|
raise QueryValidationError("; ".join(split.errors), field="where")
|
|
1921
1927
|
if names and not split.server_side:
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Server-side filter split (moved to :mod:`affinity._filtering.pushdown`; re-exported)."""
|
|
2
|
+
|
|
3
|
+
from ..._filtering.pushdown import (
|
|
4
|
+
ALL_MAPPINGS,
|
|
5
|
+
ENRICHED,
|
|
6
|
+
ENTITY_VERIFIED,
|
|
7
|
+
ID_MAX,
|
|
8
|
+
REFERENCE_MAPPINGS,
|
|
9
|
+
VERIFIED,
|
|
10
|
+
Pushdown,
|
|
11
|
+
fields_index,
|
|
12
|
+
split_where,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"ALL_MAPPINGS",
|
|
17
|
+
"ENRICHED",
|
|
18
|
+
"ENTITY_VERIFIED",
|
|
19
|
+
"ID_MAX",
|
|
20
|
+
"REFERENCE_MAPPINGS",
|
|
21
|
+
"VERIFIED",
|
|
22
|
+
"Pushdown",
|
|
23
|
+
"fields_index",
|
|
24
|
+
"split_where",
|
|
25
|
+
]
|
|
@@ -18,7 +18,8 @@ from ._filtering.dates import as_utc, parse_date_value, parse_iso_datetime
|
|
|
18
18
|
|
|
19
19
|
class EntityName(str):
|
|
20
20
|
"""A person's or company's name (or another record reference's) that also carries the
|
|
21
|
-
record's id
|
|
21
|
+
record's id: ``Owner = "Jane Doe"`` matches the name, ``Owner = 123`` the id (a number is
|
|
22
|
+
the id only; it never matches a name made of digits).
|
|
22
23
|
|
|
23
24
|
It is a ``str`` everywhere else (output, sorting, text operators)."""
|
|
24
25
|
|
|
@@ -49,7 +50,8 @@ def entity_name(value: Any, entity_id: Any) -> Any:
|
|
|
49
50
|
|
|
50
51
|
|
|
51
52
|
def _as_entity_id(value: Any) -> int | None:
|
|
52
|
-
"""A filter value read as a record id: an int, or a string of ASCII digits
|
|
53
|
+
"""A filter value read as a record id: an int, or a string of 1-20 ASCII digits (surrounding
|
|
54
|
+
spaces ignored)."""
|
|
53
55
|
if isinstance(value, bool):
|
|
54
56
|
return None
|
|
55
57
|
if isinstance(value, int):
|
|
@@ -68,7 +70,7 @@ def normalize_value(value: Any) -> Any:
|
|
|
68
70
|
- Dropdowns become their text: {"text": "Active"} -> "Active"
|
|
69
71
|
- Persons become their name: {"firstName": "Jane", "lastName": "Doe"} -> "Jane Doe"
|
|
70
72
|
- Companies become their name: {"name": "Acme", "domain": ...} -> "Acme"
|
|
71
|
-
(as :class:`EntityName`, carrying the record id, so a number matches the id)
|
|
73
|
+
(as :class:`EntityName`, carrying the record id, so a number matches the id only)
|
|
72
74
|
- Multi-value arrays become lists of those: [{"text": "A"}, {"text": "B"}] -> ["A", "B"]
|
|
73
75
|
- Scalars pass through unchanged
|
|
74
76
|
|
|
@@ -226,19 +228,23 @@ def _eq(a: Any, b: Any) -> bool:
|
|
|
226
228
|
|
|
227
229
|
def _scalar_eq(a: Any, b: Any) -> bool:
|
|
228
230
|
"""Scalar equality as ``eq`` uses it, and ``in`` / ``has_any`` / ``has_all`` per element:
|
|
229
|
-
|
|
230
|
-
bool, then by string (``5`` matches ``"5"``).
|
|
231
|
+
an :class:`EntityName` against an id-shaped value is an id comparison only; otherwise
|
|
232
|
+
direct, then ``"true"``/``"false"`` against a bool, then by string (``5`` matches ``"5"``).
|
|
233
|
+
``a`` is the field value."""
|
|
231
234
|
if a is None or b is None:
|
|
232
235
|
return a is b
|
|
233
236
|
|
|
237
|
+
# A record reference (person, company, list...) against a number or digit string is its
|
|
238
|
+
# id, never a name made of digits (Owner = 123 does not match a person named "123")
|
|
239
|
+
if isinstance(a, EntityName) and a.entity_id is not None:
|
|
240
|
+
record_id = _as_entity_id(b)
|
|
241
|
+
if record_id is not None:
|
|
242
|
+
return record_id == a.entity_id
|
|
243
|
+
|
|
234
244
|
# Try direct comparison first
|
|
235
245
|
if a == b:
|
|
236
246
|
return True
|
|
237
247
|
|
|
238
|
-
# A person or company also matches its id (Owner = 123)
|
|
239
|
-
if isinstance(a, EntityName) and a.entity_id is not None and _as_entity_id(b) == a.entity_id:
|
|
240
|
-
return True
|
|
241
|
-
|
|
242
248
|
# Handle boolean coercion: filter string "true"/"false" should match Python bool
|
|
243
249
|
# Filter parser outputs lowercase "true"/"false", Python str(True) is "True"
|
|
244
250
|
if isinstance(a, bool) and isinstance(b, str):
|
|
@@ -278,7 +278,7 @@ Options:
|
|
|
278
278
|
- `--csv`: output as CSV (to stdout)
|
|
279
279
|
- `--csv-bom`: add UTF-8 BOM for Excel compatibility
|
|
280
280
|
|
|
281
|
-
**Note:** `--filter` goes through Affinity's search: the conditions on person fields that Affinity can answer are applied on its side, and the whole filter (including attributes like `firstName` or `primaryEmail`) then runs on what comes back. A filter with only attribute conditions is refused; search names and emails with `--query`. When part of the filter runs locally, `--all` or `--max-results` is required. Filters on enriched fields warn that a few records may be missed. The next-page cursor of a filtered run starts with `xsearch2:` and `--cursor` alone resumes it. `--query` and `--filter` can't be combined. With `--json`, `meta.serverFilter` lists the conditions Affinity applied (`pushed`) and those checked locally (`local`); a `--cursor` resume doesn't report it.
|
|
281
|
+
**Note:** `--filter` goes through Affinity's search: the conditions on person fields that Affinity can answer are applied on its side, and the whole filter (including attributes like `firstName` or `primaryEmail`) then runs on what comes back. A filter with only attribute conditions is refused; search names and emails with `--query`. When part of the filter runs locally, `--all` or `--max-results` is required. Filters on enriched fields warn that a few records may be missed. Id conditions on person and company fields (`Mentor = 9`) are checked locally here, and a `starts_word` made only of digits needs another field condition (or use `--query`). The next-page cursor of a filtered run starts with `xsearch2:` and `--cursor` alone resumes it. `--query` and `--filter` can't be combined. With `--json`, `meta.serverFilter` lists the conditions Affinity applied (`pushed`) and those checked locally (`local`); a `--cursor` resume doesn't report it.
|
|
282
282
|
|
|
283
283
|
**Note:** `--query` can be combined with `--field` and `--field-type` to search with field data.
|
|
284
284
|
|
|
@@ -461,7 +461,7 @@ Options:
|
|
|
461
461
|
- `--csv`: output as CSV (to stdout)
|
|
462
462
|
- `--csv-bom`: add UTF-8 BOM for Excel compatibility
|
|
463
463
|
|
|
464
|
-
**Note:** `--filter` goes through Affinity's search: the conditions on company fields that Affinity can answer are applied on its side, and the whole filter (including attributes like `name` or `domain`) then runs on what comes back. A filter with only attribute conditions (e.g. `name =~ "Acme"`) is refused, since it would mean reading Affinity's whole company directory; search names and domains with `--query`. When part of the filter runs locally, `--all` or `--max-results` is required. Filters on enriched fields warn that a few records may be missed. The next-page cursor of a filtered run starts with `xsearch2:` and `--cursor` alone resumes it. `--query` and `--filter` can't be combined. With `--json`, `meta.serverFilter` lists the conditions Affinity applied (`pushed`) and those checked locally (`local`); a `--cursor` resume doesn't report it.
|
|
464
|
+
**Note:** `--filter` goes through Affinity's search: the conditions on company fields that Affinity can answer are applied on its side, and the whole filter (including attributes like `name` or `domain`) then runs on what comes back. A filter with only attribute conditions (e.g. `name =~ "Acme"`) is refused, since it would mean reading Affinity's whole company directory; search names and domains with `--query`. When part of the filter runs locally, `--all` or `--max-results` is required. Filters on enriched fields warn that a few records may be missed. Id conditions on person and company fields (`Mentor = 9`) are checked locally here, and a `starts_word` made only of digits needs another field condition (or use `--query`). The next-page cursor of a filtered run starts with `xsearch2:` and `--cursor` alone resumes it. `--query` and `--filter` can't be combined. With `--json`, `meta.serverFilter` lists the conditions Affinity applied (`pushed`) and those checked locally (`local`); a `--cursor` resume doesn't report it.
|
|
465
465
|
|
|
466
466
|
**Note:** `--query` can be combined with `--field` and `--field-type` to search with field data.
|
|
467
467
|
|
|
@@ -849,7 +849,7 @@ Options:
|
|
|
849
849
|
- `--unreplied-types <types>`: Comma-separated types to check: email, chat, all (default: email,chat)
|
|
850
850
|
- `--unreplied-lookback-days <days>`: Lookback period for unreplied message detection (default: 30)
|
|
851
851
|
|
|
852
|
-
**`--filter` and Affinity's search.** Conditions on list fields that Affinity's search can answer are applied on its side, so only matching entries are downloaded; the whole filter still runs locally on what comes back. When Affinity applies the whole filter, `--all`, `--max-results` and `--first-page-only` are not required (page one is the first page of matches). When part of the filter runs locally, one of them is required, otherwise the command exits with code 2 and the error's `details.local` names the conditions that stay local. The next-page cursor of a filtered export starts with `xsearch2:` and carries the search, so `--cursor` alone resumes it. If Affinity refuses the search (e.g. the API key lacks the "Export data from Lists" permission), the export reads the whole list and filters locally, with a warning. With `--json`, `meta.serverFilter` reports the split (in `--dry-run`, `data.serverFilter`): `pushed` (conditions Affinity's search applied), `local` (what runs only on your machine, with reasons) and, when the search was refused or turned off, `fallback` (why; the whole filter then ran locally). A `--cursor` resume doesn't report it. CSV and table output report the split as warnings on stderr. `XAFFINITY_QUERY_SERVER_FILTER=0` turns server filtering off for list entries (`query` on listEntries and `list export`); company and person filters always use it. `--saved-view` also filters on Affinity's side; for one known company or person use `--company-id` / `--person-id`. See [Filtering](../guides/filtering.md#filtering-on-affinitys-side) for which conditions Affinity applies.
|
|
852
|
+
**`--filter` and Affinity's search.** Conditions on list fields that Affinity's search can answer are applied on its side, so only matching entries are downloaded; the whole filter still runs locally on what comes back. When Affinity applies the whole filter, `--all`, `--max-results` and `--first-page-only` are not required (page one is the first page of matches). When part of the filter runs locally, one of them is required, otherwise the command exits with code 2 and the error's `details.local` names the conditions that stay local. The next-page cursor of a filtered export starts with `xsearch2:` and carries the search, so `--cursor` alone resumes it. If Affinity refuses the search (e.g. the API key lacks the "Export data from Lists" permission), the export reads the whole list and filters locally, with a warning, when `--all`, `--max-results` or `--first-page-only` is given; without one the command exits with code 2 before any output (`details.refused` carries the refusal). Id conditions on person and company fields (`Owner = 12345`, ids up to 2147483647) are applied on Affinity's side; names, `!=` and enriched fields of those types run locally, and a `starts_word` made only of digits runs locally too, so it needs a scope flag. With `--json`, `meta.serverFilter` reports the split (in `--dry-run`, `data.serverFilter`): `pushed` (conditions Affinity's search applied), `local` (what runs only on your machine, with reasons) and, when the search was refused or turned off, `fallback` (why; the whole filter then ran locally). A `--cursor` resume doesn't report it. CSV and table output report the split as warnings on stderr. `XAFFINITY_QUERY_SERVER_FILTER=0` turns server filtering off for list entries (`query` on listEntries and `list export`); company and person filters always use it. `--saved-view` also filters on Affinity's side; for one known company or person use `--company-id` / `--person-id`. See [Filtering](../guides/filtering.md#filtering-on-affinitys-side) for which conditions Affinity applies.
|
|
853
853
|
|
|
854
854
|
**Truncation envelope.** JSON output includes a `meta` object with `truncated` (boolean) and, when `truncated=true`, a `truncationReason` string naming the cause (`firstPageOnly` here; `field history-bulk` uses `partialFailure`). Always check this before treating a response as complete.
|
|
855
855
|
|