asap-protocol 0.3.0__tar.gz → 0.5.0__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.
- asap_protocol-0.5.0/.cursor/dev-planning/code-review/v0.5.0/sprint-s3-code-review.md +284 -0
- asap_protocol-0.5.0/.cursor/dev-planning/code-review/v0.5.0/sprint-s4-code-review.md +99 -0
- asap_protocol-0.5.0/.cursor/dev-planning/code-review/v0.5.0/sprint-s5-code-review.md +353 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/prd/prd-v1-roadmap.md +73 -11
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/tasks/v0.5.0/tasks-v0.5.0-roadmap.md +133 -74
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/tasks/v0.5.0/tasks-v0.5.0-s3-detailed.md +105 -80
- asap_protocol-0.5.0/.cursor/dev-planning/tasks/v0.5.0/tasks-v0.5.0-s4-detailed.md +323 -0
- asap_protocol-0.5.0/.cursor/dev-planning/tasks/v0.5.0/tasks-v0.5.0-s5-detailed.md +561 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/tasks/v1.0.0/tasks-v1.0.0-docs-detailed.md +0 -1
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/tasks/v1.0.0/tasks-v1.0.0-dx-detailed.md +0 -1
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/tasks/v1.0.0/tasks-v1.0.0-observability-detailed.md +0 -1
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/tasks/v1.0.0/tasks-v1.0.0-performance-detailed.md +0 -1
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/tasks/v1.0.0/tasks-v1.0.0-release-detailed.md +0 -1
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/tasks/v1.0.0/tasks-v1.0.0-roadmap.md +6 -17
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/tasks/v1.0.0/tasks-v1.0.0-security-detailed.md +12 -2
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/tasks/v1.0.0/tasks-v1.0.0-testing-detailed.md +0 -1
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.github/ISSUE_TEMPLATE/bug_report.yml +1 -1
- asap_protocol-0.5.0/.github/README.md +95 -0
- asap_protocol-0.5.0/.github/release-notes-v0.5.0.md +61 -0
- asap_protocol-0.5.0/.github/workflows/release.yml +88 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/CHANGELOG.md +85 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/PKG-INFO +22 -5
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/README.md +21 -4
- asap_protocol-0.5.0/docs/error-handling.md +376 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/docs/migration.md +144 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/docs/security.md +402 -38
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/docs/transport.md +165 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/pyproject.toml +6 -1
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/__init__.py +1 -1
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/errors.py +167 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/examples/README.md +3 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/examples/run_demo.py +9 -2
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/models/__init__.py +4 -0
- asap_protocol-0.5.0/src/asap/models/constants.py +88 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/models/entities.py +38 -2
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/models/envelope.py +7 -1
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/transport/__init__.py +3 -0
- asap_protocol-0.5.0/src/asap/transport/circuit_breaker.py +193 -0
- asap_protocol-0.5.0/src/asap/transport/client.py +934 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/transport/middleware.py +6 -5
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/transport/server.py +80 -3
- asap_protocol-0.5.0/src/asap/transport/validators.py +324 -0
- asap_protocol-0.5.0/src/asap/utils/__init__.py +7 -0
- asap_protocol-0.5.0/src/asap/utils/sanitization.py +139 -0
- asap_protocol-0.5.0/tests/compatibility/__init__.py +1 -0
- asap_protocol-0.5.0/tests/compatibility/test_v0_1_0_compatibility.py +95 -0
- asap_protocol-0.5.0/tests/compatibility/test_v0_3_0_compatibility.py +119 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/e2e/test_two_agents.py +3 -1
- asap_protocol-0.5.0/tests/examples/test_run_demo.py +169 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/models/test_entities.py +141 -7
- asap_protocol-0.5.0/tests/models/test_payloads_validation.py +84 -0
- asap_protocol-0.5.0/tests/test_cli_edge_cases.py +113 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/test_version.py +1 -1
- asap_protocol-0.5.0/tests/transport/integration/test_validation_order.py +272 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/transport/test_client.py +221 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/transport/test_middleware.py +86 -15
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/transport/test_server.py +85 -0
- asap_protocol-0.5.0/tests/transport/unit/test_circuit_breaker.py +391 -0
- asap_protocol-0.5.0/tests/transport/unit/test_circuit_breaker_persistence.py +72 -0
- asap_protocol-0.5.0/tests/transport/unit/test_client_coverage_gaps.py +300 -0
- asap_protocol-0.5.0/tests/transport/unit/test_retry_backoff.py +513 -0
- asap_protocol-0.5.0/tests/transport/unit/test_retry_edge_cases.py +134 -0
- asap_protocol-0.5.0/tests/transport/unit/test_validators.py +302 -0
- asap_protocol-0.5.0/tests/transport/unit/test_validators_edge_cases.py +115 -0
- asap_protocol-0.5.0/tests/utils/__init__.py +3 -0
- asap_protocol-0.5.0/tests/utils/test_sanitization.py +153 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/uv.lock +33 -1
- asap_protocol-0.3.0/.cursor/dev-planning/code-review/v0.1.0/pr-8-review-temp.md +0 -45
- asap_protocol-0.3.0/.cursor/dev-planning/tasks/v0.5.0/README-v0.5.0-tasks.md +0 -91
- asap_protocol-0.3.0/.cursor/dev-planning/tasks/v0.5.0/tasks-v0.5.0-s4-detailed.md +0 -209
- asap_protocol-0.3.0/.cursor/dev-planning/tasks/v0.5.0/tasks-v0.5.0-s5-detailed.md +0 -254
- asap_protocol-0.3.0/.cursor/dev-planning/tasks/v1.0.0/README-v1.0.0-tasks.md +0 -81
- asap_protocol-0.3.0/.github/workflows/release.yml +0 -36
- asap_protocol-0.3.0/docs/error-handling.md +0 -49
- asap_protocol-0.3.0/src/asap/models/constants.py +0 -15
- asap_protocol-0.3.0/src/asap/transport/client.py +0 -399
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/commands/code-quality-review.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/commands/create-prd.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/commands/generate-tasks.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/commands/reflect.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/commands/security-pr-review.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/commands/security-review.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/commands/task-list-development.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/commands/test-coverage-review.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/code-review/security-review-report.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/code-review/v0.1.0/pre-pypi-release-review.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/code-review/v0.1.0/sprint3-code-review.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/code-review/v0.1.0/sprint4-code-review.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/code-review/v0.1.0/sprint5-code-review.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/code-review/v0.5.0/sprint-s1-code-review.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/code-review/v0.5.0/sprint-s2-code-review.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/code-review/v0.5.0/sprint-s2.5-code-review.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/prd/prd-asap-implementation.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/prd/prd-review-schedule.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/tasks/v0.1.0/sprint-planning-v0.1.0.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/tasks/v0.1.0/tasks-prd-asap-implementation.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/tasks/v0.1.0/tasks-security-review-report.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/tasks/v0.1.0/tasks-sprint3-improvements.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/tasks/v0.5.0/tasks-v0.5.0-s1-detailed.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/tasks/v0.5.0/tasks-v0.5.0-s2-detailed.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/dev-planning/tasks/v0.5.0/tasks-v0.5.0-s2.5-detailed.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/docs/asap-protocol-banner.png +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/docs/general-specs.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/docs/sprint-planning-v0.1.0.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/rules/architecture-principles.mdc +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/rules/git-commits.mdc +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.cursor/rules/python-best-practices.mdc +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.github/dependabot.yml +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.github/workflows/ci.yml +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.github/workflows/docs.yml +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/.gitignore +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/CODE_OF_CONDUCT.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/CONTRIBUTING.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/LICENSE +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/SECURITY.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/benchmarks/README.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/benchmarks/__init__.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/benchmarks/benchmark_models.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/benchmarks/benchmark_transport.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/benchmarks/conftest.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/docs/api-reference.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/docs/index.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/docs/metrics.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/docs/observability.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/docs/state-management.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/docs/testing.md +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/mkdocs.yml +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/entities/agent.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/entities/artifact.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/entities/conversation.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/entities/manifest.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/entities/message.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/entities/state_snapshot.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/entities/task.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/envelope.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/parts/data_part.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/parts/file_part.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/parts/resource_part.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/parts/template_part.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/parts/text_part.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/payloads/artifact_notify.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/payloads/mcp_resource_data.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/payloads/mcp_resource_fetch.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/payloads/mcp_tool_call.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/payloads/mcp_tool_result.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/payloads/message_send.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/payloads/state_query.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/payloads/state_restore.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/payloads/task_cancel.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/payloads/task_request.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/payloads/task_response.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/schemas/payloads/task_update.schema.json +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/scripts/export_schemas.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/cli.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/examples/__init__.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/examples/coordinator.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/examples/echo_agent.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/models/base.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/models/enums.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/models/ids.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/models/parts.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/models/payloads.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/models/types.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/observability/__init__.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/observability/logging.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/observability/metrics.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/schemas.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/state/__init__.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/state/machine.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/state/snapshot.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/transport/executors.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/transport/handlers.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/src/asap/transport/jsonrpc.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/conftest.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/e2e/__init__.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/examples/__init__.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/examples/test_coordinator.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/examples/test_echo_agent.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/models/test_base.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/models/test_enums.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/models/test_envelope.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/models/test_ids.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/models/test_parts.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/models/test_payloads.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/observability/__init__.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/observability/test_logging.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/observability/test_metrics.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/state/__init__.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/state/test_machine.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/state/test_snapshot.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/test_cli.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/test_errors.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/test_schemas.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/transport/__init__.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/transport/conftest.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/transport/e2e/__init__.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/transport/e2e/test_full_agent_flow.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/transport/integration/__init__.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/transport/integration/test_metrics_cardinality.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/transport/integration/test_rate_limiting.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/transport/integration/test_request_size_limits.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/transport/integration/test_server_core.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/transport/integration/test_thread_pool_bounds.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/transport/test_handlers.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/transport/test_jsonrpc.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/transport/unit/__init__.py +0 -0
- {asap_protocol-0.3.0 → asap_protocol-0.5.0}/tests/transport/unit/test_bounded_executor.py +0 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# Code Review: PR #19 - feat(security): add replay attack prevention and HTTPS enforcement
|
|
2
|
+
|
|
3
|
+
## 1. Executive Summary
|
|
4
|
+
|
|
5
|
+
* **Impact Analysis:** **Medium Risk**. New security-critical validation code (timestamp and nonce) integrated into the request processing pipeline. HTTPS enforcement affects all client connections.
|
|
6
|
+
* **Architecture Check:** **Yes**. Implementation follows SOLID principles, separates validation logic into dedicated module (`validators.py`), and uses Protocol pattern for extensibility.
|
|
7
|
+
* **Blockers:** **0** critical issues found.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 2. Critical Issues (Must Fix)
|
|
12
|
+
|
|
13
|
+
*No critical security or logic bugs found.* The implementation is solid, properly handles edge cases, and includes comprehensive test coverage.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 3. Improvements & Refactoring (Strongly Recommended)
|
|
18
|
+
|
|
19
|
+
The following are quality improvements that would enhance the robustness and maintainability of the implementation.
|
|
20
|
+
|
|
21
|
+
### 3.1 [Concurrency] Potential Race Condition in `InMemoryNonceStore` - `src/asap/transport/validators.py`
|
|
22
|
+
|
|
23
|
+
* **Location:** Lines 169-184 (`is_used` method)
|
|
24
|
+
* **Context:** The `is_used` method performs cleanup and check within a lock, but there's a TOCTOU (time-of-check-time-of-use) race condition between `is_used()` and `mark_used()` calls in `validate_envelope_nonce()`.
|
|
25
|
+
* **Problem:** Two concurrent requests with the same nonce could both pass `is_used()` before either calls `mark_used()`.
|
|
26
|
+
* **Risk:** Low. Under typical load, this race is unlikely. However, for a high-security protocol, this should be addressed.
|
|
27
|
+
* **Suggestion:** Combine check-and-mark into a single atomic operation:
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
# file: src/asap/transport/validators.py
|
|
31
|
+
|
|
32
|
+
class InMemoryNonceStore:
|
|
33
|
+
# ... existing code ...
|
|
34
|
+
|
|
35
|
+
def check_and_mark(self, nonce: str, ttl_seconds: int) -> bool:
|
|
36
|
+
"""Atomically check if nonce is used and mark it if not.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
nonce: The nonce value to check and mark
|
|
40
|
+
ttl_seconds: Time-to-live in seconds for the nonce
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
True if nonce was already used, False if it was newly marked
|
|
44
|
+
"""
|
|
45
|
+
with self._lock:
|
|
46
|
+
self._cleanup_expired()
|
|
47
|
+
if nonce in self._store and self._store[nonce] >= time.time():
|
|
48
|
+
return True # Already used
|
|
49
|
+
self._store[nonce] = time.time() + ttl_seconds
|
|
50
|
+
return False # Newly marked
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Then in `validate_envelope_nonce()`:
|
|
54
|
+
```diff
|
|
55
|
+
- if nonce_store.is_used(nonce):
|
|
56
|
+
- raise InvalidNonceError(...)
|
|
57
|
+
- nonce_store.mark_used(nonce, ttl_seconds=600)
|
|
58
|
+
+ if nonce_store.check_and_mark(nonce, ttl_seconds=600):
|
|
59
|
+
+ raise InvalidNonceError(...)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Note:** This would require updating the `NonceStore` Protocol to include the atomic method.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
### 3.2 [Resilience] Missing Empty Nonce String Validation - `src/asap/transport/validators.py`
|
|
67
|
+
|
|
68
|
+
* **Location:** Lines 261-269
|
|
69
|
+
* **Context:** The nonce validation checks for non-string types but doesn't reject empty strings.
|
|
70
|
+
* **Problem:** An empty string `""` would pass validation and be stored, but it offers no replay protection.
|
|
71
|
+
* **Suggestion:**
|
|
72
|
+
|
|
73
|
+
```diff
|
|
74
|
+
# file: src/asap/transport/validators.py (Line 263-269)
|
|
75
|
+
|
|
76
|
+
# Validate nonce is a string
|
|
77
|
+
- if not isinstance(nonce, str):
|
|
78
|
+
+ if not isinstance(nonce, str) or not nonce:
|
|
79
|
+
raise InvalidNonceError(
|
|
80
|
+
nonce=str(nonce),
|
|
81
|
+
- message=f"Nonce must be a string, got {type(nonce).__name__}",
|
|
82
|
+
+ message=f"Nonce must be a non-empty string, got {type(nonce).__name__ if not isinstance(nonce, str) else 'empty string'}",
|
|
83
|
+
details={"envelope_id": envelope.id},
|
|
84
|
+
)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
### 3.3 [Observability] Nonce Exposed in Log Output - `src/asap/transport/server.py`
|
|
90
|
+
|
|
91
|
+
* **Location:** Lines 885-889
|
|
92
|
+
* **Context:** The nonce value is logged when validation fails.
|
|
93
|
+
* **Problem:** Nonces, while not directly sensitive, could be useful to attackers for reconnaissance if exposed in logs.
|
|
94
|
+
* **Suggestion:** Consider truncating or hashing the nonce in logs:
|
|
95
|
+
|
|
96
|
+
```diff
|
|
97
|
+
# file: src/asap/transport/server.py (Lines 885-889)
|
|
98
|
+
|
|
99
|
+
except InvalidNonceError as e:
|
|
100
|
+
logger.warning(
|
|
101
|
+
"asap.request.invalid_nonce",
|
|
102
|
+
envelope_id=envelope.id,
|
|
103
|
+
- nonce=e.nonce,
|
|
104
|
+
+ nonce_prefix=e.nonce[:8] + "..." if len(e.nonce) > 8 else e.nonce,
|
|
105
|
+
error=e.message,
|
|
106
|
+
)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
### 3.4 [Data Integrity] Hardcoded TTL in `validate_envelope_nonce` - `src/asap/transport/validators.py`
|
|
112
|
+
|
|
113
|
+
* **Location:** Line 280
|
|
114
|
+
* **Context:** The nonce TTL is hardcoded to 600 seconds (10 minutes).
|
|
115
|
+
* **Problem:** This should be configurable or derived from `MAX_ENVELOPE_AGE_SECONDS` to ensure consistency.
|
|
116
|
+
* **Suggestion:**
|
|
117
|
+
|
|
118
|
+
```diff
|
|
119
|
+
# file: src/asap/transport/validators.py (Line 279-280)
|
|
120
|
+
|
|
121
|
+
+ # Use 2x envelope age as TTL to ensure nonces expire after the envelope would
|
|
122
|
+
+ # have been rejected anyway, providing some buffer
|
|
123
|
+
+ nonce_ttl = MAX_ENVELOPE_AGE_SECONDS * 2 # 10 minutes by default
|
|
124
|
+
- nonce_store.mark_used(nonce, ttl_seconds=600)
|
|
125
|
+
+ nonce_store.mark_used(nonce, ttl_seconds=nonce_ttl)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
### 3.5 [Defensive Coding] IPv6 Localhost Edge Case - `src/asap/transport/client.py`
|
|
131
|
+
|
|
132
|
+
* **Location:** Lines 207-224 (`_is_localhost` method)
|
|
133
|
+
* **Context:** IPv6 localhost detection handles `::1` but not full forms like `[::1]`.
|
|
134
|
+
* **Problem:** URLs like `http://[::1]:8000` would have hostname `[::1]` (with brackets) not `::1`.
|
|
135
|
+
* **Suggestion:**
|
|
136
|
+
|
|
137
|
+
```diff
|
|
138
|
+
# file: src/asap/transport/client.py (Lines 222-224)
|
|
139
|
+
|
|
140
|
+
hostname_lower = hostname.lower()
|
|
141
|
+
- return hostname_lower in ("localhost", "127.0.0.1", "::1")
|
|
142
|
+
+ # Handle both ::1 and [::1] (bracket notation from URL parsing)
|
|
143
|
+
+ return hostname_lower in ("localhost", "127.0.0.1", "::1", "[::1]")
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
### 3.6 [Testing] Missing Integration Test for Validation Order - `tests/transport/`
|
|
149
|
+
|
|
150
|
+
* **Location:** New test file recommended
|
|
151
|
+
* **Context:** Timestamp validation runs before nonce validation in the server handler.
|
|
152
|
+
* **Problem:** No test verifies the validation order or that both validations are consistently applied together.
|
|
153
|
+
* **Suggestion:** Add an integration test that verifies:
|
|
154
|
+
1. Invalid timestamp is rejected before nonce check
|
|
155
|
+
2. Valid timestamp with invalid nonce is rejected
|
|
156
|
+
3. Both validations return appropriate error codes
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 4. Nitpicks & Questions
|
|
161
|
+
|
|
162
|
+
### Code Style
|
|
163
|
+
|
|
164
|
+
* **`src/asap/transport/validators.py` (Line 10):** `datetime, timezone` import could be grouped with stdlib imports for PEP 8 compliance, but current grouping is acceptable.
|
|
165
|
+
|
|
166
|
+
* **`src/asap/errors.py` (Lines 229-234):** The conditional dict merge pattern is creative but slightly hard to read:
|
|
167
|
+
```python
|
|
168
|
+
**(age_seconds is not None and {"age_seconds": age_seconds} or {})
|
|
169
|
+
```
|
|
170
|
+
Consider explicit if/else for clarity (but this is a nitpick - current code works correctly).
|
|
171
|
+
|
|
172
|
+
### Documentation
|
|
173
|
+
|
|
174
|
+
* **`docs/security.md`:** Excellent comprehensive documentation. Minor suggestion: add a "Quick Reference" table at the top summarizing the validation constants and their values.
|
|
175
|
+
|
|
176
|
+
### Type Annotations
|
|
177
|
+
|
|
178
|
+
* **`src/asap/transport/client.py` (Line 208):** `parsed_url: Any` type annotation loses type safety. Consider using `ParseResult` from `urllib.parse`:
|
|
179
|
+
```python
|
|
180
|
+
from urllib.parse import ParseResult
|
|
181
|
+
|
|
182
|
+
@staticmethod
|
|
183
|
+
def _is_localhost(parsed_url: ParseResult) -> bool:
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Test Coverage
|
|
187
|
+
|
|
188
|
+
* **`tests/transport/unit/test_validators.py`:** Tests are well-structured. The `test_expired_nonce_allowed_again` test uses `time.sleep(1.1)` which is acceptable for unit tests but could cause flakiness. Consider using time mocking for more reliable tests.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## 5. Domain-Specific Analysis
|
|
193
|
+
|
|
194
|
+
### 5.1 Replay Attack Prevention
|
|
195
|
+
|
|
196
|
+
**Timestamp Validation:**
|
|
197
|
+
- ✅ Correct implementation of 5-minute age window
|
|
198
|
+
- ✅ Correct implementation of 30-second future tolerance
|
|
199
|
+
- ✅ Timezone handling covers naive and non-UTC timestamps
|
|
200
|
+
- ✅ Error messages include detailed context for debugging
|
|
201
|
+
|
|
202
|
+
**Nonce Validation:**
|
|
203
|
+
- ✅ Optional by design (backward compatible)
|
|
204
|
+
- ✅ Lazy cleanup prevents unbounded memory growth
|
|
205
|
+
- ⚠️ Race condition between `is_used()` and `mark_used()` (see Section 3.1)
|
|
206
|
+
- ⚠️ Hardcoded 10-minute TTL (see Section 3.4)
|
|
207
|
+
|
|
208
|
+
### 5.2 HTTPS Enforcement
|
|
209
|
+
|
|
210
|
+
**Client-Side Validation:**
|
|
211
|
+
- ✅ Localhost exception for development is appropriate
|
|
212
|
+
- ✅ Clear error messages with override instructions
|
|
213
|
+
- ✅ Warning log for HTTP localhost usage
|
|
214
|
+
- ⚠️ IPv6 localhost edge case (see Section 3.5)
|
|
215
|
+
|
|
216
|
+
### 5.3 Concurrency Safety
|
|
217
|
+
|
|
218
|
+
| Component | Thread-Safe | Notes |
|
|
219
|
+
|-----------|-------------|-------|
|
|
220
|
+
| `InMemoryNonceStore` | ✅ Yes | Uses `RLock` for all operations |
|
|
221
|
+
| `validate_envelope_timestamp` | ✅ Yes | Stateless function |
|
|
222
|
+
| `validate_envelope_nonce` | ⚠️ Partial | TOCTOU race (see Section 3.1) |
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## 6. Verification Results
|
|
227
|
+
|
|
228
|
+
### Automated Tests
|
|
229
|
+
|
|
230
|
+
| Check | Command | Result |
|
|
231
|
+
|-------|---------|--------|
|
|
232
|
+
| Unit Tests | `uv run pytest tests/transport/unit/test_validators.py -v` | ✅ **11/11 passed** |
|
|
233
|
+
| HTTPS Tests | `uv run pytest tests/transport/test_client.py -v -k HTTPS` | ✅ **6/6 passed** |
|
|
234
|
+
| Full Suite | `uv run pytest` | ✅ **627 passed** |
|
|
235
|
+
| Coverage | `pytest --cov` | ✅ **91.90%** |
|
|
236
|
+
|
|
237
|
+
### Static Analysis
|
|
238
|
+
|
|
239
|
+
| Check | Command | Result |
|
|
240
|
+
|-------|---------|--------|
|
|
241
|
+
| Linting | `uv run ruff check src/asap/transport/` | ✅ **All checks passed** |
|
|
242
|
+
| Type Check | `uv run mypy --strict src/asap/transport/` | ✅ **No issues found** |
|
|
243
|
+
|
|
244
|
+
### Manual Verification
|
|
245
|
+
|
|
246
|
+
* **Timestamp validation flow:** Traced through `server.py` lines 856-879, confirms early return on validation failure.
|
|
247
|
+
* **Nonce validation flow:** Traced through `server.py` lines 881-904, confirms proper error handling.
|
|
248
|
+
* **HTTPS enforcement:** Traced through `client.py` lines 174-197, confirms localhost exception logic.
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## 7. Commits Review
|
|
253
|
+
|
|
254
|
+
The PR follows atomic commit principles with clear conventional commit messages:
|
|
255
|
+
|
|
256
|
+
| Commit | Type | Description | Status |
|
|
257
|
+
|--------|------|-------------|--------|
|
|
258
|
+
| `e3ca11a` | feat | Add timestamp validation constants | ✅ Well-scoped |
|
|
259
|
+
| `ffe7127` | feat | Add timestamp validation for replay prevention | ✅ Well-scoped |
|
|
260
|
+
| `57f324d` | feat | Add optional nonce validation | ✅ Well-scoped |
|
|
261
|
+
| `7ba44f6` | feat | Integrate timestamp validation in handler | ✅ Well-scoped |
|
|
262
|
+
| `5e03420` | feat | Enforce HTTPS for production connections | ✅ Well-scoped |
|
|
263
|
+
| `37c9dae` | test | Add timestamp and HTTPS validation tests | ✅ Well-scoped |
|
|
264
|
+
| `97bdbf3` | docs | Add replay prevention and HTTPS docs | ✅ Well-scoped |
|
|
265
|
+
| `57e6f2d` | test | Add edge case tests for improved coverage | ✅ Well-scoped |
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## 8. Conclusion
|
|
270
|
+
|
|
271
|
+
**APPROVED FOR MERGE** ✅
|
|
272
|
+
|
|
273
|
+
The implementation is production-ready with strong security foundations. The suggestions in Section 3 are improvements for future iterations, not blockers.
|
|
274
|
+
|
|
275
|
+
**Key Strengths:**
|
|
276
|
+
- Clean separation of concerns with dedicated `validators.py` module
|
|
277
|
+
- Comprehensive error handling with detailed context
|
|
278
|
+
- Backward compatible (nonce validation is optional)
|
|
279
|
+
- Thorough documentation and test coverage
|
|
280
|
+
|
|
281
|
+
**Recommended Follow-ups (Post-Merge):**
|
|
282
|
+
1. Address the TOCTOU race condition in `InMemoryNonceStore` (Priority: Medium)
|
|
283
|
+
2. Add empty nonce string validation (Priority: Low)
|
|
284
|
+
3. Make nonce TTL configurable or derived from envelope age constant (Priority: Low)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Code Review: PR #20 (S4 Retry & Auth)
|
|
2
|
+
|
|
3
|
+
## 1. Executive Summary
|
|
4
|
+
* **Impact Analysis:** **Medium-High Risk**. The core retry and circuit breaker logic is solid, but a critical gap in exception handling (Timeouts) compromises the improved resilience.
|
|
5
|
+
* **Architecture Check:** **Yes**. The implementation aligns well with the planned architecture, utilizing decorators, efficient constants, and proper layering.
|
|
6
|
+
* **Blockers:** **1** critical issue found (Unhandled Timeouts).
|
|
7
|
+
|
|
8
|
+
## 2. Critical Issues (Must Fix)
|
|
9
|
+
*Issues that cause bugs, security risks, or strictly violate architecture/linting rules.*
|
|
10
|
+
|
|
11
|
+
### Unhandled Request Timeouts - src/asap/transport/client.py
|
|
12
|
+
* **Location:** Lines 613-800 (`send` method retry loop)
|
|
13
|
+
* **Problem:** The current implementation catches `httpx.ConnectError` but fails to catch `httpx.TimeoutException`. If a request times out (network stall, firewall, slow server), the exception will bubble up immediately, bypassing the retry logic and, critically, **failing to record the failure in the Circuit Breaker**. This defeat the purpose of the circuit breaker for one of its most important use cases (unresponsive services).
|
|
14
|
+
* **Recommendation:** Catch `httpx.TimeoutException`, record the failure, and trigger the retry logic.
|
|
15
|
+
|
|
16
|
+
```diff
|
|
17
|
+
- except httpx.ConnectError as e:
|
|
18
|
+
- error_msg = (
|
|
19
|
+
- f"Connection error to {self.base_url}: {e}. "
|
|
20
|
+
- f"Verify the agent is running and accessible."
|
|
21
|
+
- )
|
|
22
|
+
- last_exception = ASAPConnectionError(error_msg, cause=e, url=self.base_url)
|
|
23
|
+
- # Log retry attempt
|
|
24
|
+
- if attempt < self.max_retries - 1:
|
|
25
|
+
- delay = self._calculate_backoff(attempt)
|
|
26
|
+
- logger.warning(
|
|
27
|
+
- "asap.client.retry",
|
|
28
|
+
- # ...
|
|
29
|
+
- )
|
|
30
|
+
- logger.info(...)
|
|
31
|
+
- await asyncio.sleep(delay)
|
|
32
|
+
- continue
|
|
33
|
+
+ except (httpx.ConnectError, httpx.TimeoutException) as e:
|
|
34
|
+
+ is_timeout = isinstance(e, httpx.TimeoutException)
|
|
35
|
+
+ error_type = "Timeout" if is_timeout else "Connection error"
|
|
36
|
+
+ error_msg = (
|
|
37
|
+
+ f"{error_type} to {self.base_url}: {e}. "
|
|
38
|
+
+ f"Verify the agent is running and accessible."
|
|
39
|
+
+ )
|
|
40
|
+
+ last_exception = ASAPConnectionError(error_msg, cause=e, url=self.base_url)
|
|
41
|
+
+
|
|
42
|
+
+ # Log retry attempt
|
|
43
|
+
+ if attempt < self.max_retries - 1:
|
|
44
|
+
+ delay = self._calculate_backoff(attempt)
|
|
45
|
+
+ logger.warning(
|
|
46
|
+
+ "asap.client.retry",
|
|
47
|
+
+ # ... args ...
|
|
48
|
+
+ message=f"{error_type}, retrying in {delay:.2f}s..."
|
|
49
|
+
+ )
|
|
50
|
+
+ await asyncio.sleep(delay)
|
|
51
|
+
+ continue
|
|
52
|
+
+
|
|
53
|
+
+ # Retries exhausted: Record failure in Circuit Breaker
|
|
54
|
+
+ if self._circuit_breaker is not None:
|
|
55
|
+
+ self._circuit_breaker.record_failure()
|
|
56
|
+
+ # Log circuit state change if needed (copy logic from 5xx handler)
|
|
57
|
+
+
|
|
58
|
+
+ raise last_exception
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## 3. Improvements & Refactoring (Strongly Recommended)
|
|
62
|
+
|
|
63
|
+
### Circuit Breaker Reset on Remote Error - src/asap/transport/client.py
|
|
64
|
+
- **Location:** Lines 741-748 (`ASAPRemoteError` handling)
|
|
65
|
+
- **Context:** If the server responds with a valid JSON-RPC error (e.g., `-32603` or "Method not found"), it means the *connection* and *transport* are healthy. Currently, this path does not call `record_success()`, so the circuit breaker acts as if the request never succeeded (failure count isn't reset). This could lead to a "stale" failure count effectively reducing the threshold for subsequent real failures.
|
|
66
|
+
- **Suggestion:** Consider a valid JSON-RPC error as a "Success" from the Circuit Breaker's perspective (service is reachable).
|
|
67
|
+
|
|
68
|
+
```diff
|
|
69
|
+
# Check for JSON-RPC error
|
|
70
|
+
if "error" in json_response:
|
|
71
|
+
+ # Record success pattern (service is reachable)
|
|
72
|
+
+ if self._circuit_breaker is not None:
|
|
73
|
+
+ self._circuit_breaker.record_success()
|
|
74
|
+
+
|
|
75
|
+
error = json_response["error"]
|
|
76
|
+
raise ASAPRemoteError(...)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Robust Retry-After Parsing - src/asap/transport/client.py
|
|
80
|
+
- **Location:** Line 676
|
|
81
|
+
- **Context:** The HTTP `Retry-After` header can be an HTTP Date string. The current implementation only supports seconds (`float`).
|
|
82
|
+
- **Suggestion:** Add support for Date parsing using `email.utils.parsedate_to_datetime`.
|
|
83
|
+
|
|
84
|
+
```diff
|
|
85
|
+
- delay = float(retry_after)
|
|
86
|
+
+ # Simple check for digit, else try date parsing
|
|
87
|
+
+ if retry_after.replace('.', '', 1).isdigit():
|
|
88
|
+
+ delay = float(retry_after)
|
|
89
|
+
+ else:
|
|
90
|
+
+ # Add date parsing logic here (or TODO)
|
|
91
|
+
+ # from email.utils import parsedate_to_datetime
|
|
92
|
+
+ # ... calculate delta ...
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## 4. Nitpicks & Questions
|
|
96
|
+
|
|
97
|
+
* **[src/asap/transport/client.py] (Line 368)**: `self._request_counter` is updated with `+= 1` which is not atomic across threads. Since `CircuitBreaker` uses `RLock`, there is an implication of thread-safety. If `ASAPClient` is shared across threads, this could cause ID collisions. Consider using `itertools.count` and `next()` or a lock if thread safety is a requirement.
|
|
98
|
+
|
|
99
|
+
* **[src/asap/transport/client.py] (Line 284)**: The `__init__` method argument list is getting very long (11 args). Consider grouping the retry/circuit-breaker configs into a `RetryConfig` dataclass/pydantic model to avoid `False, 5, 60.0` boolean trap issues and clean up the signature.
|