affinity-sdk 0.9.11__tar.gz → 0.9.14__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-0.9.11 → affinity_sdk-0.9.14}/.github/workflows/ci.yml +10 -5
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.github/workflows/release.yml +2 -2
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.gitignore +1 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.pre-commit-config.yaml +7 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/CHANGELOG.md +43 -1
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/PKG-INFO +3 -1
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/README.md +2 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/commands/company_cmds.py +4 -1
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/commands/list_cmds.py +4 -3
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/commands/person_cmds.py +4 -1
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/commands/task_cmds.py +22 -3
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/interaction_utils.py +140 -20
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/query/executor.py +522 -39
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/query/models.py +16 -2
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/query/output.py +213 -9
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/query/schema.py +45 -3
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/resolve.py +55 -1
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/services/lists.py +55 -13
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/cli/index.md +10 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/guides/query-command.md +57 -1
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/reference/query-language.md +78 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/.claude-plugin/plugin.json +1 -1
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/.claude-plugin/skills/affinity-mcp-workflows/SKILL.md +48 -23
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/.claude-plugin/skills/query-language/SKILL.md +74 -2
- affinity_sdk-0.9.11/mcp/.registry/commands.json → affinity_sdk-0.9.14/mcp/.registry/commands.generated.json +439 -302
- affinity_sdk-0.9.14/mcp/.registry/mcp-commands.json +362 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/.registry/resource-templates.json +1 -1
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/.registry/resources.json +2 -2
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/CHANGELOG.md +71 -8
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/COMPATIBILITY +1 -1
- affinity_sdk-0.9.14/mcp/VERSION +1 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/lib/cli-gateway.sh +38 -25
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/lib/common.sh +3 -2
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/mcp-bash.lock +2 -2
- affinity_sdk-0.9.14/mcp/mcpb.conf +49 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/resources/data-model/data-model.md +29 -2
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/server.d/env.sh +28 -1
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/server.d/health-checks.sh +3 -3
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/server.d/server.meta.json +1 -1
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/tools/discover-commands/tool.sh +25 -3
- affinity_sdk-0.9.14/mcp/tools/execute-read-command/tool.meta.json +36 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/tools/execute-read-command/tool.sh +14 -12
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/tools/execute-write-command/tool.meta.json +1 -1
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/tools/execute-write-command/tool.sh +23 -25
- affinity_sdk-0.9.14/mcp/tools/query/tool.meta.json +104 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/tools/query/tool.sh +52 -8
- affinity_sdk-0.9.14/mcp/user-config.json +35 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/plugins/affinity-sdk/.claude-plugin/plugin.json +1 -1
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/plugins/xaffinity-cli/.claude-plugin/plugin.json +1 -1
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/pyproject.toml +1 -1
- affinity_sdk-0.9.14/tests/cli/test_query_list_entry_relationships.py +643 -0
- affinity_sdk-0.9.14/tests/cli/test_query_list_export_parity.py +319 -0
- affinity_sdk-0.9.14/tests/cli/test_query_output_flattening.py +668 -0
- affinity_sdk-0.9.14/tests/cli/test_query_unreplied_emails.py +251 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/integration/README.md +31 -0
- affinity_sdk-0.9.14/tests/integration/setup_query_parity_data.py +414 -0
- affinity_sdk-0.9.14/tests/integration/test_query_parity_integration.py +1124 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_interaction_utils.py +237 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_query_executor.py +203 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_query_expand_interactions.py +249 -0
- affinity_sdk-0.9.14/tools/generate_mcp_command_registry.py +239 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tools/sync_mcp_registry.py +2 -2
- affinity_sdk-0.9.11/mcp/.registry/commands-metadata.json +0 -114
- affinity_sdk-0.9.11/mcp/VERSION +0 -1
- affinity_sdk-0.9.11/mcp/mcpb.conf +0 -24
- affinity_sdk-0.9.11/mcp/tools/execute-read-command/tool.meta.json +0 -42
- affinity_sdk-0.9.11/mcp/tools/query/tool.meta.json +0 -105
- affinity_sdk-0.9.11/tools/generate_cli_commands_registry.py +0 -200
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.claude-plugin/marketplace.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.editorconfig +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.env.example +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.gitattributes +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.github/CODEOWNERS +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.github/PULL_REQUEST_TEMPLATE/cli_command.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.github/PULL_REQUEST_TEMPLATE/release.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.github/dependabot.yml +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.github/labels.yml +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.github/pull_request_template.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.github/workflows/docs.yml +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.github/workflows/labels.yml +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.github/workflows/linkcheck.yml +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.github/workflows/openapi-validation.yml +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.github/workflows/plugin-build.yml +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.github/workflows/plugin-release.yml +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.github/workflows/release-detect.yml +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/.github/workflows/version-check.yml +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/CODE_OF_CONDUCT.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/CONTRIBUTING.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/LICENSE +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/SECURITY.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/VERSIONING.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/__init__.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/__init__.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/__main__.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/click_compat.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/commands/__init__.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/commands/_entity_files_dump.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/commands/_list_entry_fields.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/commands/_v1_parsing.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/commands/completion_cmd.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/commands/config_cmds.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/commands/entry_cmds.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/commands/field_cmds.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/commands/interaction_cmds.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/commands/note_cmds.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/commands/opportunity_cmds.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/commands/query_cmd.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/commands/relationship_strength_cmds.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/commands/reminder_cmds.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/commands/resolve_url_cmd.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/commands/session_cmds.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/commands/version_cmd.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/commands/whoami_cmd.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/config.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/constants.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/context.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/csv_utils.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/date_utils.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/decorators.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/errors.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/field_utils.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/formatters.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/help_json.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/logging.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/main.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/options.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/paths.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/progress.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/query/__init__.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/query/aggregates.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/query/dates.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/query/exceptions.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/query/filters.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/query/parser.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/query/planner.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/query/progress.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/render.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/resolvers.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/results.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/runner.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/serialization.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/session_cache.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/cli/types.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/client.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/clients/__init__.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/clients/http.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/clients/pipeline.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/compare.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/downloads.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/exceptions.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/filters.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/hooks.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/inbound_webhooks.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/models/__init__.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/models/entities.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/models/pagination.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/models/rate_limit_snapshot.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/models/secondary.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/models/types.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/policies.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/progress.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/py.typed +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/services/__init__.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/services/companies.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/services/opportunities.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/services/persons.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/services/rate_limits.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/services/tasks.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/services/v1_only.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/affinity/types.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/codecov.yml +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/cli-development-guide.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/ai-integrations/index.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/changelog.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/cli/commands.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/cli/scripting.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/cli-reference.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/examples.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/getting-started.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/glossary.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/guides/api-versions-and-routing.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/guides/authentication.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/guides/claude-code-plugins.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/guides/configuration.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/guides/csv-export.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/guides/datetime-handling.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/guides/debugging-hooks.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/guides/errors-and-retries.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/guides/field-types-and-values.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/guides/field-values.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/guides/filtering.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/guides/ids-and-types.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/guides/models.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/guides/opportunity-associations.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/guides/pagination.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/guides/performance.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/guides/rate-limits.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/guides/sync-vs-async.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/guides/webhooks.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/index.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/mcp/index.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/reference/client.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/reference/exceptions.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/reference/filters.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/reference/models.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/reference/services/companies.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/reference/services/lists.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/reference/services/opportunities.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/reference/services/persons.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/reference/services/tasks.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/reference/services/v1/auth.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/reference/services/v1/field-values.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/reference/services/v1/fields.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/reference/services/v1/files.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/reference/services/v1/interactions.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/reference/services/v1/notes.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/reference/services/v1/relationships.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/reference/services/v1/reminders.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/reference/services/v1/webhooks.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/reference/types.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/docs/public/troubleshooting.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/examples/advanced_usage.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/examples/async_lifecycle.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/examples/basic_usage.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/examples/filter_builder.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/examples/hooks_debugging.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/examples/list_management.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/examples/resolve_helpers.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/examples/task_polling.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/.claude-plugin/.gitignore +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/.claude-plugin/.mcp.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/.claude-plugin/xaffinity-mcp.sh +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/.gitignore +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/.registry/prompts.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/Makefile +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/README.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/completions/entity-name.sh +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/completions/list-name.sh +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/completions/status-value.sh +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/docs/DEBUGGING.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/docs/internal/debugging-improvements-plan.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/docs/internal/mcp-bash-progress-timeout-feature-request.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/icon.svg +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/lib/cache.sh +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/lib/entity-types.sh +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/lib/field-resolution.sh +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/prompts/change-status/change-status.meta.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/prompts/change-status/change-status.txt +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/prompts/interaction-brief/interaction-brief.meta.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/prompts/interaction-brief/interaction-brief.txt +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/prompts/log-call/log-call.meta.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/prompts/log-call/log-call.txt +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/prompts/log-interaction-and-update-workflow/log-interaction-and-update-workflow.meta.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/prompts/log-interaction-and-update-workflow/log-interaction-and-update-workflow.txt +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/prompts/log-message/log-message.meta.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/prompts/log-message/log-message.txt +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/prompts/pipeline-review/pipeline-review.meta.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/prompts/pipeline-review/pipeline-review.txt +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/prompts/prepare-briefing/prepare-briefing.meta.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/prompts/prepare-briefing/prepare-briefing.txt +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/prompts/warm-intro/warm-intro.meta.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/prompts/warm-intro/warm-intro.txt +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/providers/xaffinity.sh +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/resources/data-model/data-model.meta.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/resources/field-catalogs/field-catalogs.meta.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/resources/field-catalogs/field-catalogs.sh +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/resources/interaction-enums/interaction-enums.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/resources/interaction-enums/interaction-enums.meta.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/resources/me/me.meta.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/resources/me/me.sh +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/resources/me-person-id/me-person-id.meta.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/resources/me-person-id/me-person-id.sh +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/resources/saved-views/saved-views.meta.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/resources/saved-views/saved-views.sh +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/resources/workflow-config/workflow-config.meta.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/resources/workflow-config/workflow-config.sh +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/scripts/install-framework.sh +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/server.d/policy.sh +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/server.d/requirements.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/test/README.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/test/run.sh +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/test-progress.sh +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/tools/discover-commands/tool.meta.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/tools/get-entity-dossier/tool.meta.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/tools/get-entity-dossier/tool.sh +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/tools/read-xaffinity-resource/tool.meta.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/tools/read-xaffinity-resource/tool.sh +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/xaffinity-mcp-env.sh +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mcp/xaffinity-mcp.sh +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/mkdocs.yml +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/plugins/affinity-sdk/.claude-plugin/skills/affinity-python-sdk/SKILL.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/plugins/xaffinity-cli/.claude-plugin/commands/affinity-help.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/plugins/xaffinity-cli/.claude-plugin/hooks/hooks.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/plugins/xaffinity-cli/.claude-plugin/hooks/pre-xaffinity.sh +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/plugins/xaffinity-cli/.claude-plugin/skills/xaffinity-cli-usage/SKILL.md +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/cli/test_formatters.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/cli/test_output_formats_integration.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/cli/test_query_output_truncation.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/conftest.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/fixtures/webhooks/field_value_updated.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/fixtures/webhooks/list_entry_created.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/fixtures/webhooks/organization_created.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/fixtures/webhooks/organization_merged.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/fixtures/webhooks/person_created.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/fixtures/webhooks/unknown_event.json +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/integration/conftest.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/integration/test_cli_writes.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/integration/test_sdk_reads.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/integration/test_sdk_writes.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/integration/test_sdk_writes_runner.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_affinity_client_wrapper_additional_coverage.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_async_file_download_streaming.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_column_limiting.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_command_context.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_company_cmds_coverage.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_company_get.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_config_cmds.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_csv_export.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_date_params.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_date_utils.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_datetime_formatting.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_datetime_header_offsets.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_domain_linkification.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_entity_files_dump.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_entry_field.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_error_rendering.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_field_history.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_field_value_humanization.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_get_interaction_dates.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_interaction_date_chunking.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_json_safety.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_list_cmds_coverage.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_list_expand_interactions.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_missing_coverage.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_no_network_commands.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_opportunity_cmds.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_opportunity_cmds_coverage.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_pager_logic.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_person_cmds_coverage.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_person_get.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_progress_manager.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_query_aggregates.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_query_cmd.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_query_dates.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_query_filters.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_query_integration.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_query_parser.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_query_planner.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_query_safety.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_query_schema.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_rate_limit_rendering.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_resolve_url_parsing.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_result_summary.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_serialization.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_session_cache.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_types.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_unreplied_email.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_v1_only_cmds.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_v1_parsing.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_cli_write_ops.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_client.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_compare.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_env_helpers.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_exceptions_coverage.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_field_value_type.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_file_download_streaming.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_file_upload_helpers_and_validation.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_filters.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_hooks.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_http_client_additional_coverage.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_http_client_remaining_coverage.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_inbound_webhooks.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_integration_smoke.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_interactions_and_files_regressions.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_isodatetime.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_list_entry_membership_and_opportunities.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_models.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_new_features.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_pagination_iterators.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_public_api.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_raw_pipeline_hook_events.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_requirements_additional.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_sdk_interaction_dates.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_service_get_parameter_combinations.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_services_companies_coverage_gaps.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_services_lists_additional_coverage.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_services_lists_coverage_gaps.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_services_opportunities_additional_coverage.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_services_opportunities_coverage_gaps.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_services_opportunities_tasks_additional_coverage.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_services_persons_companies_additional_coverage.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_services_rate_limits_coverage_gaps.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_services_v1_only_async.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_services_v1_only_coverage_gaps.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_small_modules_coverage.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_transport_and_readonly_mode.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_v1_only_services_additional_coverage.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tests/test_v1_only_services_more_coverage.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tools/check_cli_patterns.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tools/generate_requirements_mapping.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tools/sync_mcp_version.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tools/sync_plugin_version.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/tools/validate_openapi_models.py +0 -0
- {affinity_sdk-0.9.11 → affinity_sdk-0.9.14}/uv.lock +0 -0
|
@@ -84,14 +84,19 @@ jobs:
|
|
|
84
84
|
echo "Skipping traceability mapping (docs/internal not present in repo checkout)."
|
|
85
85
|
fi
|
|
86
86
|
|
|
87
|
-
- name:
|
|
87
|
+
- name: MCP commands registry is valid
|
|
88
88
|
run: |
|
|
89
89
|
# Verify registry file exists and is valid JSON with expected structure
|
|
90
90
|
python -c "
|
|
91
91
|
import json
|
|
92
92
|
import sys
|
|
93
|
-
with open('mcp/.registry/commands.json') as f:
|
|
93
|
+
with open('mcp/.registry/commands.generated.json') as f:
|
|
94
94
|
data = json.load(f)
|
|
95
|
+
# Check _generated metadata (auto-generated indicator)
|
|
96
|
+
assert '_generated' in data, 'Missing _generated metadata'
|
|
97
|
+
assert 'generator' in data['_generated'], 'Missing generator in _generated'
|
|
98
|
+
assert 'cliVersion' in data['_generated'], 'Missing cliVersion in _generated'
|
|
99
|
+
# Check commands structure
|
|
95
100
|
assert 'commands' in data, 'Missing commands key'
|
|
96
101
|
assert isinstance(data['commands'], list), 'commands must be a list'
|
|
97
102
|
assert len(data['commands']) > 0, 'commands list is empty'
|
|
@@ -99,11 +104,11 @@ jobs:
|
|
|
99
104
|
assert 'name' in cmd, f'Command missing name: {cmd}'
|
|
100
105
|
assert 'category' in cmd, f'Command {cmd[\"name\"]} missing category'
|
|
101
106
|
assert cmd['category'] in ('read', 'write', 'local'), f'Invalid category for {cmd[\"name\"]}'
|
|
102
|
-
print(f'Registry valid: {len(data[\"commands\"])} commands')
|
|
107
|
+
print(f'Registry valid: {len(data[\"commands\"])} commands (v{data[\"_generated\"][\"cliVersion\"]})')
|
|
103
108
|
"
|
|
104
109
|
# Regenerate registry and verify it matches committed version
|
|
105
|
-
python tools/
|
|
106
|
-
git diff --exit-code mcp/.registry/commands.json
|
|
110
|
+
python tools/generate_mcp_command_registry.py
|
|
111
|
+
git diff --exit-code mcp/.registry/commands.generated.json
|
|
107
112
|
|
|
108
113
|
test-timezone:
|
|
109
114
|
runs-on: ubuntu-latest
|
|
@@ -159,8 +159,8 @@ jobs:
|
|
|
159
159
|
|
|
160
160
|
- name: Verify registry is up to date
|
|
161
161
|
run: |
|
|
162
|
-
python tools/
|
|
163
|
-
git diff --exit-code mcp/.registry/commands.json
|
|
162
|
+
python tools/generate_mcp_command_registry.py
|
|
163
|
+
git diff --exit-code mcp/.registry/commands.generated.json
|
|
164
164
|
|
|
165
165
|
build:
|
|
166
166
|
runs-on: ubuntu-latest
|
|
@@ -66,3 +66,10 @@ repos:
|
|
|
66
66
|
language: python
|
|
67
67
|
files: ^(mcp/VERSION|mcp/\.claude-plugin/plugin\.json|mcp/server\.d/server\.meta\.json)$
|
|
68
68
|
pass_filenames: false
|
|
69
|
+
- id: pytest-pre-push
|
|
70
|
+
name: Run tests before push
|
|
71
|
+
entry: pytest tests/ -x -q --tb=short
|
|
72
|
+
language: system
|
|
73
|
+
pass_filenames: false
|
|
74
|
+
stages: [pre-push]
|
|
75
|
+
always_run: true
|
|
@@ -7,6 +7,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## 0.9.14 - 2026-01-19
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- CLI: Query `select` clause now automatically includes `expand` fields. Previously, using `select` with `expand` would filter out the expansion data (e.g., `interactionDates`, `unrepliedEmails`), requiring users to explicitly list expansions in `select`.
|
|
14
|
+
|
|
15
|
+
## 0.9.13 - 2026-01-19
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- CLI: Query `include` for `listEntries` now supports `persons`, `companies`, `opportunities`, and `interactions`. Fetches related entities based on list entry entity type (e.g., company entries get associated persons).
|
|
19
|
+
- CLI: Query `expand: ["unrepliedEmails"]` now works with `listEntries`. Checks each list entry's underlying entity for unreplied incoming emails.
|
|
20
|
+
- CLI: Query extended include syntax with parameters:
|
|
21
|
+
- `{include: {interactions: {limit: 50, days: 180}}}` - Limit and lookback control
|
|
22
|
+
- `{include: {opportunities: {list: "Pipeline"}}}` - Scope to specific opportunity list
|
|
23
|
+
- `{include: {persons: {where: {...}}}}` - Filter included entities
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
- CLI: Query TOON format now correctly flattens `fields.*` and `interactionDates` like markdown and CSV formats do. Previously, TOON was missing the `_apply_explicit_flattening()` call, causing nested fields to be truncated and interaction dates to be missing entirely.
|
|
27
|
+
- CLI: `list export --check-unreplied-emails` now works standalone without requiring `--expand`.
|
|
28
|
+
- CLI: `expand: ["interactionDates"]` now always produces 8 canonical columns regardless of data presence, ensuring consistent schema across all records.
|
|
29
|
+
- SDK: Improved error message when adding wrong entity type to a list (e.g., adding a company to a person list). Now provides clear guidance about list type requirements instead of exposing raw API validation error.
|
|
30
|
+
|
|
31
|
+
### Performance
|
|
32
|
+
- CLI: Query `expand: ["interactionDates"]` is significantly faster with parallel person resolution, bounded concurrent fetches (semaphore=10), and parallelized section resolution. Default concurrency increased from 5 to 15 (tunable via `XAFFINITY_QUERY_CONCURRENCY`).
|
|
33
|
+
|
|
34
|
+
## 0.9.12 - 2026-01-17
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
- CLI: Query `listEntries` now normalizes reference field values to display strings:
|
|
38
|
+
- Person fields: `{"firstName": "Jane", "lastName": "Doe"}` → `"Jane Doe"`
|
|
39
|
+
- Company fields: `{"name": "Acme Corp", "id": 123}` → `"Acme Corp"`
|
|
40
|
+
- Multi-person/company fields: Arrays of names instead of raw objects
|
|
41
|
+
- Use `expand` or `include` for full entity data when needed
|
|
42
|
+
- CLI: Query output with explicit `select` on `fields.*` paths now flattens fields to top-level columns in table/CSV/markdown formats. JSON output preserves nested structure.
|
|
43
|
+
|
|
10
44
|
## 0.9.11 - 2026-01-17
|
|
11
45
|
|
|
12
46
|
### Added
|
|
@@ -207,7 +241,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
207
241
|
- CLI: `--limit` alias for `--max-results` on all `ls` commands (LLM-friendly).
|
|
208
242
|
- CLI: Option aliases now included in `--help --json` output.
|
|
209
243
|
|
|
210
|
-
## 0.8.
|
|
244
|
+
## 0.8.2 - 2026-01-10
|
|
245
|
+
|
|
246
|
+
_No user-facing changes. Version bump for PyPI release._
|
|
247
|
+
|
|
248
|
+
## 0.8.1 - 2026-01-10
|
|
249
|
+
|
|
250
|
+
_No user-facing changes. Version bump for PyPI release._
|
|
251
|
+
|
|
252
|
+
## 0.8.0 - 2026-01-10
|
|
211
253
|
|
|
212
254
|
### Changed
|
|
213
255
|
- CLI: Renamed `--json` to `--set-json` on `person field`, `company field`, `opportunity field` commands to avoid conflict with global `--json` output flag.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: affinity-sdk
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.14
|
|
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/
|
|
@@ -126,6 +126,8 @@ Optional: install the CLI:
|
|
|
126
126
|
pipx install "affinity-sdk[cli]"
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
+
The CLI includes a powerful `query` command for structured data extraction with filtering, aggregations, and relationship includes. Output formats include JSON, CSV, markdown, and TOON (token-optimized for LLMs).
|
|
130
|
+
|
|
129
131
|
CLI docs: https://yaniv-golan.github.io/affinity-sdk/latest/cli/
|
|
130
132
|
|
|
131
133
|
### MCP Server
|
|
@@ -69,6 +69,8 @@ Optional: install the CLI:
|
|
|
69
69
|
pipx install "affinity-sdk[cli]"
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
The CLI includes a powerful `query` command for structured data extraction with filtering, aggregations, and relationship includes. Output formats include JSON, CSV, markdown, and TOON (token-optimized for LLMs).
|
|
73
|
+
|
|
72
74
|
CLI docs: https://yaniv-golan.github.io/affinity-sdk/latest/cli/
|
|
73
75
|
|
|
74
76
|
### MCP Server
|
|
@@ -1904,7 +1904,10 @@ def company_merge(
|
|
|
1904
1904
|
primary_id: int,
|
|
1905
1905
|
duplicate_id: int,
|
|
1906
1906
|
) -> None:
|
|
1907
|
-
"""Merge a duplicate company into a primary
|
|
1907
|
+
"""Merge a duplicate company into a primary (beta).
|
|
1908
|
+
|
|
1909
|
+
Returns a taskUrl for tracking progress. Use 'task wait <url>' to wait for completion.
|
|
1910
|
+
"""
|
|
1908
1911
|
|
|
1909
1912
|
def fn(ctx: CLIContext, warnings: list[str]) -> CommandOutput:
|
|
1910
1913
|
client = ctx.get_client(warnings=warnings)
|
|
@@ -1055,8 +1055,8 @@ def list_export(
|
|
|
1055
1055
|
):
|
|
1056
1056
|
next_cursor = page_next_cursor
|
|
1057
1057
|
|
|
1058
|
-
if not want_expand:
|
|
1059
|
-
# No expansion - yield row as-is
|
|
1058
|
+
if not want_expand and not check_unreplied_emails:
|
|
1059
|
+
# No expansion and no unreplied email check - yield row as-is
|
|
1060
1060
|
rows_written += 1
|
|
1061
1061
|
if progress is not None and task_id is not None:
|
|
1062
1062
|
filter_desc = _format_filter_progress(csv_iter_state)
|
|
@@ -1469,7 +1469,8 @@ def list_export(
|
|
|
1469
1469
|
):
|
|
1470
1470
|
next_cursor = page_next_cursor
|
|
1471
1471
|
|
|
1472
|
-
if not want_expand:
|
|
1472
|
+
if not want_expand and not check_unreplied_emails:
|
|
1473
|
+
# No expansion and no unreplied email check - add row as-is
|
|
1473
1474
|
rows.append(row)
|
|
1474
1475
|
if progress is not None and task_id is not None:
|
|
1475
1476
|
filter_desc = _format_filter_progress(table_iter_state)
|
|
@@ -1763,7 +1763,10 @@ def person_merge(
|
|
|
1763
1763
|
primary_id: int,
|
|
1764
1764
|
duplicate_id: int,
|
|
1765
1765
|
) -> None:
|
|
1766
|
-
"""Merge a duplicate person into a primary
|
|
1766
|
+
"""Merge a duplicate person into a primary (beta).
|
|
1767
|
+
|
|
1768
|
+
Returns a taskUrl for tracking progress. Use 'task wait <url>' to wait for completion.
|
|
1769
|
+
"""
|
|
1767
1770
|
|
|
1768
1771
|
def fn(ctx: CLIContext, warnings: list[str]) -> CommandOutput:
|
|
1769
1772
|
client = ctx.get_client(warnings=warnings)
|
|
@@ -13,7 +13,19 @@ from ..serialization import serialize_model_for_cli
|
|
|
13
13
|
|
|
14
14
|
@click.group(name="task", cls=RichGroup)
|
|
15
15
|
def task_group() -> None:
|
|
16
|
-
"""
|
|
16
|
+
"""Poll async tasks (e.g., merges).
|
|
17
|
+
|
|
18
|
+
Some operations like 'company merge' and 'person merge' run asynchronously
|
|
19
|
+
and return a task URL. Use these commands to check status or wait for completion.
|
|
20
|
+
|
|
21
|
+
Example workflow:
|
|
22
|
+
|
|
23
|
+
xaffinity company merge 123 456 --json
|
|
24
|
+
|
|
25
|
+
# Returns {"taskUrl": "https://api.affinity.co/v2/tasks/..."}
|
|
26
|
+
|
|
27
|
+
xaffinity task wait "https://api.affinity.co/v2/tasks/..."
|
|
28
|
+
"""
|
|
17
29
|
|
|
18
30
|
|
|
19
31
|
def _task_payload(task: MergeTask) -> dict[str, object]:
|
|
@@ -26,7 +38,10 @@ def _task_payload(task: MergeTask) -> dict[str, object]:
|
|
|
26
38
|
@output_options
|
|
27
39
|
@click.pass_obj
|
|
28
40
|
def task_get(ctx: CLIContext, task_url: str) -> None:
|
|
29
|
-
"""Get
|
|
41
|
+
"""Get current status of an async task.
|
|
42
|
+
|
|
43
|
+
Returns task status (pending, in_progress, success, failed) without waiting.
|
|
44
|
+
"""
|
|
30
45
|
|
|
31
46
|
def fn(ctx: CLIContext, warnings: list[str]) -> CommandOutput:
|
|
32
47
|
client = ctx.get_client(warnings=warnings)
|
|
@@ -78,7 +93,11 @@ def task_wait(
|
|
|
78
93
|
poll_interval: float,
|
|
79
94
|
max_poll_interval: float,
|
|
80
95
|
) -> None:
|
|
81
|
-
"""Wait for
|
|
96
|
+
"""Wait for an async task to complete.
|
|
97
|
+
|
|
98
|
+
Polls the task URL with exponential backoff until it reaches 'success' or 'failed'.
|
|
99
|
+
Returns the final task status. Raises an error if the task fails or times out.
|
|
100
|
+
"""
|
|
82
101
|
|
|
83
102
|
def fn(ctx: CLIContext, warnings: list[str]) -> CommandOutput:
|
|
84
103
|
client = ctx.get_client(warnings=warnings)
|
|
@@ -10,6 +10,7 @@ This module provides:
|
|
|
10
10
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
|
+
import asyncio
|
|
13
14
|
import logging
|
|
14
15
|
from datetime import datetime, timedelta, timezone
|
|
15
16
|
from typing import TYPE_CHECKING, Any
|
|
@@ -270,13 +271,21 @@ async def _resolve_person_names_async(
|
|
|
270
271
|
client: AsyncAffinity,
|
|
271
272
|
person_ids: list[int],
|
|
272
273
|
cache: dict[int, str] | None = None,
|
|
274
|
+
*,
|
|
275
|
+
person_semaphore: asyncio.Semaphore | None = None,
|
|
273
276
|
) -> list[str]:
|
|
274
277
|
"""Resolve person IDs to names asynchronously, using cache when available.
|
|
275
278
|
|
|
279
|
+
Person fetches run in parallel with bounded concurrency via a SHARED semaphore.
|
|
280
|
+
|
|
276
281
|
Args:
|
|
277
282
|
client: AsyncAffinity client for async API calls
|
|
278
283
|
person_ids: List of person IDs to resolve
|
|
279
284
|
cache: Optional dict cache (mutated in place). Thread-safe under CPython GIL.
|
|
285
|
+
person_semaphore: Optional SHARED semaphore for bounded concurrent fetches.
|
|
286
|
+
IMPORTANT: Pass the same semaphore across all calls to limit total
|
|
287
|
+
concurrent person API calls. Creating a new semaphore per call defeats
|
|
288
|
+
the bounded concurrency purpose.
|
|
280
289
|
|
|
281
290
|
Returns:
|
|
282
291
|
List of person names in same order as input IDs.
|
|
@@ -285,42 +294,56 @@ async def _resolve_person_names_async(
|
|
|
285
294
|
if cache is None:
|
|
286
295
|
cache = {}
|
|
287
296
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
names.append(cache[pid])
|
|
292
|
-
continue
|
|
297
|
+
# NOTE: Benign race possible - two tasks may both see same ID as uncached
|
|
298
|
+
# before either updates cache. Result: duplicate fetch, correct final state.
|
|
299
|
+
uncached_ids = [pid for pid in person_ids if pid not in cache]
|
|
293
300
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
301
|
+
if uncached_ids:
|
|
302
|
+
# Use SHARED semaphore from caller, or create local fallback (for backwards compat)
|
|
303
|
+
sem = person_semaphore or asyncio.Semaphore(10)
|
|
297
304
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
except Exception:
|
|
303
|
-
# Cache as fallback to avoid repeated failures
|
|
304
|
-
cache[pid] = f"Person {pid}"
|
|
305
|
-
names.append(f"Person {pid}")
|
|
305
|
+
async def fetch_person(pid: int) -> None:
|
|
306
|
+
async with sem:
|
|
307
|
+
try:
|
|
308
|
+
from affinity.types import PersonId
|
|
306
309
|
|
|
307
|
-
|
|
310
|
+
person = await client.persons.get(PersonId(pid))
|
|
311
|
+
name = person.full_name or f"Person {pid}"
|
|
312
|
+
cache[pid] = name
|
|
313
|
+
except Exception:
|
|
314
|
+
# Cache as fallback to avoid repeated failures
|
|
315
|
+
cache[pid] = f"Person {pid}"
|
|
316
|
+
|
|
317
|
+
# PERF: Parallelize person fetches with bounded concurrency
|
|
318
|
+
await asyncio.gather(*[fetch_person(pid) for pid in uncached_ids])
|
|
319
|
+
|
|
320
|
+
# Return names in original order
|
|
321
|
+
return [cache.get(pid, f"Person {pid}") for pid in person_ids]
|
|
308
322
|
|
|
309
323
|
|
|
324
|
+
# PERF: section_iteration_boundary
|
|
310
325
|
async def resolve_interaction_names_async(
|
|
311
326
|
client: AsyncAffinity,
|
|
312
327
|
interaction_data: dict[str, Any] | None,
|
|
313
328
|
cache: dict[int, str] | None = None,
|
|
329
|
+
*,
|
|
330
|
+
person_semaphore: asyncio.Semaphore | None = None,
|
|
314
331
|
) -> None:
|
|
315
332
|
"""Resolve teamMemberNames in transformed interaction data asynchronously.
|
|
316
333
|
|
|
317
334
|
Mutates the interaction_data dict in place, adding teamMemberNames
|
|
318
335
|
to any section that has teamMemberIds.
|
|
319
336
|
|
|
337
|
+
Sections (lastMeeting, nextMeeting, lastEmail) are resolved in parallel.
|
|
338
|
+
Person fetches within each section use a SHARED semaphore for bounded concurrency.
|
|
339
|
+
|
|
320
340
|
Args:
|
|
321
341
|
client: AsyncAffinity client for async API calls
|
|
322
342
|
interaction_data: Transformed interaction data from transform_interaction_data()
|
|
323
343
|
cache: Optional dict cache for person names (mutated in place)
|
|
344
|
+
person_semaphore: Optional SHARED semaphore for bounded person resolution.
|
|
345
|
+
If not provided, a local semaphore is created (not recommended for
|
|
346
|
+
multi-record expansion - pass shared semaphore from caller).
|
|
324
347
|
"""
|
|
325
348
|
if interaction_data is None:
|
|
326
349
|
return
|
|
@@ -328,16 +351,21 @@ async def resolve_interaction_names_async(
|
|
|
328
351
|
if cache is None:
|
|
329
352
|
cache = {}
|
|
330
353
|
|
|
331
|
-
|
|
332
|
-
|
|
354
|
+
async def resolve_section(section_key: str) -> None:
|
|
355
|
+
"""Resolve person names for a single section."""
|
|
333
356
|
section = interaction_data.get(section_key)
|
|
334
357
|
if section and "teamMemberIds" in section:
|
|
335
358
|
person_ids = section["teamMemberIds"]
|
|
336
359
|
if person_ids:
|
|
337
360
|
section["teamMemberNames"] = await _resolve_person_names_async(
|
|
338
|
-
client, person_ids, cache
|
|
361
|
+
client, person_ids, cache, person_semaphore=person_semaphore
|
|
339
362
|
)
|
|
340
363
|
|
|
364
|
+
# PERF: Parallelize over sections (lastMeeting, nextMeeting, lastEmail)
|
|
365
|
+
await asyncio.gather(
|
|
366
|
+
*[resolve_section(key) for key in ("lastMeeting", "nextMeeting", "lastEmail")]
|
|
367
|
+
)
|
|
368
|
+
|
|
341
369
|
|
|
342
370
|
# =============================================================================
|
|
343
371
|
# Unreplied Email Detection
|
|
@@ -436,6 +464,98 @@ def check_unreplied_email(
|
|
|
436
464
|
return None
|
|
437
465
|
|
|
438
466
|
|
|
467
|
+
async def async_check_unreplied_email(
|
|
468
|
+
client: AsyncAffinity,
|
|
469
|
+
entity_type: str | int,
|
|
470
|
+
entity_id: int,
|
|
471
|
+
lookback_days: int = 30,
|
|
472
|
+
) -> dict[str, Any] | None:
|
|
473
|
+
"""Async version: Check for unreplied incoming emails for an entity.
|
|
474
|
+
|
|
475
|
+
Supports person, company, and opportunity entity types.
|
|
476
|
+
Also handles V1 integer entityType formats (0=person, 1=company).
|
|
477
|
+
|
|
478
|
+
Args:
|
|
479
|
+
client: AsyncAffinity client for API calls
|
|
480
|
+
entity_type: "company", "person", "opportunity" (or V1 integers 0, 1)
|
|
481
|
+
entity_id: The entity ID
|
|
482
|
+
lookback_days: Number of days to look back for emails (default 30)
|
|
483
|
+
|
|
484
|
+
Returns:
|
|
485
|
+
Dict with unreplied email info if found, None otherwise.
|
|
486
|
+
Example: {
|
|
487
|
+
"date": "2026-01-10T10:00:00Z",
|
|
488
|
+
"daysSince": 5,
|
|
489
|
+
"subject": "Following up on our conversation",
|
|
490
|
+
}
|
|
491
|
+
"""
|
|
492
|
+
from affinity.models.types import InteractionDirection, InteractionType
|
|
493
|
+
from affinity.types import CompanyId, OpportunityId, PersonId
|
|
494
|
+
|
|
495
|
+
try:
|
|
496
|
+
now = datetime.now(timezone.utc)
|
|
497
|
+
start_time = now - timedelta(days=lookback_days)
|
|
498
|
+
|
|
499
|
+
# Build entity-specific filter kwargs
|
|
500
|
+
# Handle V1 (integer) and V2 (string) entityType formats
|
|
501
|
+
iter_kwargs: dict[str, Any] = {
|
|
502
|
+
"type": InteractionType.EMAIL,
|
|
503
|
+
"start_time": start_time,
|
|
504
|
+
"end_time": now,
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if entity_type in ("company", 1, "organization"):
|
|
508
|
+
iter_kwargs["company_id"] = CompanyId(entity_id)
|
|
509
|
+
elif entity_type in ("person", 0):
|
|
510
|
+
iter_kwargs["person_id"] = PersonId(entity_id)
|
|
511
|
+
elif entity_type == "opportunity":
|
|
512
|
+
iter_kwargs["opportunity_id"] = OpportunityId(entity_id)
|
|
513
|
+
else:
|
|
514
|
+
logger.debug(f"Unsupported entity type for unreplied email check: {entity_type}")
|
|
515
|
+
return None
|
|
516
|
+
|
|
517
|
+
# Fetch email interactions for the entity
|
|
518
|
+
emails = []
|
|
519
|
+
async for email in client.interactions.iter(**iter_kwargs):
|
|
520
|
+
emails.append(email)
|
|
521
|
+
|
|
522
|
+
if not emails:
|
|
523
|
+
return None
|
|
524
|
+
|
|
525
|
+
# Sort by date descending (most recent first)
|
|
526
|
+
emails.sort(key=lambda e: e.date, reverse=True)
|
|
527
|
+
|
|
528
|
+
# Find the most recent incoming email
|
|
529
|
+
last_incoming = None
|
|
530
|
+
for email in emails:
|
|
531
|
+
if email.direction == InteractionDirection.INCOMING:
|
|
532
|
+
last_incoming = email
|
|
533
|
+
break
|
|
534
|
+
|
|
535
|
+
if not last_incoming:
|
|
536
|
+
return None
|
|
537
|
+
|
|
538
|
+
# Check if there's an outgoing email after the last incoming
|
|
539
|
+
has_reply = any(
|
|
540
|
+
e.direction == InteractionDirection.OUTGOING and e.date > last_incoming.date
|
|
541
|
+
for e in emails
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
if has_reply:
|
|
545
|
+
return None
|
|
546
|
+
|
|
547
|
+
# Return unreplied email info
|
|
548
|
+
return {
|
|
549
|
+
"date": _format_datetime(last_incoming.date),
|
|
550
|
+
"daysSince": _days_since(last_incoming.date, now),
|
|
551
|
+
"subject": last_incoming.subject,
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
except Exception as e:
|
|
555
|
+
logger.warning(f"Failed to check unreplied emails for {entity_type} {entity_id}: {e}")
|
|
556
|
+
return None
|
|
557
|
+
|
|
558
|
+
|
|
439
559
|
def flatten_unreplied_email_for_csv(unreplied: dict[str, Any] | None) -> dict[str, str]:
|
|
440
560
|
"""Flatten unreplied email data for CSV columns.
|
|
441
561
|
|