codex-a2a 0.7.0__tar.gz → 0.7.2__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-0.7.0/src/codex_a2a.egg-info → codex_a2a-0.7.2}/PKG-INFO +1 -1
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/docs/guide.md +1 -1
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/client/__init__.py +2 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/client/auth.py +20 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/client/client.py +14 -1
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/client/config.py +1 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/client/payload_text.py +11 -1
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/config.py +1 -1
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/execution/cancellation.py +1 -1
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/execution/executor.py +1 -1
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/execution/output_mapping.py +17 -9
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/execution/stream_chunks.py +16 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/execution/stream_processor.py +3 -3
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/execution/thread_lifecycle_runtime.py +2 -2
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/server/application.py +2 -2
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/server/http_middlewares.py +2 -2
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/server/request_handler.py +2 -2
- {codex_a2a-0.7.0 → codex_a2a-0.7.2/src/codex_a2a.egg-info}/PKG-INFO +1 -1
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/client/test_client_flow.py +53 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/config/test_settings.py +1 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/execution/test_stream_chunks.py +18 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/execution/test_streaming_output_contract.py +2 -2
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/server/test_cli.py +6 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/.github/dependabot.yml +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/.github/workflows/ci.yml +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/.github/workflows/dependency-health.yml +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/.github/workflows/publish.yml +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/.gitignore +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/.pre-commit-config.yaml +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/.secrets.baseline +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/AGENTS.md +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/CONTRIBUTING.md +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/LICENSE +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/README.md +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/SECURITY.md +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/docs/architecture.md +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/docs/compatibility.md +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/docs/conformance-triage.md +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/docs/conformance.md +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/docs/extension-specifications.md +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/docs/maintainer-architecture.md +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/mypy.ini +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/pyproject.toml +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/scripts/README.md +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/scripts/conformance.sh +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/scripts/dependency_health.sh +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/scripts/doctor.sh +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/scripts/health_common.sh +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/scripts/publish_github_release.sh +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/scripts/smoke_test_built_cli.sh +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/scripts/sync_codex_docs.sh +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/scripts/validate_baseline.sh +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/scripts/validate_runtime_matrix.sh +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/setup.cfg +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/__init__.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/auth.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/cli.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/client/agent_card.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/client/errors.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/client/manager.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/client/request_context.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/client/types.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/contracts/__init__.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/contracts/extensions.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/contracts/runtime_output.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/execution/__init__.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/execution/directory_policy.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/execution/discovery_runtime.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/execution/exec_runtime.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/execution/request_metadata.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/execution/request_overrides.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/execution/response_emitter.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/execution/review_runtime.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/execution/session_runtime.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/execution/stream_interrupts.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/execution/stream_state.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/execution/streaming.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/execution/tool_call_payloads.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/execution/watch_events.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/input_mapping.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/__init__.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/application.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/control_params.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/discovery_control.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/discovery_params.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/discovery_payload_mapping.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/discovery_query.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/dispatch.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/errors.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/exec_control.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/hooks.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/interrupt_lifecycle.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/interrupt_params.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/interrupt_recovery.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/interrupt_recovery_params.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/interrupts.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/owner_guard.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/params.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/params_common.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/payload_mapping.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/query_params.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/review_control.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/review_control_params.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/session_control.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/session_query.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/thread_lifecycle_control.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/thread_lifecycle_params.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/turn_control.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/jsonrpc/turn_control_params.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/logging_context.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/media_modes.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/metrics.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/parts/text.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/payload_helpers.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/profile/__init__.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/profile/runtime.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/protocol_versions.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/py.typed +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/server/__init__.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/server/agent_card.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/server/call_context.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/server/database.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/server/migrations.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/server/openapi.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/server/output_negotiation.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/server/runtime_state.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/server/task_store.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/upstream/__init__.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/upstream/client.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/upstream/conversation_facade.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/upstream/discovery_payloads.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/upstream/exec_facade.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/upstream/interrupt_bridge.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/upstream/interrupts.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/upstream/models.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/upstream/notification_mapping.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/upstream/request_mapping.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/upstream/startup.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/upstream/stream_bridge.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a/upstream/transport.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a.egg-info/SOURCES.txt +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a.egg-info/dependency_links.txt +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a.egg-info/entry_points.txt +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a.egg-info/requires.txt +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/src/codex_a2a.egg-info/top_level.txt +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/__init__.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/client/__init__.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/client/test_agent_card.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/client/test_cli.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/client/test_client_manager.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/client/test_errors.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/client/test_lifecycle.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/client/test_payload_text.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/client/test_request_context.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/client/test_types.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/config/__init__.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/contracts/__init__.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/contracts/test_extension_contract_consistency.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/execution/__init__.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/execution/test_agent_errors.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/execution/test_cancellation.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/execution/test_codex_agent_session_binding.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/execution/test_directory_validation.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/execution/test_discovery_exec_runtime.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/execution/test_metrics.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/execution/test_review_runtime.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/execution/test_session_ownership.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/execution/test_session_persistence.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/execution/test_session_runtime.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/execution/test_stream_interrupts.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/execution/test_thread_lifecycle_runtime.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/execution/test_tool_call_payloads.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/fixtures/codex_app_server/command_execution_output_delta.json +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/fixtures/codex_app_server/file_change_output_delta.json +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/jsonrpc/__init__.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/jsonrpc/test_codex_discovery_extension.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/jsonrpc/test_codex_exec_extension.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/jsonrpc/test_codex_session_extension.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/jsonrpc/test_codex_thread_lifecycle_extension.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/jsonrpc/test_codex_turn_review_extension.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/jsonrpc/test_dispatch.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/jsonrpc/test_guard_hooks.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/jsonrpc/test_jsonrpc_models.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/package/__init__.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/package/test_logging.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/package/test_publish_github_release_script.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/package/test_release_distribution_contract.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/package/test_typing_contract.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/package/test_version.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/parts/__init__.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/parts/test_text.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/profile/__init__.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/profile/test_profile.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/scripts/__init__.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/scripts/test_script_health_contract.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/server/__init__.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/server/test_agent_card.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/server/test_auth.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/server/test_call_context_builder.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/server/test_database.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/server/test_database_app_persistence.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/server/test_migrations.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/server/test_request_handler.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/server/test_runtime_state.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/server/test_task_store.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/server/test_transport_contract.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/support/__init__.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/support/context.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/support/dummy_clients.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/support/fixtures.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/support/settings.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/test_input_mapping.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/test_protocol_versions.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/upstream/__init__.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/upstream/test_codex_client_params.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/upstream/test_interrupt_persistence.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/tests/upstream/test_modular_upstream_components.py +0 -0
- {codex_a2a-0.7.0 → codex_a2a-0.7.2}/uv.lock +0 -0
|
@@ -191,7 +191,7 @@ Use the grouped sections below as the deployment-first reading order:
|
|
|
191
191
|
- `A2A_DATABASE_URL` also owns the adapter-managed runtime-state schema lifecycle. On startup, `codex-a2a` auto-creates the runtime-state tables, records a schema version for the `runtime_state` scope, and applies in-place migrations for those tables only.
|
|
192
192
|
- The adapter-managed runtime-state schema is limited to `a2a_session_bindings`, `a2a_session_owners`, `a2a_pending_session_claims`, `a2a_pending_interrupt_requests`, and `a2a_schema_version`. It does not own the A2A SDK task-store tables or any upstream Codex/provider-local state.
|
|
193
193
|
- For deployment-specific durability constraints and migration-scope boundaries, see [compatibility.md](./compatibility.md).
|
|
194
|
-
- `A2A_LOG_LEVEL`: `DEBUG/INFO/WARNING/ERROR`, default `
|
|
194
|
+
- `A2A_LOG_LEVEL`: `DEBUG/INFO/WARNING/ERROR`, default `WARNING`
|
|
195
195
|
- `A2A_LOG_PAYLOADS`: log A2A/Codex payload bodies, default `false`
|
|
196
196
|
- `A2A_LOG_BODY_LIMIT`: payload log body size limit, default `0` (no truncation)
|
|
197
197
|
- `A2A_TITLE`: agent name, default `Codex A2A`
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Minimal A2A client facade package."""
|
|
2
2
|
|
|
3
|
+
from .auth import StaticCredentialService
|
|
3
4
|
from .client import A2AClient
|
|
4
5
|
from .config import A2AClientConfig
|
|
5
6
|
from .errors import (
|
|
@@ -25,6 +26,7 @@ __all__ = [
|
|
|
25
26
|
"A2AClient",
|
|
26
27
|
"A2AClientManager",
|
|
27
28
|
"A2AClientConfig",
|
|
29
|
+
"StaticCredentialService",
|
|
28
30
|
"A2AClientConfigError",
|
|
29
31
|
"A2AClientError",
|
|
30
32
|
"A2AClientLifecycleError",
|
|
@@ -2,6 +2,10 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from base64 import b64decode, b64encode
|
|
4
4
|
from binascii import Error as BinasciiError
|
|
5
|
+
from collections.abc import Mapping
|
|
6
|
+
|
|
7
|
+
from a2a.client.auth.credentials import CredentialService
|
|
8
|
+
from a2a.client.middleware import ClientCallContext
|
|
5
9
|
|
|
6
10
|
BASIC_AUTH_FORMAT_ERROR = (
|
|
7
11
|
"A2A_CLIENT_BASIC_AUTH must be 'username:password' or a base64-encoded "
|
|
@@ -32,3 +36,19 @@ def _decode_basic_auth(value: str) -> bytes:
|
|
|
32
36
|
return b64decode(padded_value, validate=True)
|
|
33
37
|
except (BinasciiError, ValueError) as exc:
|
|
34
38
|
raise ValueError(BASIC_AUTH_FORMAT_ERROR) from exc
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class StaticCredentialService(CredentialService):
|
|
42
|
+
def __init__(self, credentials: Mapping[str, str]) -> None:
|
|
43
|
+
self._credentials = {
|
|
44
|
+
scheme_name: credential
|
|
45
|
+
for scheme_name, credential in dict(credentials).items()
|
|
46
|
+
if isinstance(scheme_name, str) and scheme_name and isinstance(credential, str)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async def get_credentials(
|
|
50
|
+
self,
|
|
51
|
+
security_scheme_name: str,
|
|
52
|
+
context: ClientCallContext | None,
|
|
53
|
+
) -> str | None:
|
|
54
|
+
return self._credentials.get(security_scheme_name)
|
|
@@ -7,6 +7,8 @@ from uuid import uuid4
|
|
|
7
7
|
|
|
8
8
|
import httpx
|
|
9
9
|
from a2a.client import A2ACardResolver, ClientConfig, ClientFactory
|
|
10
|
+
from a2a.client.auth.credentials import CredentialService
|
|
11
|
+
from a2a.client.auth.interceptor import AuthInterceptor
|
|
10
12
|
from a2a.client.middleware import ClientCallContext, ClientCallInterceptor
|
|
11
13
|
from a2a.types import (
|
|
12
14
|
AgentCard,
|
|
@@ -21,6 +23,7 @@ from a2a.types import (
|
|
|
21
23
|
)
|
|
22
24
|
|
|
23
25
|
from .agent_card import build_agent_card_request_kwargs, resolve_agent_card_endpoint
|
|
26
|
+
from .auth import StaticCredentialService
|
|
24
27
|
from .config import A2AClientConfig
|
|
25
28
|
from .errors import (
|
|
26
29
|
A2AClientConfigError,
|
|
@@ -103,6 +106,7 @@ class A2AClient:
|
|
|
103
106
|
httpx_client: httpx.AsyncClient | None = None,
|
|
104
107
|
card_resolver_factory=A2ACardResolver,
|
|
105
108
|
client_factory_type=ClientFactory,
|
|
109
|
+
credential_service: CredentialService | None = None,
|
|
106
110
|
) -> None:
|
|
107
111
|
if not config.agent_url:
|
|
108
112
|
raise A2AClientConfigError("agent_url is required")
|
|
@@ -118,6 +122,7 @@ class A2AClient:
|
|
|
118
122
|
self._sdk_client: _SDKClientProtocol | None = None
|
|
119
123
|
self._card_resolver_factory = card_resolver_factory
|
|
120
124
|
self._client_factory_type = client_factory_type
|
|
125
|
+
self._credential_service = credential_service
|
|
121
126
|
self._client_config = ClientConfig(
|
|
122
127
|
streaming=True,
|
|
123
128
|
supported_transports=list(config.supported_transports),
|
|
@@ -328,7 +333,15 @@ class A2AClient:
|
|
|
328
333
|
return self._sdk_client
|
|
329
334
|
|
|
330
335
|
def _build_interceptors(self) -> list[ClientCallInterceptor]:
|
|
331
|
-
|
|
336
|
+
interceptors: list[ClientCallInterceptor] = [
|
|
337
|
+
_HeaderInterceptor(self._config.default_headers)
|
|
338
|
+
]
|
|
339
|
+
credential_service = self._credential_service
|
|
340
|
+
if credential_service is None and self._config.auth_credentials:
|
|
341
|
+
credential_service = StaticCredentialService(self._config.auth_credentials)
|
|
342
|
+
if credential_service is not None:
|
|
343
|
+
interceptors.append(AuthInterceptor(credential_service))
|
|
344
|
+
return interceptors
|
|
332
345
|
|
|
333
346
|
def _build_user_message(
|
|
334
347
|
self,
|
|
@@ -14,6 +14,7 @@ class A2AClientConfig(BaseModel):
|
|
|
14
14
|
request_timeout_seconds: float | None = None
|
|
15
15
|
close_http_client: bool = True
|
|
16
16
|
default_headers: dict[str, str] = Field(default_factory=dict)
|
|
17
|
+
auth_credentials: dict[str, str] = Field(default_factory=dict)
|
|
17
18
|
accepted_output_modes: list[str] = Field(default_factory=list)
|
|
18
19
|
extensions: list[str] = Field(default_factory=list)
|
|
19
20
|
|
|
@@ -3,7 +3,9 @@ from __future__ import annotations
|
|
|
3
3
|
from collections.abc import Mapping
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
|
-
from a2a.types import Message, TextPart
|
|
6
|
+
from a2a.types import Message, Part, TextPart
|
|
7
|
+
from a2a.utils.message import get_message_text
|
|
8
|
+
from a2a.utils.parts import get_text_parts
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
def _extract_from_iterable(items: Any) -> str | None:
|
|
@@ -19,6 +21,11 @@ def _extract_from_iterable(items: Any) -> str | None:
|
|
|
19
21
|
def _extract_from_parts(parts: Any) -> str | None:
|
|
20
22
|
if not isinstance(parts, (list, tuple)):
|
|
21
23
|
return None
|
|
24
|
+
if all(isinstance(part, Part) for part in parts):
|
|
25
|
+
sdk_text = "\n".join(text for text in get_text_parts(list(parts)) if text.strip())
|
|
26
|
+
if sdk_text:
|
|
27
|
+
return sdk_text
|
|
28
|
+
|
|
22
29
|
collected: list[str] = []
|
|
23
30
|
for part in parts:
|
|
24
31
|
text_part = None
|
|
@@ -100,6 +107,9 @@ def extract_text_from_payload(payload: Any) -> str | None:
|
|
|
100
107
|
return _extract_from_iterable(payload)
|
|
101
108
|
|
|
102
109
|
if isinstance(payload, Message):
|
|
110
|
+
sdk_text = get_message_text(payload).strip()
|
|
111
|
+
if sdk_text:
|
|
112
|
+
return sdk_text
|
|
103
113
|
return _extract_from_parts(payload.parts)
|
|
104
114
|
|
|
105
115
|
if isinstance(payload, str):
|
|
@@ -330,7 +330,7 @@ class Settings(BaseSettings):
|
|
|
330
330
|
a2a_enable_turn_control: bool = Field(default=True, alias="A2A_ENABLE_TURN_CONTROL")
|
|
331
331
|
a2a_enable_review_control: bool = Field(default=False, alias="A2A_ENABLE_REVIEW_CONTROL")
|
|
332
332
|
a2a_enable_exec_control: bool = Field(default=False, alias="A2A_ENABLE_EXEC_CONTROL")
|
|
333
|
-
a2a_log_level: str = Field(default="
|
|
333
|
+
a2a_log_level: str = Field(default="WARNING", alias="A2A_LOG_LEVEL")
|
|
334
334
|
a2a_log_payloads: bool = Field(default=False, alias="A2A_LOG_PAYLOADS")
|
|
335
335
|
a2a_log_body_limit: int = Field(default=0, alias="A2A_LOG_BODY_LIMIT")
|
|
336
336
|
a2a_documentation_url: str | None = Field(default=None, alias="A2A_DOCUMENTATION_URL")
|
|
@@ -176,7 +176,7 @@ class CodexAgentExecutor(AgentExecutor):
|
|
|
176
176
|
try:
|
|
177
177
|
directory = self._resolve_and_validate_directory(requested_dir)
|
|
178
178
|
except ValueError as e:
|
|
179
|
-
logger.
|
|
179
|
+
logger.debug("Directory validation failed: %s", e)
|
|
180
180
|
await self._emit_error(
|
|
181
181
|
event_queue,
|
|
182
182
|
task_id=task_id,
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import uuid
|
|
4
3
|
from collections.abc import Mapping
|
|
5
4
|
from typing import Any
|
|
6
5
|
|
|
7
6
|
from a2a.server.agent_execution import RequestContext
|
|
8
7
|
from a2a.server.events.event_queue import EventQueue
|
|
8
|
+
from a2a.server.tasks import TaskUpdater
|
|
9
9
|
from a2a.types import (
|
|
10
10
|
Artifact,
|
|
11
11
|
DataPart,
|
|
12
12
|
Message,
|
|
13
13
|
Part,
|
|
14
|
-
Role,
|
|
15
14
|
TaskArtifactUpdateEvent,
|
|
16
15
|
TextPart,
|
|
17
16
|
)
|
|
17
|
+
from a2a.utils.message import new_agent_text_message
|
|
18
18
|
|
|
19
19
|
from codex_a2a.contracts.runtime_output import (
|
|
20
20
|
build_output_metadata as build_runtime_output_metadata,
|
|
@@ -28,13 +28,10 @@ def build_assistant_message(
|
|
|
28
28
|
*,
|
|
29
29
|
message_id: str | None = None,
|
|
30
30
|
) -> Message:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
task_id=task_id,
|
|
36
|
-
context_id=context_id,
|
|
37
|
-
)
|
|
31
|
+
message = new_agent_text_message(text, context_id=context_id, task_id=task_id)
|
|
32
|
+
if message_id is None:
|
|
33
|
+
return message
|
|
34
|
+
return message.model_copy(update={"message_id": message_id})
|
|
38
35
|
|
|
39
36
|
|
|
40
37
|
async def enqueue_artifact_update(
|
|
@@ -50,6 +47,17 @@ async def enqueue_artifact_update(
|
|
|
50
47
|
event_metadata: Mapping[str, Any] | None = None,
|
|
51
48
|
) -> None:
|
|
52
49
|
normalized_last_chunk = True if last_chunk is True else None
|
|
50
|
+
if event_metadata is None:
|
|
51
|
+
updater = TaskUpdater(event_queue, task_id, context_id)
|
|
52
|
+
await updater.add_artifact(
|
|
53
|
+
parts=[Part(root=part)],
|
|
54
|
+
artifact_id=artifact_id,
|
|
55
|
+
metadata=dict(artifact_metadata) if artifact_metadata else None,
|
|
56
|
+
append=append,
|
|
57
|
+
last_chunk=normalized_last_chunk,
|
|
58
|
+
)
|
|
59
|
+
return
|
|
60
|
+
|
|
53
61
|
artifact = Artifact(
|
|
54
62
|
artifact_id=artifact_id,
|
|
55
63
|
parts=[Part(root=part)],
|
|
@@ -387,6 +387,14 @@ def tool_delta_chunks(
|
|
|
387
387
|
if not isinstance(delta_value, Mapping):
|
|
388
388
|
logger.warning(
|
|
389
389
|
"Suppressing non-structured tool_call payload "
|
|
390
|
+
"task_id=%s session_id=%s source=%s payload_type=%s",
|
|
391
|
+
task_id,
|
|
392
|
+
session_id,
|
|
393
|
+
source,
|
|
394
|
+
type(delta_value).__name__,
|
|
395
|
+
)
|
|
396
|
+
logger.debug(
|
|
397
|
+
"Suppressed non-structured tool_call payload "
|
|
390
398
|
"task_id=%s session_id=%s source=%s payload=%s",
|
|
391
399
|
task_id,
|
|
392
400
|
session_id,
|
|
@@ -398,6 +406,14 @@ def tool_delta_chunks(
|
|
|
398
406
|
if payload is None:
|
|
399
407
|
logger.warning(
|
|
400
408
|
"Suppressing unrecognized tool_call payload "
|
|
409
|
+
"task_id=%s session_id=%s source=%s payload_keys=%s",
|
|
410
|
+
task_id,
|
|
411
|
+
session_id,
|
|
412
|
+
source,
|
|
413
|
+
sorted(str(key) for key in delta_value),
|
|
414
|
+
)
|
|
415
|
+
logger.debug(
|
|
416
|
+
"Suppressed unrecognized tool_call payload "
|
|
401
417
|
"task_id=%s session_id=%s source=%s payload=%s",
|
|
402
418
|
task_id,
|
|
403
419
|
session_id,
|
|
@@ -122,7 +122,7 @@ class StreamEventProcessor:
|
|
|
122
122
|
self._diagnostics = StreamDiagnostics(started_at=time.monotonic())
|
|
123
123
|
|
|
124
124
|
def log_started(self, logger) -> None: # noqa: ANN001
|
|
125
|
-
logger.
|
|
125
|
+
logger.debug(
|
|
126
126
|
"Codex event stream started task_id=%s session_id=%s idle_diagnostic_seconds=%.1f",
|
|
127
127
|
self._task_id,
|
|
128
128
|
self._session_id,
|
|
@@ -178,7 +178,7 @@ class StreamEventProcessor:
|
|
|
178
178
|
if self._diagnostics.completion_observed:
|
|
179
179
|
return
|
|
180
180
|
self._diagnostics.completion_observed = True
|
|
181
|
-
logger.
|
|
181
|
+
logger.debug(
|
|
182
182
|
"Codex event stream completion observed task_id=%s session_id=%s "
|
|
183
183
|
"emitted_chunk_count=%s suppressed_chunk_count=%s",
|
|
184
184
|
self._task_id,
|
|
@@ -192,7 +192,7 @@ class StreamEventProcessor:
|
|
|
192
192
|
if self._completion_event.is_set():
|
|
193
193
|
await self.observe_completion(logger)
|
|
194
194
|
snapshot = self._diagnostics.snapshot(now=time.monotonic(), stream_open=False)
|
|
195
|
-
logger.
|
|
195
|
+
logger.debug(
|
|
196
196
|
"Codex event stream closed task_id=%s session_id=%s completion_observed=%s "
|
|
197
197
|
"emitted_chunk_count=%s suppressed_chunk_count=%s started_ms_ago=%s "
|
|
198
198
|
"last_upstream_event_ms_ago=%s last_visible_chunk_ms_ago=%s idle_log_count=%s",
|
|
@@ -334,7 +334,7 @@ class CodexThreadLifecycleRuntime:
|
|
|
334
334
|
if subscription is None:
|
|
335
335
|
return
|
|
336
336
|
if subscription.connection_scope != self._client.connection_scope_id:
|
|
337
|
-
logger.
|
|
337
|
+
logger.debug(
|
|
338
338
|
"Skipping upstream thread/unsubscribe for subscription %s because "
|
|
339
339
|
"connection_scope changed from %s to %s",
|
|
340
340
|
subscription_key,
|
|
@@ -343,7 +343,7 @@ class CodexThreadLifecycleRuntime:
|
|
|
343
343
|
)
|
|
344
344
|
return
|
|
345
345
|
if not subscription.thread_filter:
|
|
346
|
-
logger.
|
|
346
|
+
logger.debug(
|
|
347
347
|
"Skipping upstream thread/unsubscribe for subscription %s because "
|
|
348
348
|
"thread_filter is not concrete",
|
|
349
349
|
subscription_key,
|
|
@@ -241,13 +241,13 @@ def _normalize_log_level(value: str) -> str:
|
|
|
241
241
|
normalized = (value or "").strip().upper()
|
|
242
242
|
if normalized in {"CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"}:
|
|
243
243
|
return normalized
|
|
244
|
-
return "
|
|
244
|
+
return "WARNING"
|
|
245
245
|
|
|
246
246
|
|
|
247
247
|
def _configure_logging(level: str) -> None:
|
|
248
248
|
install_log_record_factory()
|
|
249
249
|
logging.basicConfig(
|
|
250
|
-
level=getattr(logging, level, logging.
|
|
250
|
+
level=getattr(logging, level, logging.WARNING),
|
|
251
251
|
format=(
|
|
252
252
|
"%(asctime)s %(levelname)s %(name)s [correlation_id=%(correlation_id)s]: %(message)s"
|
|
253
253
|
),
|
|
@@ -633,11 +633,11 @@ def install_http_middlewares(
|
|
|
633
633
|
token = set_correlation_id(correlation_id)
|
|
634
634
|
started_at = time.perf_counter()
|
|
635
635
|
path = request.url.path
|
|
636
|
-
logger.
|
|
636
|
+
logger.debug("A2A request started method=%s path=%s", request.method, path)
|
|
637
637
|
try:
|
|
638
638
|
response = await call_next(request)
|
|
639
639
|
response.headers[CORRELATION_ID_HEADER] = correlation_id
|
|
640
|
-
logger.
|
|
640
|
+
logger.debug(
|
|
641
641
|
"A2A request completed method=%s path=%s status=%s duration_ms=%.2f",
|
|
642
642
|
request.method,
|
|
643
643
|
path,
|
|
@@ -386,7 +386,7 @@ class CodexRequestHandler(DefaultRequestHandler):
|
|
|
386
386
|
):
|
|
387
387
|
yield event
|
|
388
388
|
except (asyncio.CancelledError, GeneratorExit):
|
|
389
|
-
logger.
|
|
389
|
+
logger.debug("Client disconnected. Cancelling producer task %s", task_id)
|
|
390
390
|
if producer_task is not None:
|
|
391
391
|
producer_task.cancel()
|
|
392
392
|
if queue is not None:
|
|
@@ -472,7 +472,7 @@ class CodexRequestHandler(DefaultRequestHandler):
|
|
|
472
472
|
else:
|
|
473
473
|
try:
|
|
474
474
|
if asyncio.current_task() and asyncio.current_task().cancelled():
|
|
475
|
-
logger.
|
|
475
|
+
logger.debug(
|
|
476
476
|
"Client disconnected from message request. Cancelling task %s", task_id
|
|
477
477
|
)
|
|
478
478
|
producer_task.cancel()
|
|
@@ -3,13 +3,16 @@ from __future__ import annotations
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
|
+
from a2a.client.auth.interceptor import AuthInterceptor
|
|
6
7
|
from a2a.types import (
|
|
7
8
|
AgentCapabilities,
|
|
8
9
|
AgentCard,
|
|
9
10
|
Artifact,
|
|
11
|
+
HTTPAuthSecurityScheme,
|
|
10
12
|
Message,
|
|
11
13
|
Part,
|
|
12
14
|
Role,
|
|
15
|
+
SecurityScheme,
|
|
13
16
|
Task,
|
|
14
17
|
TaskArtifactUpdateEvent,
|
|
15
18
|
TaskIdParams,
|
|
@@ -23,6 +26,7 @@ from codex_a2a.client import (
|
|
|
23
26
|
A2AClient,
|
|
24
27
|
A2AClientConfig,
|
|
25
28
|
A2AUnsupportedBindingError,
|
|
29
|
+
StaticCredentialService,
|
|
26
30
|
)
|
|
27
31
|
from codex_a2a.client.types import A2ACancelTaskRequest, A2AGetTaskRequest, A2ASendRequest
|
|
28
32
|
|
|
@@ -203,6 +207,55 @@ async def test_build_client_maps_unsupported_transport_binding() -> None:
|
|
|
203
207
|
await client._build_client()
|
|
204
208
|
|
|
205
209
|
|
|
210
|
+
def test_build_interceptors_adds_sdk_auth_interceptor_for_config_credentials() -> None:
|
|
211
|
+
client = A2AClient(
|
|
212
|
+
A2AClientConfig(
|
|
213
|
+
agent_url="https://example.org",
|
|
214
|
+
auth_credentials={"bearerAuth": "peer-token"},
|
|
215
|
+
),
|
|
216
|
+
httpx_client=_MockAsyncHttpClient(),
|
|
217
|
+
card_resolver_factory=_MockAgentCardResolver,
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
interceptors = client._build_interceptors() # noqa: SLF001
|
|
221
|
+
|
|
222
|
+
assert any(isinstance(interceptor, AuthInterceptor) for interceptor in interceptors)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@pytest.mark.asyncio
|
|
226
|
+
async def test_static_credential_service_works_with_sdk_auth_interceptor() -> None:
|
|
227
|
+
card = AgentCard(
|
|
228
|
+
name="mock",
|
|
229
|
+
description="mock agent",
|
|
230
|
+
url="https://example.org",
|
|
231
|
+
version="1.0.0",
|
|
232
|
+
capabilities=AgentCapabilities(),
|
|
233
|
+
default_input_modes=["text/plain"],
|
|
234
|
+
default_output_modes=["text/plain"],
|
|
235
|
+
skills=[],
|
|
236
|
+
security_schemes={
|
|
237
|
+
"bearerAuth": SecurityScheme(
|
|
238
|
+
root=HTTPAuthSecurityScheme(
|
|
239
|
+
scheme="bearer",
|
|
240
|
+
bearer_format="opaque",
|
|
241
|
+
)
|
|
242
|
+
)
|
|
243
|
+
},
|
|
244
|
+
security=[{"bearerAuth": []}],
|
|
245
|
+
)
|
|
246
|
+
interceptor = AuthInterceptor(StaticCredentialService({"bearerAuth": "peer-token"}))
|
|
247
|
+
|
|
248
|
+
_payload, http_kwargs = await interceptor.intercept(
|
|
249
|
+
"message/send",
|
|
250
|
+
{},
|
|
251
|
+
{},
|
|
252
|
+
card,
|
|
253
|
+
None,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
assert http_kwargs["headers"]["Authorization"] == "Bearer peer-token"
|
|
257
|
+
|
|
258
|
+
|
|
206
259
|
def test_extract_text_prefers_stream_artifact_payload() -> None:
|
|
207
260
|
task = Task(
|
|
208
261
|
id="remote-task",
|
|
@@ -76,6 +76,7 @@ def test_settings_valid():
|
|
|
76
76
|
assert settings.a2a_version == __version__
|
|
77
77
|
assert settings.a2a_protocol_version == "0.3.0"
|
|
78
78
|
assert settings.a2a_supported_protocol_versions == ["0.3", "1.0"]
|
|
79
|
+
assert settings.a2a_log_level == "WARNING"
|
|
79
80
|
|
|
80
81
|
|
|
81
82
|
def test_settings_parse_a2a_supported_protocol_versions() -> None:
|
|
@@ -142,3 +142,21 @@ def test_tool_delta_chunks_normalizes_payload_and_rejects_unstructured_payload(c
|
|
|
142
142
|
|
|
143
143
|
assert rejected == []
|
|
144
144
|
assert "Suppressing non-structured tool_call payload" in caplog.text
|
|
145
|
+
assert "payload_type=str" in caplog.text
|
|
146
|
+
assert "bad-payload" not in caplog.text
|
|
147
|
+
|
|
148
|
+
caplog.clear()
|
|
149
|
+
with caplog.at_level(logging.WARNING):
|
|
150
|
+
rejected = tool_delta_chunks(
|
|
151
|
+
state=state,
|
|
152
|
+
delta_value={"unexpected": "secret-value"},
|
|
153
|
+
message_id=None,
|
|
154
|
+
source="delta",
|
|
155
|
+
task_id="task-1",
|
|
156
|
+
session_id="ses-1",
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
assert rejected == []
|
|
160
|
+
assert "Suppressing unrecognized tool_call payload" in caplog.text
|
|
161
|
+
assert "payload_keys=['unexpected']" in caplog.text
|
|
162
|
+
assert "secret-value" not in caplog.text
|
|
@@ -1899,7 +1899,7 @@ async def test_streaming_logs_idle_diagnostics_at_debug_when_only_transport_keep
|
|
|
1899
1899
|
|
|
1900
1900
|
|
|
1901
1901
|
@pytest.mark.asyncio
|
|
1902
|
-
async def
|
|
1902
|
+
async def test_streaming_logs_completion_observed_in_close_diagnostics_at_debug(
|
|
1903
1903
|
monkeypatch: pytest.MonkeyPatch,
|
|
1904
1904
|
caplog: pytest.LogCaptureFixture,
|
|
1905
1905
|
) -> None:
|
|
@@ -1922,7 +1922,7 @@ async def test_streaming_logs_completion_observed_in_close_diagnostics(
|
|
|
1922
1922
|
executor._should_stream = lambda context: True # type: ignore[method-assign]
|
|
1923
1923
|
queue = DummyEventQueue()
|
|
1924
1924
|
|
|
1925
|
-
caplog.set_level(logging.
|
|
1925
|
+
caplog.set_level(logging.DEBUG, logger="codex_a2a.execution.streaming")
|
|
1926
1926
|
|
|
1927
1927
|
await executor.execute(
|
|
1928
1928
|
make_request_context(task_id="task-close-log", context_id="ctx-close-log", text="go"),
|
|
@@ -9,6 +9,7 @@ import pytest
|
|
|
9
9
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
|
10
10
|
|
|
11
11
|
import codex_a2a.cli as cli
|
|
12
|
+
import codex_a2a.server.application as app_module
|
|
12
13
|
from codex_a2a import __version__
|
|
13
14
|
|
|
14
15
|
|
|
@@ -39,3 +40,8 @@ def test_cli_defaults_to_serve_when_no_subcommand() -> None:
|
|
|
39
40
|
assert cli.main([]) == 0
|
|
40
41
|
|
|
41
42
|
serve_mock.assert_called_once_with()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_normalize_log_level_defaults_invalid_values_to_warning() -> None:
|
|
46
|
+
assert app_module._normalize_log_level("debug") == "DEBUG"
|
|
47
|
+
assert app_module._normalize_log_level("not-a-level") == "WARNING"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|