codex-a2a 1.3.4__tar.gz → 1.4.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.
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/.github/workflows/publish.yml +4 -1
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/CONTRIBUTING.md +2 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/PKG-INFO +5 -5
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/README.md +1 -1
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/docs/compatibility.md +10 -4
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/docs/conformance-triage.md +1 -1
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/docs/conformance.md +1 -1
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/docs/guide.md +3 -2
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/mypy.ini +3 -1
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/pyproject.toml +3 -4
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/scripts/README.md +1 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/scripts/dependency_health.sh +4 -1
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/scripts/validate_baseline.sh +4 -1
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/a2a_proto.py +0 -28
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/client/errors.py +4 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/server/application.py +43 -129
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/server/http_middlewares.py +81 -52
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/server/openapi.py +20 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/server/request_handler.py +86 -52
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/server/runtime_limits.py +16 -2
- codex_a2a-1.4.0/src/codex_a2a/server/task_store.py +320 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a.egg-info/PKG-INFO +5 -5
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a.egg-info/requires.txt +3 -3
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/client/test_errors.py +10 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/execution/test_codex_agent_session_binding.py +6 -5
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/package/test_release_distribution_contract.py +4 -2
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/scripts/test_script_health_contract.py +2 -8
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/scripts/test_validate_baseline_script.py +1 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/server/test_database_app_persistence.py +37 -3
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/server/test_output_negotiation.py +4 -5
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/server/test_request_handler.py +117 -12
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/server/test_request_handler_lifecycle.py +16 -7
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/server/test_runtime_limits.py +16 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/server/test_sdk_upgrade.py +182 -11
- codex_a2a-1.4.0/tests/server/test_task_store.py +438 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/server/test_transport_contract.py +266 -43
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/test_input_mapping.py +6 -7
- codex_a2a-1.4.0/uv.lock +2389 -0
- codex_a2a-1.3.4/src/codex_a2a/server/task_store.py +0 -514
- codex_a2a-1.3.4/tests/server/test_task_store.py +0 -437
- codex_a2a-1.3.4/uv.lock +0 -2231
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/.github/dependabot.yml +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/.github/workflows/ci.yml +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/.github/workflows/compatibility.yml +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/.github/workflows/dependency-health.yml +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/.gitignore +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/.pre-commit-config.yaml +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/.secrets.baseline +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/AGENTS.md +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/LICENSE +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/SECURITY.md +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/docs/a2a-extension-baseline.md +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/docs/architecture.md +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/docs/extension-specifications.md +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/docs/inspection/README.md +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/docs/inspection/error_redaction_policy.md +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/docs/inspection/release_integrity_policy.md +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/docs/maintainer-architecture.md +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/scripts/audit_low_call_sites.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/scripts/check_codex_app_server_schema.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/scripts/check_dead_code.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/scripts/conformance.sh +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/scripts/health_common.sh +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/scripts/publish_github_release.sh +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/scripts/smoke_live_codex_turn.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/scripts/smoke_test_built_cli.sh +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/scripts/smoke_test_live_codex.sh +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/scripts/sync_codex_docs.sh +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/scripts/tck_auth_plugin.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/scripts/validate_runtime_matrix.sh +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/setup.cfg +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/__init__.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/auth.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/cli.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/client/__init__.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/client/agent_card.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/client/auth.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/client/client.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/client/config.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/client/extension_negotiation.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/client/manager.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/client/network_policy.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/client/payload_text.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/client/request_context.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/config.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/contracts/__init__.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/contracts/extension_registry.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/contracts/extension_specs.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/contracts/extensions.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/contracts/runtime_output.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/execution/__init__.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/execution/discovery_runtime.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/execution/exec_runtime.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/execution/executor.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/execution/output_mapping.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/execution/request_metadata.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/execution/request_overrides.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/execution/response_emitter.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/execution/review_runtime.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/execution/session_runtime.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/execution/stream_chunks.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/execution/stream_interrupts.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/execution/stream_processor.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/execution/stream_state.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/execution/streaming.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/execution/thread_lifecycle_runtime.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/execution/tool_call_payloads.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/execution/watch_events.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/input_mapping.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/__init__.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/application.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/discovery_control.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/discovery_params.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/discovery_payload_mapping.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/discovery_query.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/dispatch.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/errors.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/exec_control.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/exec_control_params.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/hooks.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/interrupt_lifecycle.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/interrupt_params.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/interrupt_recovery.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/interrupt_recovery_params.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/interrupts.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/owner_guard.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/params.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/params_common.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/payload_mapping.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/query_params.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/request_models.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/review_control.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/review_control_params.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/session_query.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/thread_lifecycle_control.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/thread_lifecycle_params.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/turn_control.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/turn_control_params.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/logging_context.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/media_modes.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/metrics.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/payload_helpers.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/profile/__init__.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/profile/runtime.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/protocol_versions.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/py.typed +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/redact.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/server/__init__.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/server/agent_card.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/server/call_context.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/server/database.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/server/migrations.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/server/openapi_contract_fragments.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/server/output_negotiation.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/server/push_config_store.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/server/runtime_state.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/server/runtime_state_schema.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/skill_handles.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/upstream/__init__.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/upstream/client.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/upstream/conversation_facade.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/upstream/discovery_payloads.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/upstream/interrupt_bridge.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/upstream/interrupts.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/upstream/models.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/upstream/notification_mapping.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/upstream/request_mapping.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/upstream/startup.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/upstream/stream_bridge.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a/upstream/transport.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a.egg-info/SOURCES.txt +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a.egg-info/dependency_links.txt +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a.egg-info/entry_points.txt +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/src/codex_a2a.egg-info/top_level.txt +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/__init__.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/client/__init__.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/client/test_agent_card.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/client/test_cli.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/client/test_client_flow.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/client/test_client_http_headers.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/client/test_client_manager.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/client/test_lifecycle.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/client/test_network_policy.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/client/test_payload_text.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/client/test_request_context.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/config/__init__.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/config/test_settings.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/contracts/__init__.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/contracts/test_extension_contract_consistency.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/contracts/test_extension_registry.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/execution/__init__.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/execution/test_agent_errors.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/execution/test_cancellation.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/execution/test_directory_validation.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/execution/test_discovery_exec_runtime.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/execution/test_error_redaction.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/execution/test_metrics.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/execution/test_request_metadata.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/execution/test_review_runtime.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/execution/test_session_ownership.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/execution/test_session_persistence.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/execution/test_session_runtime.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/execution/test_stream_chunks.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/execution/test_stream_interrupts.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/execution/test_streaming_output_contract.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/execution/test_thread_lifecycle_runtime.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/execution/test_tool_call_payloads.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/execution/test_watch_lifecycle.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/fixtures/codex_app_server/command_execution_output_delta.json +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/fixtures/codex_app_server/file_change_output_delta.json +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/fixtures/sdk_task_store_main_0a3fb68.sql +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/jsonrpc/__init__.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/jsonrpc/test_codex_discovery_extension.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/jsonrpc/test_codex_exec_extension.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/jsonrpc/test_codex_session_extension.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/jsonrpc/test_codex_thread_lifecycle_extension.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/jsonrpc/test_codex_turn_review_extension.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/jsonrpc/test_dispatch.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/jsonrpc/test_error_redaction.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/jsonrpc/test_guard_hooks.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/jsonrpc/test_interrupt_lifecycle.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/jsonrpc/test_interrupt_params.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/jsonrpc/test_interrupt_protocol_handlers.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/jsonrpc/test_jsonrpc_models.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/jsonrpc/test_protocol_payload_mapping.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/package/__init__.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/package/test_logging.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/package/test_publish_github_release_script.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/package/test_typing_contract.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/package/test_version.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/profile/__init__.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/profile/test_profile.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/scripts/__init__.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/scripts/test_audit_low_call_sites.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/scripts/test_check_codex_app_server_schema.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/scripts/test_tck_auth_plugin.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/server/__init__.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/server/test_agent_card.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/server/test_auth.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/server/test_call_context_builder.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/server/test_cli.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/server/test_database.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/server/test_http_boundary.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/server/test_migrations.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/server/test_push_config_store.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/server/test_runtime_state.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/support/__init__.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/support/context.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/support/dummy_clients.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/support/fixtures.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/support/http_auth.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/support/jsonrpc_errors.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/support/settings.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/test_a2a_proto.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/test_protocol_versions.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/test_redact.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/test_skill_handles.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/upstream/__init__.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/upstream/test_codex_client_params.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/upstream/test_interrupt_bridge.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/upstream/test_interrupt_persistence.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/upstream/test_modular_upstream_components.py +0 -0
- {codex_a2a-1.3.4 → codex_a2a-1.4.0}/tests/upstream/test_request_mapping.py +0 -0
|
@@ -55,8 +55,11 @@ jobs:
|
|
|
55
55
|
run: |
|
|
56
56
|
audit_cache_dir="$(mktemp -d)"
|
|
57
57
|
trap 'rm -rf "${audit_cache_dir}"' EXIT
|
|
58
|
+
# The export is complete and hash-pinned; avoid a second pip resolver pass.
|
|
58
59
|
XDG_CACHE_HOME="${audit_cache_dir}" \
|
|
59
|
-
uv run pip-audit
|
|
60
|
+
uv run pip-audit \
|
|
61
|
+
--disable-pip \
|
|
62
|
+
--requirement /tmp/runtime-requirements.txt
|
|
60
63
|
|
|
61
64
|
- name: Resolve release tag
|
|
62
65
|
id: release_tag
|
|
@@ -72,6 +72,8 @@ bash -n scripts/smoke_test_built_cli.sh
|
|
|
72
72
|
|
|
73
73
|
If `pre-commit` rewrites files, review the rewritten output and re-run the checks until the working tree is clean.
|
|
74
74
|
|
|
75
|
+
Do not silence signals with global suppressions. `pytest` `filterwarnings` `ignore:` entries and `mypy` `follow_imports = silent` hide real failures or drift; scope the fix instead (for example `[mypy-<package>.*] ignore_missing_imports = True`) so the gate keeps reporting everything else.
|
|
76
|
+
|
|
75
77
|
Ruff's version is declared in the `dev` extra in `pyproject.toml` and resolved in `uv.lock`. The local pre-commit hooks run `uv run --locked --extra dev ruff`, so direct commands, hooks, and CI use the same project dependency. To upgrade Ruff, update that dependency and regenerate `uv.lock`; there is no separate Ruff hook revision to maintain.
|
|
76
78
|
|
|
77
79
|
## Compatibility Expectations
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codex-a2a
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: Codex A2A runtime adapter
|
|
5
5
|
Author: liujuanjuan1984@Intelligent-Internet
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -23,7 +23,7 @@ Classifier: Topic :: Internet :: WWW/HTTP
|
|
|
23
23
|
Requires-Python: >=3.11
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE
|
|
26
|
-
Requires-Dist: a2a-sdk==1.
|
|
26
|
+
Requires-Dist: a2a-sdk==1.2.3
|
|
27
27
|
Requires-Dist: aiosqlite<1.0,>=0.20
|
|
28
28
|
Requires-Dist: fastapi<0.142,>=0.121
|
|
29
29
|
Requires-Dist: httpx<0.29,>=0.28.1
|
|
@@ -41,8 +41,8 @@ Requires-Dist: pytest>=8.0; extra == "dev"
|
|
|
41
41
|
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
42
42
|
Requires-Dist: pytest-cov<8.0,>=5.0; extra == "dev"
|
|
43
43
|
Requires-Dist: diff-cover<11,>=9; extra == "dev"
|
|
44
|
-
Requires-Dist: ruff==0.
|
|
45
|
-
Requires-Dist: mypy<2.
|
|
44
|
+
Requires-Dist: ruff==0.17.2; extra == "dev"
|
|
45
|
+
Requires-Dist: mypy<2.5,>=1.11; extra == "dev"
|
|
46
46
|
Dynamic: license-file
|
|
47
47
|
|
|
48
48
|
# codex-a2a
|
|
@@ -222,7 +222,7 @@ The normative compatibility split and deployment model live in [Compatibility Gu
|
|
|
222
222
|
### Normative Upstreams
|
|
223
223
|
|
|
224
224
|
- **[A2A Protocol](https://github.com/a2aproject/A2A)**: The Linux Foundation open protocol implemented by this adapter at the declared A2A `1.0` line.
|
|
225
|
-
- **[a2aproject/a2a-python](https://github.com/a2aproject/a2a-python)**: The official Apache-2.0 Python SDK; this repository pins the `a2a-sdk` package to `1.
|
|
225
|
+
- **[a2aproject/a2a-python](https://github.com/a2aproject/a2a-python)**: The official Apache-2.0 Python SDK; this repository pins the `a2a-sdk` package to `1.2.3`.
|
|
226
226
|
- **[openai/codex](https://github.com/openai/codex)**: The Apache-2.0 Codex CLI repository containing the local App Server used by this adapter. OpenAI's [open-source component list](https://developers.openai.com/codex/open-source) identifies the IDE extension and Codex cloud as not open source. Desktop and web product behavior is also separate from this local runtime and outside this repository's compatibility claim.
|
|
227
227
|
|
|
228
228
|
### Community Gateways and Hubs
|
|
@@ -175,7 +175,7 @@ The normative compatibility split and deployment model live in [Compatibility Gu
|
|
|
175
175
|
### Normative Upstreams
|
|
176
176
|
|
|
177
177
|
- **[A2A Protocol](https://github.com/a2aproject/A2A)**: The Linux Foundation open protocol implemented by this adapter at the declared A2A `1.0` line.
|
|
178
|
-
- **[a2aproject/a2a-python](https://github.com/a2aproject/a2a-python)**: The official Apache-2.0 Python SDK; this repository pins the `a2a-sdk` package to `1.
|
|
178
|
+
- **[a2aproject/a2a-python](https://github.com/a2aproject/a2a-python)**: The official Apache-2.0 Python SDK; this repository pins the `a2a-sdk` package to `1.2.3`.
|
|
179
179
|
- **[openai/codex](https://github.com/openai/codex)**: The Apache-2.0 Codex CLI repository containing the local App Server used by this adapter. OpenAI's [open-source component list](https://developers.openai.com/codex/open-source) identifies the IDE extension and Codex cloud as not open source. Desktop and web product behavior is also separate from this local runtime and outside this repository's compatibility claim.
|
|
180
180
|
|
|
181
181
|
### Community Gateways and Hubs
|
|
@@ -7,8 +7,8 @@ This document explains the compatibility promises this repository currently trie
|
|
|
7
7
|
| Layer | Supported contract | Evidence and boundary |
|
|
8
8
|
| --- | --- | --- |
|
|
9
9
|
| Python | 3.11–3.14 | Package classifiers cover all four versions; CI runs 3.13 as the quality gate and 3.11, 3.12, and 3.14 in the runtime matrix. |
|
|
10
|
-
| A2A Python SDK | Exactly `a2a-sdk==1.
|
|
11
|
-
| Protocol Buffers | `protobuf>=6.33.5,<8.0` | The lockfile selects 7.36.2. SDK 1.
|
|
10
|
+
| A2A Python SDK | Exactly `a2a-sdk==1.2.3` | Pinned in `pyproject.toml`; this is the [official A2A Python SDK](https://github.com/a2aproject/a2a-python), not a fork. |
|
|
11
|
+
| Protocol Buffers | `protobuf>=6.33.5,<8.0` | The lockfile selects 7.36.2. SDK 1.2.3 supports protobuf 7; Google API Core, common protos, and proto-plus must resolve within the same range. Task retrieval delegates validation and history trimming to the SDK. |
|
|
12
12
|
| SQLAlchemy | `sqlalchemy[asyncio]>=2.0,<3.0` | The `asyncio` extra explicitly installs `greenlet` for the async persistence engine, including standalone wheel installations. The lockfile selects the development version. |
|
|
13
13
|
| A2A protocol | `1.0` wire line | The released upstream A2A specification is normative for portable core semantics; the current compatible release is `v1.0.1`. SDK compatibility with other protocol versions does not extend this adapter's claim. |
|
|
14
14
|
| Inbound A2A transports | JSON-RPC and HTTP+JSON | Both are repository-owned runtime surfaces. gRPC is not exposed by this adapter. |
|
|
@@ -74,13 +74,19 @@ Service-level behavior layered on top of those core methods should also be decla
|
|
|
74
74
|
|
|
75
75
|
Task-store resilience is also service-level behavior in this deployment:
|
|
76
76
|
|
|
77
|
-
-
|
|
78
|
-
-
|
|
77
|
+
- A `SendMessage` that references a task already in a terminal state follows the SDK contract and raises `UnsupportedOperationError`; the task is not restarted in place.
|
|
78
|
+
- Database-backed deployments use the SDK `VersionedDatabaseTaskStore`, `DatabaseTaskEventStream`, and their per-task compare-and-swap versioning; stale writes are rejected rather than overwriting the current task snapshot.
|
|
79
|
+
- Memory-backed deployments use the SDK `CopyingTaskStoreAdapter` and `LegacyTaskStoreAdapter`; they are process-local and do not provide cross-replica version coordination.
|
|
79
80
|
- Task-store I/O failures are surfaced as stable service errors instead of leaking backend-specific exceptions through request handlers.
|
|
80
81
|
|
|
82
|
+
HTTP+JSON message endpoints accept the A2A 1.0 `message.parts` shape. A JSON-RPC envelope, legacy `message.content`, lowercase role values, or Part wrappers using `kind`, `type`, or `file` are rejected before SDK parsing rather than being partially accepted through unknown-field handling. JSON-RPC send methods apply the same message-shape guard and return `-32602` for invalid message fields.
|
|
83
|
+
|
|
84
|
+
Successful HTTP+JSON responses use the SDK's `application/a2a+json` media type. REST error envelopes remain AIP-193 `application/json` payloads, and OpenAPI response declarations follow the same split.
|
|
85
|
+
|
|
81
86
|
Task durability is deployment-dependent:
|
|
82
87
|
|
|
83
88
|
- `A2A_DATABASE_URL=<sqlalchemy-async-url>` preserves task lookup/cancel/resubscribe state across process restarts.
|
|
89
|
+
- SDK keyset page tokens remain usable after a task from the preceding page is deleted; memory and database regressions cover this pagination behavior.
|
|
84
90
|
- `A2A_DATABASE_URL` now defaults to a workspace-local SQLite database at `${CODEX_WORKSPACE_ROOT}/.codex-a2a/codex-a2a.db` when `CODEX_WORKSPACE_ROOT` is configured; otherwise it falls back to `sqlite+aiosqlite:///./codex-a2a.db`. Persistence therefore remains the default runtime behavior.
|
|
85
91
|
- The same database-backed mode also preserves session-binding ownership state and pending interrupt callback requests that still fall within their TTL. Session-binding and ownership persistence are independent from the in-memory session cache TTL.
|
|
86
92
|
- The default SQLite + WAL profile is intended for a stable local filesystem path, not for network filesystems or sync-managed folders with weaker locking behavior.
|
|
@@ -5,7 +5,7 @@ This document is the standing triage template for local `./scripts/conformance.s
|
|
|
5
5
|
## Authorities Used For Triage
|
|
6
6
|
|
|
7
7
|
- The latest compatible released upstream A2A specification is authoritative; currently this is `v1.0.1` for the repository's advertised `1.0` wire line.
|
|
8
|
-
- `a2a-sdk==1.
|
|
8
|
+
- `a2a-sdk==1.2.3` as installed in this repository.
|
|
9
9
|
- The default A2A protocol version advertised by this repository: `1.0`.
|
|
10
10
|
- TCK assertions are non-normative observations and may lag the released specification.
|
|
11
11
|
- Repository compatibility policy:
|
|
@@ -74,7 +74,7 @@ When a TCK run fails, inspect the raw report before changing the runtime:
|
|
|
74
74
|
|
|
75
75
|
- Some failures may point to real runtime gaps.
|
|
76
76
|
- Some failures may reflect an outdated embedded specification or a TCK-specific interpretation. Resolve those disagreements against the latest compatible released A2A specification.
|
|
77
|
-
- Some failures may come from TCK assumptions that do not match `a2a-sdk==1.
|
|
77
|
+
- Some failures may come from TCK assumptions that do not match `a2a-sdk==1.2.3`.
|
|
78
78
|
- Some failures may come from older A2A naming or schema expectations that no longer match the repository's `1.0` contract.
|
|
79
79
|
- Some failures may be local experiment artifacts from the dummy-backed runtime.
|
|
80
80
|
|
|
@@ -9,6 +9,7 @@ This guide covers runtime configuration, transport contracts, streaming/session/
|
|
|
9
9
|
- Shared JSON-RPC (`POST /`) for both core A2A methods and provider-private extension methods
|
|
10
10
|
- Agent Card publishes both HTTP+JSON and JSON-RPC endpoints through `supported_interfaces[]`.
|
|
11
11
|
- Both supported interfaces publish the same `public_url`. For HTTP+JSON, that URL is the REST service root: per A2A 1.0 the concrete REST methods (`/message:send`, `/tasks/...`) live at that root, and the protocol version is negotiated via the `A2A-Version` header rather than a URL prefix.
|
|
12
|
+
- Successful HTTP+JSON responses use `application/a2a+json`; REST errors remain AIP-193 `application/json` envelopes.
|
|
12
13
|
- The public Agent Card at `/.well-known/agent-card.json` is intentionally slimmed to the minimum discovery surface.
|
|
13
14
|
- The authenticated extended card exposes the authenticated provider-private skill inventory and deployment-aware examples:
|
|
14
15
|
- preferred: JSON-RPC `GetExtendedAgentCard`
|
|
@@ -541,7 +542,7 @@ On the current npm global install layout for Linux x64, the command above resolv
|
|
|
541
542
|
- Successful terminal chat results are artifact-first. Non-streaming terminal `Task` objects carry the completed answer in `Task.artifacts`; streaming terminal status updates carry lifecycle state and metadata but do not repeat the final answer in `status.message`.
|
|
542
543
|
- `status.message` remains reserved for user-facing status, interrupt, or error details. Clients should not treat it as the canonical completed-result carrier on successful terminal surfaces.
|
|
543
544
|
- `SubscribeToTask` remains part of the core A2A method baseline, but this deployment's terminal-task replay-once policy is a declared service-level behavior rather than a generic A2A guarantee.
|
|
544
|
-
-
|
|
545
|
+
- Database-backed task persistence uses the SDK's versioned compare-and-swap store so stale writers cannot overwrite a newer task snapshot.
|
|
545
546
|
|
|
546
547
|
### Streaming and Interrupt Contract
|
|
547
548
|
|
|
@@ -1280,7 +1281,7 @@ Terminal-task note:
|
|
|
1280
1281
|
- If the task is already terminal, this service replays one final task snapshot and then closes the stream.
|
|
1281
1282
|
- That replay-once terminal behavior is a service-level contract for this deployment. It is published through the compatibility profile and wire contract so clients do not mistake it for a generic A2A baseline rule.
|
|
1282
1283
|
- If the task store is unavailable while loading subscribe state, the service returns a controlled failure instead of exposing backend exception details.
|
|
1283
|
-
- In the default SQLite-backed deployment,
|
|
1284
|
+
- In the default SQLite-backed deployment, the SDK version table rejects stale writes without relying on a process-local snapshot check.
|
|
1284
1285
|
|
|
1285
1286
|
## Development Setup
|
|
1286
1287
|
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
python_version = 3.11
|
|
3
3
|
files = src/codex_a2a, tests/client/test_agent_card.py, tests/client/test_errors.py, tests/client/test_lifecycle.py, tests/client/test_payload_text.py, tests/client/test_request_context.py, tests/config/test_settings.py, tests/contracts/test_extension_contract_consistency.py, tests/contracts/test_extension_registry.py, tests/execution/test_agent_errors.py, tests/execution/test_cancellation.py, tests/execution/test_directory_validation.py, tests/execution/test_metrics.py, tests/execution/test_request_metadata.py, tests/execution/test_session_persistence.py, tests/execution/test_session_runtime.py, tests/execution/test_stream_chunks.py, tests/execution/test_stream_interrupts.py, tests/execution/test_tool_call_payloads.py, tests/jsonrpc/test_codex_discovery_extension.py, tests/jsonrpc/test_codex_exec_extension.py, tests/jsonrpc/test_codex_session_extension.py, tests/jsonrpc/test_codex_thread_lifecycle_extension.py, tests/jsonrpc/test_codex_turn_review_extension.py, tests/jsonrpc/test_dispatch.py, tests/jsonrpc/test_guard_hooks.py, tests/jsonrpc/test_interrupt_params.py, tests/jsonrpc/test_jsonrpc_models.py, tests/jsonrpc/test_protocol_payload_mapping.py, tests/package/test_logging.py, tests/package/test_publish_github_release_script.py, tests/package/test_release_distribution_contract.py, tests/package/test_typing_contract.py, tests/package/test_version.py, tests/profile/test_profile.py, tests/scripts/test_audit_low_call_sites.py, tests/scripts/test_script_health_contract.py, tests/scripts/test_tck_auth_plugin.py, tests/server/test_agent_card.py, tests/server/test_auth.py, tests/server/test_call_context_builder.py, tests/server/test_cli.py, tests/server/test_database.py, tests/server/test_database_app_persistence.py, tests/server/test_migrations.py, tests/server/test_output_negotiation.py, tests/server/test_push_config_store.py, tests/server/test_request_handler.py, tests/server/test_runtime_limits.py, tests/server/test_runtime_state.py, tests/support/context.py, tests/support/dummy_clients.py, tests/support/fixtures.py, tests/support/http_auth.py, tests/support/jsonrpc_errors.py, tests/support/settings.py, tests/test_a2a_proto.py, tests/test_input_mapping.py, tests/test_protocol_versions.py, tests/upstream/test_interrupt_bridge.py, tests/upstream/test_interrupt_persistence.py, tests/upstream/test_request_mapping.py
|
|
4
4
|
mypy_path = src
|
|
5
|
-
follow_imports = silent
|
|
6
5
|
warn_unused_ignores = True
|
|
7
6
|
warn_redundant_casts = True
|
|
8
7
|
show_error_codes = True
|
|
@@ -10,5 +9,8 @@ show_error_codes = True
|
|
|
10
9
|
[mypy-a2a.*]
|
|
11
10
|
ignore_missing_imports = True
|
|
12
11
|
|
|
12
|
+
[mypy-tck.*]
|
|
13
|
+
ignore_missing_imports = True
|
|
14
|
+
|
|
13
15
|
[mypy-setuptools_scm]
|
|
14
16
|
ignore_missing_imports = True
|
|
@@ -25,7 +25,7 @@ classifiers = [
|
|
|
25
25
|
"Topic :: Internet :: WWW/HTTP",
|
|
26
26
|
]
|
|
27
27
|
dependencies = [
|
|
28
|
-
"a2a-sdk==1.
|
|
28
|
+
"a2a-sdk==1.2.3",
|
|
29
29
|
"aiosqlite>=0.20,<1.0",
|
|
30
30
|
"fastapi>=0.121,<0.142",
|
|
31
31
|
"httpx>=0.28.1,<0.29",
|
|
@@ -46,8 +46,8 @@ dev = [
|
|
|
46
46
|
"pytest-asyncio>=0.23",
|
|
47
47
|
"pytest-cov>=5.0,<8.0",
|
|
48
48
|
"diff-cover>=9,<11",
|
|
49
|
-
"ruff==0.
|
|
50
|
-
"mypy>=1.11,<2.
|
|
49
|
+
"ruff==0.17.2",
|
|
50
|
+
"mypy>=1.11,<2.5",
|
|
51
51
|
]
|
|
52
52
|
|
|
53
53
|
[project.scripts]
|
|
@@ -90,7 +90,6 @@ tag_regex = "^v?(?P<version>.+)$"
|
|
|
90
90
|
[tool.pytest.ini_options]
|
|
91
91
|
addopts = "--cov=src/codex_a2a --cov-branch --cov-report=term-missing:skip-covered --cov-report=xml"
|
|
92
92
|
filterwarnings = [
|
|
93
|
-
"ignore:'HTTP_413_REQUEST_ENTITY_TOO_LARGE' is deprecated. Use 'HTTP_413_CONTENT_TOO_LARGE' instead.:DeprecationWarning:a2a\\.server\\.apps\\.jsonrpc\\.fastapi_app",
|
|
94
93
|
"error::pytest.PytestUnhandledThreadExceptionWarning",
|
|
95
94
|
"error::pytest.PytestUnraisableExceptionWarning",
|
|
96
95
|
"always:.*was deleted before being closed.*:ResourceWarning:aiosqlite.core",
|
|
@@ -38,6 +38,7 @@ The `Publish` workflow now separates build, PyPI publish, and GitHub Release syn
|
|
|
38
38
|
- `validate_baseline.sh` now includes a conservative private dead-code check before type-checking and test execution.
|
|
39
39
|
- `audit_low_call_sites.py` is intentionally advisory only. Use it to surface low-call-count candidates, then inspect the actual consumer chain before removing an abstraction.
|
|
40
40
|
- `validate_baseline.sh` now blocks on runtime dependency vulnerability audit, while `dependency_health.sh` remains focused on broader dependency review (`outdated` + dev audit).
|
|
41
|
+
- Vulnerability audits consume complete hashed `uv export` requirements with `pip-audit --disable-pip`, avoiding a second pip resolver pass while retaining hash verification.
|
|
41
42
|
- The baseline makes up to three runtime audit attempts (the initial attempt plus at most two retries), with a fresh cache per attempt. If every attempt fails, it returns the last audit exit code and stops before package build and wheel smoke testing. A successful retry allows validation to continue.
|
|
42
43
|
- [`.github/dependabot.yml`](../.github/dependabot.yml) prefers a single weekly grouped Dependabot PR for `uv`, while the repository scripts remain the explicit audit and validation entrypoints.
|
|
43
44
|
- End-user runtime startup does not use repository scripts. Prefer the published CLI command documented in [README.md](../README.md) and [docs/guide.md](../docs/guide.md).
|
|
@@ -24,4 +24,7 @@ uv export \
|
|
|
24
24
|
--output-file "${dev_requirements}" >/dev/null
|
|
25
25
|
|
|
26
26
|
echo "[dependency-health] run dev dependency vulnerability audit"
|
|
27
|
-
|
|
27
|
+
# uv export is complete and hash-pinned; do not run a second pip resolver pass.
|
|
28
|
+
XDG_CACHE_HOME="${audit_cache_dir}" uv run pip-audit \
|
|
29
|
+
--disable-pip \
|
|
30
|
+
--requirement "${dev_requirements}"
|
|
@@ -16,7 +16,10 @@ run_pip_audit() {
|
|
|
16
16
|
|
|
17
17
|
while true; do
|
|
18
18
|
audit_cache_dir="$(mktemp -d)"
|
|
19
|
-
|
|
19
|
+
# uv export is complete and hash-pinned; do not run a second pip resolver pass.
|
|
20
|
+
if XDG_CACHE_HOME="${audit_cache_dir}" uv run pip-audit \
|
|
21
|
+
--disable-pip \
|
|
22
|
+
--requirement "${requirement_file}"; then
|
|
20
23
|
rm -rf "${audit_cache_dir}"
|
|
21
24
|
return 0
|
|
22
25
|
else
|
|
@@ -53,34 +53,6 @@ def new_data_part(data: Any) -> Part:
|
|
|
53
53
|
return Part(data=ParseDict(data, Value()))
|
|
54
54
|
|
|
55
55
|
|
|
56
|
-
def new_file_url_part(
|
|
57
|
-
url: str,
|
|
58
|
-
*,
|
|
59
|
-
media_type: str | None = None,
|
|
60
|
-
filename: str | None = None,
|
|
61
|
-
) -> Part:
|
|
62
|
-
kwargs: dict[str, Any] = {"url": url}
|
|
63
|
-
if media_type is not None:
|
|
64
|
-
kwargs["media_type"] = media_type
|
|
65
|
-
if filename is not None:
|
|
66
|
-
kwargs["filename"] = filename
|
|
67
|
-
return Part(**kwargs)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def new_file_bytes_part(
|
|
71
|
-
raw: bytes,
|
|
72
|
-
*,
|
|
73
|
-
media_type: str | None = None,
|
|
74
|
-
filename: str | None = None,
|
|
75
|
-
) -> Part:
|
|
76
|
-
kwargs: dict[str, Any] = {"raw": raw}
|
|
77
|
-
if media_type is not None:
|
|
78
|
-
kwargs["media_type"] = media_type
|
|
79
|
-
if filename is not None:
|
|
80
|
-
kwargs["filename"] = filename
|
|
81
|
-
return Part(**kwargs)
|
|
82
|
-
|
|
83
|
-
|
|
84
56
|
def part_kind(part: Part | None) -> str | None:
|
|
85
57
|
if part is None:
|
|
86
58
|
return None
|
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import httpx
|
|
4
4
|
from a2a.client import A2AClientError as SDKA2AClientError
|
|
5
5
|
from a2a.client.errors import A2AClientTimeoutError, AgentCardResolutionError
|
|
6
|
+
from google.protobuf.json_format import ParseError # type: ignore[import-untyped]
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class A2AClientError(RuntimeError):
|
|
@@ -127,6 +128,9 @@ def map_a2a_sdk_error(
|
|
|
127
128
|
if isinstance(exc, A2AClientTimeoutError):
|
|
128
129
|
return A2AAgentUnavailableError(str(exc))
|
|
129
130
|
|
|
131
|
+
if isinstance(exc, ParseError):
|
|
132
|
+
return A2AClientProtocolError(str(exc))
|
|
133
|
+
|
|
130
134
|
if isinstance(exc, SDKA2AClientError):
|
|
131
135
|
return A2AClientProtocolError(str(exc))
|
|
132
136
|
|
|
@@ -2,20 +2,15 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
from contextlib import AsyncExitStack, asynccontextmanager
|
|
5
|
-
from typing import Any
|
|
5
|
+
from typing import Any, cast
|
|
6
6
|
|
|
7
7
|
import uvicorn
|
|
8
|
+
from a2a.server.request_handlers.request_handler import RequestHandler
|
|
8
9
|
from a2a.server.routes.agent_card_routes import create_agent_card_routes
|
|
9
|
-
from a2a.server.routes.rest_dispatcher import RestDispatcher
|
|
10
10
|
from a2a.server.routes.rest_routes import create_rest_routes
|
|
11
|
-
from a2a.types import SubscribeToTaskRequest, a2a_pb2
|
|
12
|
-
from a2a.utils import proto_utils
|
|
13
|
-
from a2a.utils.error_handlers import build_rest_error_payload
|
|
14
11
|
from fastapi import FastAPI
|
|
15
|
-
from
|
|
16
|
-
from starlette.
|
|
17
|
-
from starlette.responses import JSONResponse, PlainTextResponse, Response
|
|
18
|
-
from starlette.routing import BaseRoute, Mount, Route
|
|
12
|
+
from starlette.responses import JSONResponse, PlainTextResponse
|
|
13
|
+
from starlette.routing import BaseRoute, Mount
|
|
19
14
|
|
|
20
15
|
from codex_a2a.client.manager import A2AClientManager
|
|
21
16
|
from codex_a2a.config import Settings
|
|
@@ -29,7 +24,6 @@ from codex_a2a.jsonrpc.application import (
|
|
|
29
24
|
CodexSessionQueryJSONRPCApplication,
|
|
30
25
|
create_extension_jsonrpc_routes,
|
|
31
26
|
)
|
|
32
|
-
from codex_a2a.jsonrpc.errors import build_http_error_body
|
|
33
27
|
from codex_a2a.jsonrpc.hooks import SessionGuardHooks
|
|
34
28
|
from codex_a2a.logging_context import install_log_record_factory
|
|
35
29
|
from codex_a2a.metrics import get_metrics_registry
|
|
@@ -47,7 +41,6 @@ from codex_a2a.server.runtime_limits import (
|
|
|
47
41
|
OperationCapacity,
|
|
48
42
|
OperationCapacityMiddleware,
|
|
49
43
|
RequestBodyLimitMiddleware,
|
|
50
|
-
StreamBudgetExceeded,
|
|
51
44
|
apply_stream_budget,
|
|
52
45
|
)
|
|
53
46
|
from codex_a2a.server.runtime_state import build_runtime_state_runtime
|
|
@@ -67,151 +60,66 @@ def _is_sdk_tenant_mount(route: BaseRoute) -> bool:
|
|
|
67
60
|
return isinstance(route, Mount) and route.path == "/{tenant}"
|
|
68
61
|
|
|
69
62
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
class BudgetedRestDispatcher(RestDispatcher):
|
|
75
|
-
"""REST dispatcher that applies streaming output budgets to SSE streams."""
|
|
63
|
+
class StreamBudgetedRequestHandler:
|
|
64
|
+
"""Apply stream budgets through the public RequestHandler API."""
|
|
76
65
|
|
|
77
66
|
def __init__(
|
|
78
67
|
self,
|
|
79
|
-
request_handler:
|
|
80
|
-
context_builder: Any,
|
|
68
|
+
request_handler: RequestHandler,
|
|
81
69
|
*,
|
|
82
70
|
stream_budget_max_bytes: int,
|
|
83
71
|
stream_budget_max_duration_seconds: float,
|
|
84
72
|
stream_budget_idle_timeout_seconds: float,
|
|
85
73
|
) -> None:
|
|
86
|
-
|
|
87
|
-
request_handler=request_handler,
|
|
88
|
-
context_builder=context_builder,
|
|
89
|
-
)
|
|
74
|
+
self._request_handler = request_handler
|
|
90
75
|
self._stream_budget_max_bytes = stream_budget_max_bytes
|
|
91
76
|
self._stream_budget_max_duration_seconds = stream_budget_max_duration_seconds
|
|
92
77
|
self._stream_budget_idle_timeout_seconds = stream_budget_idle_timeout_seconds
|
|
93
78
|
|
|
94
|
-
|
|
95
|
-
self,
|
|
96
|
-
request: Request,
|
|
97
|
-
handler_func: Any,
|
|
98
|
-
) -> Any:
|
|
99
|
-
async def budgeted_handler(context: Any) -> Any:
|
|
100
|
-
async for item in apply_stream_budget(
|
|
101
|
-
aiter(handler_func(context)),
|
|
102
|
-
max_bytes=self._stream_budget_max_bytes,
|
|
103
|
-
max_duration_seconds=self._stream_budget_max_duration_seconds,
|
|
104
|
-
idle_timeout_seconds=self._stream_budget_idle_timeout_seconds,
|
|
105
|
-
):
|
|
106
|
-
yield item
|
|
107
|
-
|
|
108
|
-
return await super()._handle_streaming(request, budgeted_handler)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
def _stream_budget_error_response(error: StreamBudgetExceeded) -> JSONResponse:
|
|
112
|
-
return JSONResponse(
|
|
113
|
-
build_http_error_body(
|
|
114
|
-
status_code=429,
|
|
115
|
-
status="RESOURCE_EXHAUSTED",
|
|
116
|
-
message=str(error),
|
|
117
|
-
reason=_STREAM_BUDGET_REJECT_REASON,
|
|
118
|
-
),
|
|
119
|
-
status_code=429,
|
|
120
|
-
)
|
|
79
|
+
def __getattr__(self, name: str) -> Any:
|
|
80
|
+
return getattr(self._request_handler, name)
|
|
121
81
|
|
|
82
|
+
def _apply_budget(self, stream: Any) -> Any:
|
|
83
|
+
return apply_stream_budget(
|
|
84
|
+
stream,
|
|
85
|
+
max_bytes=self._stream_budget_max_bytes,
|
|
86
|
+
max_duration_seconds=self._stream_budget_max_duration_seconds,
|
|
87
|
+
idle_timeout_seconds=self._stream_budget_idle_timeout_seconds,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
async def on_message_send_stream(self, params: Any, context: Any) -> Any:
|
|
91
|
+
async for item in self._apply_budget(
|
|
92
|
+
self._request_handler.on_message_send_stream(params, context)
|
|
93
|
+
):
|
|
94
|
+
yield item
|
|
122
95
|
|
|
123
|
-
def
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
96
|
+
async def on_subscribe_to_task(self, params: Any, context: Any) -> Any:
|
|
97
|
+
async for item in self._apply_budget(
|
|
98
|
+
self._request_handler.on_subscribe_to_task(params, context)
|
|
99
|
+
):
|
|
100
|
+
yield item
|
|
128
101
|
|
|
129
102
|
|
|
130
103
|
def _create_single_tenant_rest_routes(
|
|
131
104
|
*,
|
|
132
|
-
request_handler:
|
|
105
|
+
request_handler: RequestHandler,
|
|
133
106
|
context_builder: Any = None,
|
|
134
107
|
path_prefix: str = "",
|
|
135
108
|
enable_v0_3_compat: bool = False,
|
|
136
|
-
stream_budget_max_bytes: int = 0,
|
|
137
|
-
stream_budget_max_duration_seconds: float = 0.0,
|
|
138
|
-
stream_budget_idle_timeout_seconds: float = 0.0,
|
|
139
109
|
) -> list[BaseRoute]:
|
|
140
110
|
# The SDK exposes a tenant-prefixed REST alias by default. This service's
|
|
141
111
|
# supported HTTP+JSON contract is the spec-rooted single-tenant surface
|
|
142
112
|
# (A2A 1.0 resolves REST paths from the advertised interface URL, with no
|
|
143
113
|
# version prefix in the URL), so the application assembly narrows the route
|
|
144
|
-
# set explicitly
|
|
114
|
+
# set explicitly. Streaming budgets are applied through the public
|
|
115
|
+
# RequestHandler wrapper passed by the caller.
|
|
145
116
|
sdk_routes = create_rest_routes(
|
|
146
117
|
request_handler=request_handler,
|
|
147
118
|
context_builder=context_builder,
|
|
148
119
|
enable_v0_3_compat=enable_v0_3_compat,
|
|
149
120
|
path_prefix=path_prefix,
|
|
150
121
|
)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
dispatcher = BudgetedRestDispatcher(
|
|
154
|
-
request_handler=request_handler,
|
|
155
|
-
context_builder=context_builder,
|
|
156
|
-
stream_budget_max_bytes=stream_budget_max_bytes,
|
|
157
|
-
stream_budget_max_duration_seconds=stream_budget_max_duration_seconds,
|
|
158
|
-
stream_budget_idle_timeout_seconds=stream_budget_idle_timeout_seconds,
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
async def _handle_streaming_route(request: Request, handler_func: Any) -> Response:
|
|
162
|
-
try:
|
|
163
|
-
return await dispatcher._handle_streaming(request, handler_func)
|
|
164
|
-
except StreamBudgetExceeded as error:
|
|
165
|
-
return _stream_budget_error_response(error)
|
|
166
|
-
except Exception as error: # noqa: BLE001 - mirrors SDK rest_stream_error_handler
|
|
167
|
-
return _rest_error_response(error)
|
|
168
|
-
|
|
169
|
-
async def message_stream_route(request: Request) -> Response:
|
|
170
|
-
async def _handler(context: Any) -> Any:
|
|
171
|
-
body = await request.body()
|
|
172
|
-
params = a2a_pb2.SendMessageRequest()
|
|
173
|
-
Parse(body, params)
|
|
174
|
-
async for event in request_handler.on_message_send_stream(params, context):
|
|
175
|
-
yield MessageToDict(proto_utils.to_stream_response(event))
|
|
176
|
-
|
|
177
|
-
return await _handle_streaming_route(request, _handler)
|
|
178
|
-
|
|
179
|
-
async def subscribe_route(request: Request) -> Response:
|
|
180
|
-
task_id = request.path_params["id"]
|
|
181
|
-
|
|
182
|
-
async def _handler(context: Any) -> Any:
|
|
183
|
-
async for event in request_handler.on_subscribe_to_task(
|
|
184
|
-
SubscribeToTaskRequest(id=task_id),
|
|
185
|
-
context,
|
|
186
|
-
):
|
|
187
|
-
yield MessageToDict(proto_utils.to_stream_response(event))
|
|
188
|
-
|
|
189
|
-
return await _handle_streaming_route(request, _handler)
|
|
190
|
-
|
|
191
|
-
prefixed_streaming_paths = {f"{path_prefix}{path}" for path in _STREAMING_REST_PATHS}
|
|
192
|
-
retained = [
|
|
193
|
-
route
|
|
194
|
-
for route in base_routes
|
|
195
|
-
if not (hasattr(route, "path") and route.path in prefixed_streaming_paths)
|
|
196
|
-
]
|
|
197
|
-
streaming_routes = [
|
|
198
|
-
Route(
|
|
199
|
-
path=f"{path_prefix}/message:stream",
|
|
200
|
-
endpoint=message_stream_route,
|
|
201
|
-
methods=["POST"],
|
|
202
|
-
),
|
|
203
|
-
Route(
|
|
204
|
-
path=f"{path_prefix}/tasks/{{id}}:subscribe",
|
|
205
|
-
endpoint=subscribe_route,
|
|
206
|
-
methods=["GET"],
|
|
207
|
-
),
|
|
208
|
-
Route(
|
|
209
|
-
path=f"{path_prefix}/tasks/{{id}}:subscribe",
|
|
210
|
-
endpoint=subscribe_route,
|
|
211
|
-
methods=["POST"],
|
|
212
|
-
),
|
|
213
|
-
]
|
|
214
|
-
return retained + streaming_routes
|
|
122
|
+
return [route for route in sdk_routes if not _is_sdk_tenant_mount(route)]
|
|
215
123
|
|
|
216
124
|
|
|
217
125
|
def create_app(settings: Settings) -> FastAPI:
|
|
@@ -257,6 +165,7 @@ def create_app(settings: Settings) -> FastAPI:
|
|
|
257
165
|
push_config_store=push_config_store_runtime.push_config_store,
|
|
258
166
|
agent_card=agent_card,
|
|
259
167
|
extended_agent_card=extended_agent_card,
|
|
168
|
+
event_stream=task_store_runtime.event_stream,
|
|
260
169
|
)
|
|
261
170
|
exec_runtime = CodexExecRuntime(
|
|
262
171
|
client=client,
|
|
@@ -379,16 +288,22 @@ def create_app(settings: Settings) -> FastAPI:
|
|
|
379
288
|
stream_budget_idle_timeout_seconds=settings.a2a_stream_idle_timeout_seconds,
|
|
380
289
|
)
|
|
381
290
|
)
|
|
291
|
+
rest_request_handler = cast(
|
|
292
|
+
RequestHandler,
|
|
293
|
+
StreamBudgetedRequestHandler(
|
|
294
|
+
handler,
|
|
295
|
+
stream_budget_max_bytes=settings.a2a_stream_max_bytes,
|
|
296
|
+
stream_budget_max_duration_seconds=settings.a2a_stream_max_duration_seconds,
|
|
297
|
+
stream_budget_idle_timeout_seconds=settings.a2a_stream_idle_timeout_seconds,
|
|
298
|
+
),
|
|
299
|
+
)
|
|
382
300
|
app.router.routes.extend(
|
|
383
301
|
_create_single_tenant_rest_routes(
|
|
384
|
-
request_handler=
|
|
302
|
+
request_handler=rest_request_handler,
|
|
385
303
|
context_builder=context_builder,
|
|
386
304
|
# A2A 1.0 roots the HTTP+JSON surface at the service root; the SDK
|
|
387
305
|
# default prefix is empty, matching the URL the Agent Card advertises.
|
|
388
306
|
path_prefix="",
|
|
389
|
-
stream_budget_max_bytes=settings.a2a_stream_max_bytes,
|
|
390
|
-
stream_budget_max_duration_seconds=settings.a2a_stream_max_duration_seconds,
|
|
391
|
-
stream_budget_idle_timeout_seconds=settings.a2a_stream_idle_timeout_seconds,
|
|
392
307
|
)
|
|
393
308
|
)
|
|
394
309
|
app.state.codex_client = client
|
|
@@ -433,7 +348,6 @@ def create_app(settings: Settings) -> FastAPI:
|
|
|
433
348
|
install_http_middlewares(
|
|
434
349
|
app,
|
|
435
350
|
settings=settings,
|
|
436
|
-
task_store=task_store,
|
|
437
351
|
agent_card=agent_card,
|
|
438
352
|
extended_agent_card=extended_agent_card,
|
|
439
353
|
)
|