codex-a2a 1.3.5__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.
Files changed (263) hide show
  1. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/.github/workflows/publish.yml +4 -1
  2. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/CONTRIBUTING.md +2 -0
  3. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/PKG-INFO +5 -5
  4. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/README.md +1 -1
  5. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/docs/compatibility.md +10 -4
  6. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/docs/conformance-triage.md +1 -1
  7. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/docs/conformance.md +1 -1
  8. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/docs/guide.md +3 -2
  9. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/mypy.ini +3 -1
  10. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/pyproject.toml +3 -4
  11. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/scripts/README.md +1 -0
  12. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/scripts/dependency_health.sh +4 -1
  13. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/scripts/validate_baseline.sh +4 -1
  14. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/a2a_proto.py +0 -28
  15. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/client/errors.py +4 -0
  16. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/server/application.py +43 -129
  17. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/server/http_middlewares.py +81 -52
  18. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/server/openapi.py +20 -0
  19. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/server/request_handler.py +86 -52
  20. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/server/runtime_limits.py +16 -2
  21. codex_a2a-1.4.0/src/codex_a2a/server/task_store.py +320 -0
  22. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a.egg-info/PKG-INFO +5 -5
  23. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a.egg-info/requires.txt +3 -3
  24. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/client/test_errors.py +10 -0
  25. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/execution/test_codex_agent_session_binding.py +6 -5
  26. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/package/test_release_distribution_contract.py +4 -2
  27. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/scripts/test_script_health_contract.py +2 -8
  28. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/scripts/test_validate_baseline_script.py +1 -0
  29. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/server/test_database_app_persistence.py +37 -3
  30. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/server/test_output_negotiation.py +4 -5
  31. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/server/test_request_handler.py +117 -12
  32. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/server/test_request_handler_lifecycle.py +16 -7
  33. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/server/test_runtime_limits.py +16 -0
  34. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/server/test_sdk_upgrade.py +182 -11
  35. codex_a2a-1.4.0/tests/server/test_task_store.py +438 -0
  36. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/server/test_transport_contract.py +266 -43
  37. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/test_input_mapping.py +6 -7
  38. codex_a2a-1.4.0/uv.lock +2389 -0
  39. codex_a2a-1.3.5/src/codex_a2a/server/task_store.py +0 -514
  40. codex_a2a-1.3.5/tests/server/test_task_store.py +0 -437
  41. codex_a2a-1.3.5/uv.lock +0 -2232
  42. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/.github/dependabot.yml +0 -0
  43. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/.github/workflows/ci.yml +0 -0
  44. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/.github/workflows/compatibility.yml +0 -0
  45. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/.github/workflows/dependency-health.yml +0 -0
  46. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/.gitignore +0 -0
  47. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/.pre-commit-config.yaml +0 -0
  48. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/.secrets.baseline +0 -0
  49. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/AGENTS.md +0 -0
  50. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/LICENSE +0 -0
  51. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/SECURITY.md +0 -0
  52. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/docs/a2a-extension-baseline.md +0 -0
  53. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/docs/architecture.md +0 -0
  54. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/docs/extension-specifications.md +0 -0
  55. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/docs/inspection/README.md +0 -0
  56. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/docs/inspection/error_redaction_policy.md +0 -0
  57. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/docs/inspection/release_integrity_policy.md +0 -0
  58. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/docs/maintainer-architecture.md +0 -0
  59. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/scripts/audit_low_call_sites.py +0 -0
  60. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/scripts/check_codex_app_server_schema.py +0 -0
  61. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/scripts/check_dead_code.py +0 -0
  62. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/scripts/conformance.sh +0 -0
  63. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/scripts/health_common.sh +0 -0
  64. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/scripts/publish_github_release.sh +0 -0
  65. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/scripts/smoke_live_codex_turn.py +0 -0
  66. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/scripts/smoke_test_built_cli.sh +0 -0
  67. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/scripts/smoke_test_live_codex.sh +0 -0
  68. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/scripts/sync_codex_docs.sh +0 -0
  69. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/scripts/tck_auth_plugin.py +0 -0
  70. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/scripts/validate_runtime_matrix.sh +0 -0
  71. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/setup.cfg +0 -0
  72. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/__init__.py +0 -0
  73. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/auth.py +0 -0
  74. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/cli.py +0 -0
  75. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/client/__init__.py +0 -0
  76. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/client/agent_card.py +0 -0
  77. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/client/auth.py +0 -0
  78. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/client/client.py +0 -0
  79. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/client/config.py +0 -0
  80. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/client/extension_negotiation.py +0 -0
  81. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/client/manager.py +0 -0
  82. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/client/network_policy.py +0 -0
  83. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/client/payload_text.py +0 -0
  84. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/client/request_context.py +0 -0
  85. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/config.py +0 -0
  86. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/contracts/__init__.py +0 -0
  87. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/contracts/extension_registry.py +0 -0
  88. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/contracts/extension_specs.py +0 -0
  89. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/contracts/extensions.py +0 -0
  90. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/contracts/runtime_output.py +0 -0
  91. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/execution/__init__.py +0 -0
  92. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/execution/discovery_runtime.py +0 -0
  93. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/execution/exec_runtime.py +0 -0
  94. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/execution/executor.py +0 -0
  95. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/execution/output_mapping.py +0 -0
  96. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/execution/request_metadata.py +0 -0
  97. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/execution/request_overrides.py +0 -0
  98. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/execution/response_emitter.py +0 -0
  99. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/execution/review_runtime.py +0 -0
  100. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/execution/session_runtime.py +0 -0
  101. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/execution/stream_chunks.py +0 -0
  102. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/execution/stream_interrupts.py +0 -0
  103. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/execution/stream_processor.py +0 -0
  104. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/execution/stream_state.py +0 -0
  105. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/execution/streaming.py +0 -0
  106. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/execution/thread_lifecycle_runtime.py +0 -0
  107. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/execution/tool_call_payloads.py +0 -0
  108. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/execution/watch_events.py +0 -0
  109. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/input_mapping.py +0 -0
  110. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/__init__.py +0 -0
  111. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/application.py +0 -0
  112. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/discovery_control.py +0 -0
  113. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/discovery_params.py +0 -0
  114. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/discovery_payload_mapping.py +0 -0
  115. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/discovery_query.py +0 -0
  116. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/dispatch.py +0 -0
  117. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/errors.py +0 -0
  118. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/exec_control.py +0 -0
  119. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/exec_control_params.py +0 -0
  120. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/hooks.py +0 -0
  121. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/interrupt_lifecycle.py +0 -0
  122. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/interrupt_params.py +0 -0
  123. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/interrupt_recovery.py +0 -0
  124. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/interrupt_recovery_params.py +0 -0
  125. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/interrupts.py +0 -0
  126. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/owner_guard.py +0 -0
  127. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/params.py +0 -0
  128. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/params_common.py +0 -0
  129. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/payload_mapping.py +0 -0
  130. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/query_params.py +0 -0
  131. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/request_models.py +0 -0
  132. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/review_control.py +0 -0
  133. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/review_control_params.py +0 -0
  134. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/session_query.py +0 -0
  135. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/thread_lifecycle_control.py +0 -0
  136. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/thread_lifecycle_params.py +0 -0
  137. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/turn_control.py +0 -0
  138. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/jsonrpc/turn_control_params.py +0 -0
  139. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/logging_context.py +0 -0
  140. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/media_modes.py +0 -0
  141. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/metrics.py +0 -0
  142. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/payload_helpers.py +0 -0
  143. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/profile/__init__.py +0 -0
  144. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/profile/runtime.py +0 -0
  145. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/protocol_versions.py +0 -0
  146. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/py.typed +0 -0
  147. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/redact.py +0 -0
  148. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/server/__init__.py +0 -0
  149. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/server/agent_card.py +0 -0
  150. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/server/call_context.py +0 -0
  151. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/server/database.py +0 -0
  152. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/server/migrations.py +0 -0
  153. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/server/openapi_contract_fragments.py +0 -0
  154. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/server/output_negotiation.py +0 -0
  155. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/server/push_config_store.py +0 -0
  156. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/server/runtime_state.py +0 -0
  157. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/server/runtime_state_schema.py +0 -0
  158. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/skill_handles.py +0 -0
  159. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/upstream/__init__.py +0 -0
  160. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/upstream/client.py +0 -0
  161. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/upstream/conversation_facade.py +0 -0
  162. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/upstream/discovery_payloads.py +0 -0
  163. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/upstream/interrupt_bridge.py +0 -0
  164. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/upstream/interrupts.py +0 -0
  165. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/upstream/models.py +0 -0
  166. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/upstream/notification_mapping.py +0 -0
  167. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/upstream/request_mapping.py +0 -0
  168. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/upstream/startup.py +0 -0
  169. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/upstream/stream_bridge.py +0 -0
  170. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a/upstream/transport.py +0 -0
  171. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a.egg-info/SOURCES.txt +0 -0
  172. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a.egg-info/dependency_links.txt +0 -0
  173. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a.egg-info/entry_points.txt +0 -0
  174. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/src/codex_a2a.egg-info/top_level.txt +0 -0
  175. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/__init__.py +0 -0
  176. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/client/__init__.py +0 -0
  177. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/client/test_agent_card.py +0 -0
  178. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/client/test_cli.py +0 -0
  179. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/client/test_client_flow.py +0 -0
  180. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/client/test_client_http_headers.py +0 -0
  181. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/client/test_client_manager.py +0 -0
  182. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/client/test_lifecycle.py +0 -0
  183. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/client/test_network_policy.py +0 -0
  184. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/client/test_payload_text.py +0 -0
  185. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/client/test_request_context.py +0 -0
  186. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/config/__init__.py +0 -0
  187. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/config/test_settings.py +0 -0
  188. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/contracts/__init__.py +0 -0
  189. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/contracts/test_extension_contract_consistency.py +0 -0
  190. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/contracts/test_extension_registry.py +0 -0
  191. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/execution/__init__.py +0 -0
  192. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/execution/test_agent_errors.py +0 -0
  193. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/execution/test_cancellation.py +0 -0
  194. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/execution/test_directory_validation.py +0 -0
  195. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/execution/test_discovery_exec_runtime.py +0 -0
  196. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/execution/test_error_redaction.py +0 -0
  197. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/execution/test_metrics.py +0 -0
  198. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/execution/test_request_metadata.py +0 -0
  199. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/execution/test_review_runtime.py +0 -0
  200. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/execution/test_session_ownership.py +0 -0
  201. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/execution/test_session_persistence.py +0 -0
  202. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/execution/test_session_runtime.py +0 -0
  203. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/execution/test_stream_chunks.py +0 -0
  204. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/execution/test_stream_interrupts.py +0 -0
  205. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/execution/test_streaming_output_contract.py +0 -0
  206. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/execution/test_thread_lifecycle_runtime.py +0 -0
  207. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/execution/test_tool_call_payloads.py +0 -0
  208. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/execution/test_watch_lifecycle.py +0 -0
  209. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/fixtures/codex_app_server/command_execution_output_delta.json +0 -0
  210. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/fixtures/codex_app_server/file_change_output_delta.json +0 -0
  211. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/fixtures/sdk_task_store_main_0a3fb68.sql +0 -0
  212. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/jsonrpc/__init__.py +0 -0
  213. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/jsonrpc/test_codex_discovery_extension.py +0 -0
  214. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/jsonrpc/test_codex_exec_extension.py +0 -0
  215. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/jsonrpc/test_codex_session_extension.py +0 -0
  216. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/jsonrpc/test_codex_thread_lifecycle_extension.py +0 -0
  217. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/jsonrpc/test_codex_turn_review_extension.py +0 -0
  218. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/jsonrpc/test_dispatch.py +0 -0
  219. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/jsonrpc/test_error_redaction.py +0 -0
  220. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/jsonrpc/test_guard_hooks.py +0 -0
  221. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/jsonrpc/test_interrupt_lifecycle.py +0 -0
  222. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/jsonrpc/test_interrupt_params.py +0 -0
  223. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/jsonrpc/test_interrupt_protocol_handlers.py +0 -0
  224. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/jsonrpc/test_jsonrpc_models.py +0 -0
  225. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/jsonrpc/test_protocol_payload_mapping.py +0 -0
  226. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/package/__init__.py +0 -0
  227. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/package/test_logging.py +0 -0
  228. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/package/test_publish_github_release_script.py +0 -0
  229. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/package/test_typing_contract.py +0 -0
  230. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/package/test_version.py +0 -0
  231. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/profile/__init__.py +0 -0
  232. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/profile/test_profile.py +0 -0
  233. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/scripts/__init__.py +0 -0
  234. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/scripts/test_audit_low_call_sites.py +0 -0
  235. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/scripts/test_check_codex_app_server_schema.py +0 -0
  236. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/scripts/test_tck_auth_plugin.py +0 -0
  237. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/server/__init__.py +0 -0
  238. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/server/test_agent_card.py +0 -0
  239. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/server/test_auth.py +0 -0
  240. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/server/test_call_context_builder.py +0 -0
  241. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/server/test_cli.py +0 -0
  242. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/server/test_database.py +0 -0
  243. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/server/test_http_boundary.py +0 -0
  244. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/server/test_migrations.py +0 -0
  245. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/server/test_push_config_store.py +0 -0
  246. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/server/test_runtime_state.py +0 -0
  247. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/support/__init__.py +0 -0
  248. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/support/context.py +0 -0
  249. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/support/dummy_clients.py +0 -0
  250. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/support/fixtures.py +0 -0
  251. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/support/http_auth.py +0 -0
  252. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/support/jsonrpc_errors.py +0 -0
  253. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/support/settings.py +0 -0
  254. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/test_a2a_proto.py +0 -0
  255. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/test_protocol_versions.py +0 -0
  256. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/test_redact.py +0 -0
  257. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/test_skill_handles.py +0 -0
  258. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/upstream/__init__.py +0 -0
  259. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/upstream/test_codex_client_params.py +0 -0
  260. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/upstream/test_interrupt_bridge.py +0 -0
  261. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/upstream/test_interrupt_persistence.py +0 -0
  262. {codex_a2a-1.3.5 → codex_a2a-1.4.0}/tests/upstream/test_modular_upstream_components.py +0 -0
  263. {codex_a2a-1.3.5 → 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 --requirement /tmp/runtime-requirements.txt
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.5
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.1.5
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.16.9; extra == "dev"
45
- Requires-Dist: mypy<2.4,>=1.11; extra == "dev"
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.1.2`.
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.1.2`.
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.1.5` | 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.1.5 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. |
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
- - Once a task reaches a terminal state, later conflicting persistence writes are dropped on a first-terminal-state-wins basis.
78
- - In the default SQLite-backed deployment, terminal-task persistence is guarded with an atomic database upsert instead of a process-local read-before-write check.
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.1.5` as installed in this repository.
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.1.5`.
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
- - Task persistence also applies a service-level resilience policy: once a task has been durably stored in a terminal state, later conflicting writes are dropped instead of overwriting that terminal snapshot.
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, terminal-task persistence is also database-guarded so late conflicting writes are dropped without relying on a process-local stale snapshot check.
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.1.5",
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.16.9",
50
- "mypy>=1.11,<2.4",
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
- XDG_CACHE_HOME="${audit_cache_dir}" uv run pip-audit --requirement "${dev_requirements}"
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
- if XDG_CACHE_HOME="${audit_cache_dir}" uv run pip-audit --requirement "${requirement_file}"; then
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 google.protobuf.json_format import MessageToDict, Parse # type: ignore[import-untyped]
16
- from starlette.requests import Request
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
- _STREAMING_REST_PATHS = frozenset({"/message:stream", "/tasks/{id}:subscribe"})
71
- _STREAM_BUDGET_REJECT_REASON = "STREAM_BUDGET_EXCEEDED"
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: Any,
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
- super().__init__(
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
- async def _handle_streaming(
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 _rest_error_response(error: Exception) -> JSONResponse:
124
- """Map a pre-SSE error to the same REST error payload the SDK emits."""
125
- payload = build_rest_error_payload(error)
126
- http_code = payload.get("error", {}).get("code", 500)
127
- return JSONResponse(content=payload, status_code=http_code)
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: Any,
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 and replaces the streaming routes with budgeted ones.
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
- base_routes = [route for route in sdk_routes if not _is_sdk_tenant_mount(route)]
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=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
  )