codex-a2a 1.3.2__tar.gz → 1.3.4__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 (260) hide show
  1. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/.pre-commit-config.yaml +11 -2
  2. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/CONTRIBUTING.md +2 -0
  3. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/PKG-INFO +6 -6
  4. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/docs/compatibility.md +3 -1
  5. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/docs/conformance-triage.md +1 -1
  6. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/docs/conformance.md +1 -1
  7. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/docs/maintainer-architecture.md +10 -0
  8. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/pyproject.toml +5 -5
  9. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/scripts/README.md +1 -0
  10. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/scripts/validate_baseline.sh +2 -1
  11. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/execution/discovery_runtime.py +22 -16
  12. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/execution/exec_runtime.py +9 -3
  13. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/execution/review_runtime.py +33 -31
  14. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/execution/thread_lifecycle_runtime.py +18 -16
  15. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/server/application.py +15 -13
  16. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/server/output_negotiation.py +0 -109
  17. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/server/request_handler.py +148 -64
  18. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/upstream/client.py +6 -4
  19. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/upstream/stream_bridge.py +2 -2
  20. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a.egg-info/PKG-INFO +6 -6
  21. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a.egg-info/SOURCES.txt +5 -0
  22. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a.egg-info/requires.txt +5 -5
  23. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/execution/test_discovery_exec_runtime.py +100 -0
  24. codex_a2a-1.3.4/tests/execution/test_watch_lifecycle.py +69 -0
  25. codex_a2a-1.3.4/tests/fixtures/sdk_task_store_main_0a3fb68.sql +19 -0
  26. codex_a2a-1.3.4/tests/scripts/test_validate_baseline_script.py +89 -0
  27. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/server/test_request_handler.py +73 -43
  28. codex_a2a-1.3.4/tests/server/test_request_handler_lifecycle.py +343 -0
  29. codex_a2a-1.3.4/tests/server/test_sdk_upgrade.py +379 -0
  30. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/upstream/test_codex_client_params.py +22 -0
  31. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/uv.lock +319 -247
  32. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/.github/dependabot.yml +0 -0
  33. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/.github/workflows/ci.yml +0 -0
  34. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/.github/workflows/compatibility.yml +0 -0
  35. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/.github/workflows/dependency-health.yml +0 -0
  36. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/.github/workflows/publish.yml +0 -0
  37. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/.gitignore +0 -0
  38. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/.secrets.baseline +0 -0
  39. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/AGENTS.md +0 -0
  40. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/LICENSE +0 -0
  41. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/README.md +0 -0
  42. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/SECURITY.md +0 -0
  43. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/docs/a2a-extension-baseline.md +0 -0
  44. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/docs/architecture.md +0 -0
  45. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/docs/extension-specifications.md +0 -0
  46. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/docs/guide.md +0 -0
  47. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/docs/inspection/README.md +0 -0
  48. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/docs/inspection/error_redaction_policy.md +0 -0
  49. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/docs/inspection/release_integrity_policy.md +0 -0
  50. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/mypy.ini +0 -0
  51. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/scripts/audit_low_call_sites.py +0 -0
  52. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/scripts/check_codex_app_server_schema.py +0 -0
  53. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/scripts/check_dead_code.py +0 -0
  54. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/scripts/conformance.sh +0 -0
  55. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/scripts/dependency_health.sh +0 -0
  56. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/scripts/health_common.sh +0 -0
  57. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/scripts/publish_github_release.sh +0 -0
  58. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/scripts/smoke_live_codex_turn.py +0 -0
  59. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/scripts/smoke_test_built_cli.sh +0 -0
  60. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/scripts/smoke_test_live_codex.sh +0 -0
  61. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/scripts/sync_codex_docs.sh +0 -0
  62. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/scripts/tck_auth_plugin.py +0 -0
  63. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/scripts/validate_runtime_matrix.sh +0 -0
  64. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/setup.cfg +0 -0
  65. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/__init__.py +0 -0
  66. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/a2a_proto.py +0 -0
  67. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/auth.py +0 -0
  68. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/cli.py +0 -0
  69. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/client/__init__.py +0 -0
  70. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/client/agent_card.py +0 -0
  71. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/client/auth.py +0 -0
  72. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/client/client.py +0 -0
  73. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/client/config.py +0 -0
  74. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/client/errors.py +0 -0
  75. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/client/extension_negotiation.py +0 -0
  76. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/client/manager.py +0 -0
  77. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/client/network_policy.py +0 -0
  78. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/client/payload_text.py +0 -0
  79. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/client/request_context.py +0 -0
  80. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/config.py +0 -0
  81. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/contracts/__init__.py +0 -0
  82. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/contracts/extension_registry.py +0 -0
  83. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/contracts/extension_specs.py +0 -0
  84. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/contracts/extensions.py +0 -0
  85. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/contracts/runtime_output.py +0 -0
  86. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/execution/__init__.py +0 -0
  87. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/execution/executor.py +0 -0
  88. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/execution/output_mapping.py +0 -0
  89. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/execution/request_metadata.py +0 -0
  90. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/execution/request_overrides.py +0 -0
  91. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/execution/response_emitter.py +0 -0
  92. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/execution/session_runtime.py +0 -0
  93. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/execution/stream_chunks.py +0 -0
  94. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/execution/stream_interrupts.py +0 -0
  95. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/execution/stream_processor.py +0 -0
  96. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/execution/stream_state.py +0 -0
  97. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/execution/streaming.py +0 -0
  98. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/execution/tool_call_payloads.py +0 -0
  99. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/execution/watch_events.py +0 -0
  100. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/input_mapping.py +0 -0
  101. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/__init__.py +0 -0
  102. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/application.py +0 -0
  103. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/discovery_control.py +0 -0
  104. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/discovery_params.py +0 -0
  105. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/discovery_payload_mapping.py +0 -0
  106. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/discovery_query.py +0 -0
  107. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/dispatch.py +0 -0
  108. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/errors.py +0 -0
  109. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/exec_control.py +0 -0
  110. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/exec_control_params.py +0 -0
  111. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/hooks.py +0 -0
  112. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/interrupt_lifecycle.py +0 -0
  113. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/interrupt_params.py +0 -0
  114. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/interrupt_recovery.py +0 -0
  115. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/interrupt_recovery_params.py +0 -0
  116. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/interrupts.py +0 -0
  117. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/owner_guard.py +0 -0
  118. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/params.py +0 -0
  119. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/params_common.py +0 -0
  120. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/payload_mapping.py +0 -0
  121. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/query_params.py +0 -0
  122. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/request_models.py +0 -0
  123. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/review_control.py +0 -0
  124. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/review_control_params.py +0 -0
  125. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/session_query.py +0 -0
  126. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/thread_lifecycle_control.py +0 -0
  127. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/thread_lifecycle_params.py +0 -0
  128. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/turn_control.py +0 -0
  129. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/jsonrpc/turn_control_params.py +0 -0
  130. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/logging_context.py +0 -0
  131. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/media_modes.py +0 -0
  132. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/metrics.py +0 -0
  133. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/payload_helpers.py +0 -0
  134. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/profile/__init__.py +0 -0
  135. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/profile/runtime.py +0 -0
  136. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/protocol_versions.py +0 -0
  137. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/py.typed +0 -0
  138. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/redact.py +0 -0
  139. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/server/__init__.py +0 -0
  140. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/server/agent_card.py +0 -0
  141. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/server/call_context.py +0 -0
  142. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/server/database.py +0 -0
  143. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/server/http_middlewares.py +0 -0
  144. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/server/migrations.py +0 -0
  145. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/server/openapi.py +0 -0
  146. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/server/openapi_contract_fragments.py +0 -0
  147. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/server/push_config_store.py +0 -0
  148. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/server/runtime_limits.py +0 -0
  149. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/server/runtime_state.py +0 -0
  150. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/server/runtime_state_schema.py +0 -0
  151. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/server/task_store.py +0 -0
  152. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/skill_handles.py +0 -0
  153. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/upstream/__init__.py +0 -0
  154. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/upstream/conversation_facade.py +0 -0
  155. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/upstream/discovery_payloads.py +0 -0
  156. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/upstream/interrupt_bridge.py +0 -0
  157. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/upstream/interrupts.py +0 -0
  158. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/upstream/models.py +0 -0
  159. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/upstream/notification_mapping.py +0 -0
  160. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/upstream/request_mapping.py +0 -0
  161. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/upstream/startup.py +0 -0
  162. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a/upstream/transport.py +0 -0
  163. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a.egg-info/dependency_links.txt +0 -0
  164. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a.egg-info/entry_points.txt +0 -0
  165. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/src/codex_a2a.egg-info/top_level.txt +0 -0
  166. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/__init__.py +0 -0
  167. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/client/__init__.py +0 -0
  168. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/client/test_agent_card.py +0 -0
  169. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/client/test_cli.py +0 -0
  170. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/client/test_client_flow.py +0 -0
  171. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/client/test_client_http_headers.py +0 -0
  172. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/client/test_client_manager.py +0 -0
  173. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/client/test_errors.py +0 -0
  174. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/client/test_lifecycle.py +0 -0
  175. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/client/test_network_policy.py +0 -0
  176. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/client/test_payload_text.py +0 -0
  177. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/client/test_request_context.py +0 -0
  178. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/config/__init__.py +0 -0
  179. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/config/test_settings.py +0 -0
  180. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/contracts/__init__.py +0 -0
  181. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/contracts/test_extension_contract_consistency.py +0 -0
  182. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/contracts/test_extension_registry.py +0 -0
  183. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/execution/__init__.py +0 -0
  184. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/execution/test_agent_errors.py +0 -0
  185. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/execution/test_cancellation.py +0 -0
  186. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/execution/test_codex_agent_session_binding.py +0 -0
  187. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/execution/test_directory_validation.py +0 -0
  188. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/execution/test_error_redaction.py +0 -0
  189. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/execution/test_metrics.py +0 -0
  190. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/execution/test_request_metadata.py +0 -0
  191. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/execution/test_review_runtime.py +0 -0
  192. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/execution/test_session_ownership.py +0 -0
  193. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/execution/test_session_persistence.py +0 -0
  194. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/execution/test_session_runtime.py +0 -0
  195. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/execution/test_stream_chunks.py +0 -0
  196. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/execution/test_stream_interrupts.py +0 -0
  197. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/execution/test_streaming_output_contract.py +0 -0
  198. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/execution/test_thread_lifecycle_runtime.py +0 -0
  199. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/execution/test_tool_call_payloads.py +0 -0
  200. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/fixtures/codex_app_server/command_execution_output_delta.json +0 -0
  201. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/fixtures/codex_app_server/file_change_output_delta.json +0 -0
  202. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/jsonrpc/__init__.py +0 -0
  203. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/jsonrpc/test_codex_discovery_extension.py +0 -0
  204. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/jsonrpc/test_codex_exec_extension.py +0 -0
  205. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/jsonrpc/test_codex_session_extension.py +0 -0
  206. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/jsonrpc/test_codex_thread_lifecycle_extension.py +0 -0
  207. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/jsonrpc/test_codex_turn_review_extension.py +0 -0
  208. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/jsonrpc/test_dispatch.py +0 -0
  209. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/jsonrpc/test_error_redaction.py +0 -0
  210. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/jsonrpc/test_guard_hooks.py +0 -0
  211. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/jsonrpc/test_interrupt_lifecycle.py +0 -0
  212. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/jsonrpc/test_interrupt_params.py +0 -0
  213. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/jsonrpc/test_interrupt_protocol_handlers.py +0 -0
  214. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/jsonrpc/test_jsonrpc_models.py +0 -0
  215. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/jsonrpc/test_protocol_payload_mapping.py +0 -0
  216. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/package/__init__.py +0 -0
  217. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/package/test_logging.py +0 -0
  218. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/package/test_publish_github_release_script.py +0 -0
  219. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/package/test_release_distribution_contract.py +0 -0
  220. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/package/test_typing_contract.py +0 -0
  221. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/package/test_version.py +0 -0
  222. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/profile/__init__.py +0 -0
  223. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/profile/test_profile.py +0 -0
  224. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/scripts/__init__.py +0 -0
  225. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/scripts/test_audit_low_call_sites.py +0 -0
  226. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/scripts/test_check_codex_app_server_schema.py +0 -0
  227. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/scripts/test_script_health_contract.py +0 -0
  228. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/scripts/test_tck_auth_plugin.py +0 -0
  229. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/server/__init__.py +0 -0
  230. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/server/test_agent_card.py +0 -0
  231. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/server/test_auth.py +0 -0
  232. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/server/test_call_context_builder.py +0 -0
  233. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/server/test_cli.py +0 -0
  234. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/server/test_database.py +0 -0
  235. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/server/test_database_app_persistence.py +0 -0
  236. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/server/test_http_boundary.py +0 -0
  237. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/server/test_migrations.py +0 -0
  238. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/server/test_output_negotiation.py +0 -0
  239. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/server/test_push_config_store.py +0 -0
  240. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/server/test_runtime_limits.py +0 -0
  241. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/server/test_runtime_state.py +0 -0
  242. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/server/test_task_store.py +0 -0
  243. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/server/test_transport_contract.py +0 -0
  244. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/support/__init__.py +0 -0
  245. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/support/context.py +0 -0
  246. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/support/dummy_clients.py +0 -0
  247. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/support/fixtures.py +0 -0
  248. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/support/http_auth.py +0 -0
  249. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/support/jsonrpc_errors.py +0 -0
  250. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/support/settings.py +0 -0
  251. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/test_a2a_proto.py +0 -0
  252. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/test_input_mapping.py +0 -0
  253. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/test_protocol_versions.py +0 -0
  254. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/test_redact.py +0 -0
  255. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/test_skill_handles.py +0 -0
  256. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/upstream/__init__.py +0 -0
  257. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/upstream/test_interrupt_bridge.py +0 -0
  258. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/upstream/test_interrupt_persistence.py +0 -0
  259. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/upstream/test_modular_upstream_components.py +0 -0
  260. {codex_a2a-1.3.2 → codex_a2a-1.3.4}/tests/upstream/test_request_mapping.py +0 -0
@@ -6,12 +6,21 @@ repos:
6
6
  - id: trailing-whitespace
7
7
  - id: end-of-file-fixer
8
8
  - id: check-yaml
9
- - repo: https://github.com/astral-sh/ruff-pre-commit
10
- rev: v0.14.14
9
+ - repo: local
11
10
  hooks:
12
11
  - id: ruff
12
+ name: ruff check (uv)
13
+ entry: uv run --locked --extra dev ruff check --force-exclude
14
+ language: system
15
+ types_or: [python, pyi, jupyter]
16
+ require_serial: true
13
17
  args: ["--fix"]
14
18
  - id: ruff-format
19
+ name: ruff format (uv)
20
+ entry: uv run --locked --extra dev ruff format --force-exclude
21
+ language: system
22
+ types_or: [python, pyi, jupyter]
23
+ require_serial: true
15
24
  - repo: https://github.com/Yelp/detect-secrets
16
25
  rev: v1.5.0
17
26
  hooks:
@@ -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
+ 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
+
75
77
  ## Compatibility Expectations
76
78
 
77
79
  - The repository targets Python 3.11 through 3.14. CI uses 3.13 for the full quality gate and exercises 3.11, 3.12, and 3.14 in the runtime matrix.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codex-a2a
3
- Version: 1.3.2
3
+ Version: 1.3.4
4
4
  Summary: Codex A2A runtime adapter
5
5
  Author: liujuanjuan1984@Intelligent-Internet
6
6
  License-Expression: Apache-2.0
@@ -23,14 +23,14 @@ 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.2
26
+ Requires-Dist: a2a-sdk==1.1.5
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
30
- Requires-Dist: protobuf<7.0,>=6.33.5
30
+ Requires-Dist: protobuf<8.0,>=6.33.5
31
31
  Requires-Dist: pydantic<3.0,>=2.6
32
32
  Requires-Dist: pydantic-settings<3.0,>=2.2
33
- Requires-Dist: sqlalchemy<3.0,>=2.0
33
+ Requires-Dist: sqlalchemy[asyncio]<3.0,>=2.0
34
34
  Requires-Dist: sse-starlette<4.0,>=2.1
35
35
  Requires-Dist: uvicorn<0.53,>=0.29
36
36
  Provides-Extra: dev
@@ -40,8 +40,8 @@ Requires-Dist: pygments<3.0,>=2.20.0; extra == "dev"
40
40
  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
- Requires-Dist: diff-cover<10,>=9; extra == "dev"
44
- Requires-Dist: ruff>=0.5; extra == "dev"
43
+ Requires-Dist: diff-cover<11,>=9; extra == "dev"
44
+ Requires-Dist: ruff==0.16.9; extra == "dev"
45
45
  Requires-Dist: mypy<2.4,>=1.11; extra == "dev"
46
46
  Dynamic: license-file
47
47
 
@@ -7,7 +7,9 @@ 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.2` | Pinned in `pyproject.toml`; this is the [official A2A Python SDK](https://github.com/a2aproject/a2a-python), not a fork. |
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. |
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. |
11
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. |
12
14
  | Inbound A2A transports | JSON-RPC and HTTP+JSON | Both are repository-owned runtime surfaces. gRPC is not exposed by this adapter. |
13
15
  | Codex local runtime | Codex CLI/App Server | The local [Apache-2.0 Codex CLI](https://github.com/openai/codex) is the upstream process boundary. Scheduled smoke tests install the latest stable CLI and complete a real local App Server turn. |
@@ -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.2` as installed in this repository.
8
+ - `a2a-sdk==1.1.5` 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.2`.
77
+ - Some failures may come from TCK assumptions that do not match `a2a-sdk==1.1.5`.
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
 
@@ -74,6 +74,16 @@ flowchart TD
74
74
  - **Session State**: Stores the binding of `context_id` to `session_id`.
75
75
  - **Interrupt Store**: Stores pending interrupt requests to survive service restarts.
76
76
 
77
+ ## Background Streams and Shutdown
78
+
79
+ Background streams use SDK Source/Sink queues: producers persist once before
80
+ broadcasting, and slow or disconnected subscribers cannot block other consumers.
81
+ Shutdown drains SDK and adapter tasks before closing clients and stores.
82
+
83
+ Use the [standard upgrade procedure](./guide.md#released-cli-self-start) and restart;
84
+ the task-store format is unchanged. SDK upgrade regressions run in normal CI and
85
+ can be checked with `uv run pytest --no-cov tests/server/test_sdk_upgrade.py`.
86
+
77
87
  ## Configuration Layering
78
88
 
79
89
  Configuration is handled in `src/codex_a2a/config.py` using `pydantic-settings`. It is categorized by prefix:
@@ -25,14 +25,14 @@ classifiers = [
25
25
  "Topic :: Internet :: WWW/HTTP",
26
26
  ]
27
27
  dependencies = [
28
- "a2a-sdk==1.1.2",
28
+ "a2a-sdk==1.1.5",
29
29
  "aiosqlite>=0.20,<1.0",
30
30
  "fastapi>=0.121,<0.142",
31
31
  "httpx>=0.28.1,<0.29",
32
- "protobuf>=6.33.5,<7.0",
32
+ "protobuf>=6.33.5,<8.0",
33
33
  "pydantic>=2.6,<3.0",
34
34
  "pydantic-settings>=2.2,<3.0",
35
- "sqlalchemy>=2.0,<3.0",
35
+ "sqlalchemy[asyncio]>=2.0,<3.0",
36
36
  "sse-starlette>=2.1,<4.0",
37
37
  "uvicorn>=0.29,<0.53",
38
38
  ]
@@ -45,8 +45,8 @@ dev = [
45
45
  "pytest>=8.0",
46
46
  "pytest-asyncio>=0.23",
47
47
  "pytest-cov>=5.0,<8.0",
48
- "diff-cover>=9,<10",
49
- "ruff>=0.5",
48
+ "diff-cover>=9,<11",
49
+ "ruff==0.16.9",
50
50
  "mypy>=1.11,<2.4",
51
51
  ]
52
52
 
@@ -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
+ - 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.
41
42
  - [`.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.
42
43
  - 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).
43
44
  - Keep long-form documentation changes in `docs/` to avoid divergence.
@@ -19,8 +19,9 @@ run_pip_audit() {
19
19
  if XDG_CACHE_HOME="${audit_cache_dir}" uv run pip-audit --requirement "${requirement_file}"; then
20
20
  rm -rf "${audit_cache_dir}"
21
21
  return 0
22
+ else
23
+ status=$?
22
24
  fi
23
- status=$?
24
25
  rm -rf "${audit_cache_dir}"
25
26
  if [[ "${attempt}" -ge "${max_attempts}" ]]; then
26
27
  echo "pip-audit failed after ${attempt} attempts" >&2
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  import asyncio
4
4
  import logging
5
5
  import uuid
6
+ from contextlib import aclosing
6
7
  from dataclasses import dataclass
7
8
  from typing import TYPE_CHECKING, Any
8
9
 
@@ -94,22 +95,27 @@ class CodexDiscoveryRuntime:
94
95
 
95
96
  async def _run_watch(self, *, handle: DiscoveryWatchHandle, event_queue) -> None: # noqa: ANN001
96
97
  append = False
97
- async for event in self._client.stream_events(stop_event=handle.stop_event):
98
- payload = self._payload_from_event(event, events=handle.events)
99
- if payload is None:
100
- continue
101
- await enqueue_artifact_update(
102
- event_queue=event_queue,
103
- task_id=handle.task_id,
104
- context_id=handle.context_id,
105
- artifact_id=f"{handle.task_id}:discovery",
106
- part=new_data_part(payload),
107
- append=append,
108
- last_chunk=None,
109
- artifact_metadata={"codex": {"discovery_watch": {"events": sorted(handle.events)}}},
110
- event_metadata={"codex": {"discovery_watch": {"events": sorted(handle.events)}}},
111
- )
112
- append = True
98
+ async with aclosing(self._client.stream_events(stop_event=handle.stop_event)) as events:
99
+ async for event in events:
100
+ payload = self._payload_from_event(event, events=handle.events)
101
+ if payload is None:
102
+ continue
103
+ await enqueue_artifact_update(
104
+ event_queue=event_queue,
105
+ task_id=handle.task_id,
106
+ context_id=handle.context_id,
107
+ artifact_id=f"{handle.task_id}:discovery",
108
+ part=new_data_part(payload),
109
+ append=append,
110
+ last_chunk=None,
111
+ artifact_metadata={
112
+ "codex": {"discovery_watch": {"events": sorted(handle.events)}}
113
+ },
114
+ event_metadata={
115
+ "codex": {"discovery_watch": {"events": sorted(handle.events)}}
116
+ },
117
+ )
118
+ append = True
113
119
 
114
120
  def _payload_from_event(
115
121
  self,
@@ -278,10 +278,16 @@ class CodexExecRuntime:
278
278
  )
279
279
  finally:
280
280
  handle.stop_event.set()
281
+ child_tasks: list[asyncio.Future] = [exec_task]
281
282
  if pending_event_task is not None:
282
- pending_event_task.cancel()
283
- with contextlib.suppress(asyncio.CancelledError):
284
- await pending_event_task
283
+ child_tasks.append(pending_event_task)
284
+ for child_task in child_tasks:
285
+ if not child_task.done() and not (
286
+ isinstance(child_task, asyncio.Task) and child_task.cancelling()
287
+ ):
288
+ child_task.cancel()
289
+ await asyncio.gather(*child_tasks, return_exceptions=True)
290
+ await stream_iter.aclose()
285
291
 
286
292
  async def _emit_output_delta(
287
293
  self,
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  import asyncio
4
4
  import logging
5
5
  import uuid
6
+ from contextlib import aclosing
6
7
  from dataclasses import dataclass
7
8
  from typing import TYPE_CHECKING, Any
8
9
 
@@ -132,39 +133,40 @@ class CodexReviewRuntime:
132
133
  )
133
134
  append = True
134
135
 
135
- async for event in self._client.stream_events(stop_event=handle.stop_event):
136
- payload = self._payload_from_event(event, handle=handle)
137
- if payload is None:
138
- continue
139
- is_terminal = payload["event"] in {"review.completed", "review.failed"}
140
- await enqueue_artifact_update(
141
- event_queue=event_queue,
142
- task_id=handle.task_id,
143
- context_id=handle.context_id,
144
- artifact_id=f"{handle.task_id}:review-watch",
145
- part=new_data_part(payload),
146
- append=append,
147
- last_chunk=is_terminal,
148
- artifact_metadata=metadata,
149
- event_metadata=metadata,
150
- )
151
- append = True
152
- if is_terminal:
153
- await event_queue.enqueue_event(
154
- TaskStatusUpdateEvent(
155
- task_id=handle.task_id,
156
- context_id=handle.context_id,
157
- status=TaskStatus(
158
- state=(
159
- TaskState.TASK_STATE_COMPLETED
160
- if payload["event"] == "review.completed"
161
- else TaskState.TASK_STATE_FAILED
136
+ async with aclosing(self._client.stream_events(stop_event=handle.stop_event)) as events:
137
+ async for event in events:
138
+ payload = self._payload_from_event(event, handle=handle)
139
+ if payload is None:
140
+ continue
141
+ is_terminal = payload["event"] in {"review.completed", "review.failed"}
142
+ await enqueue_artifact_update(
143
+ event_queue=event_queue,
144
+ task_id=handle.task_id,
145
+ context_id=handle.context_id,
146
+ artifact_id=f"{handle.task_id}:review-watch",
147
+ part=new_data_part(payload),
148
+ append=append,
149
+ last_chunk=is_terminal,
150
+ artifact_metadata=metadata,
151
+ event_metadata=metadata,
152
+ )
153
+ append = True
154
+ if is_terminal:
155
+ await event_queue.enqueue_event(
156
+ TaskStatusUpdateEvent(
157
+ task_id=handle.task_id,
158
+ context_id=handle.context_id,
159
+ status=TaskStatus(
160
+ state=(
161
+ TaskState.TASK_STATE_COMPLETED
162
+ if payload["event"] == "review.completed"
163
+ else TaskState.TASK_STATE_FAILED
164
+ ),
162
165
  ),
163
- ),
164
- metadata=metadata,
166
+ metadata=metadata,
167
+ )
165
168
  )
166
- )
167
- break
169
+ break
168
170
 
169
171
  def _started_payload(self, handle: ReviewWatchHandle) -> dict[str, Any] | None:
170
172
  if "review.started" not in handle.events:
@@ -5,6 +5,7 @@ import hashlib
5
5
  import json
6
6
  import logging
7
7
  import uuid
8
+ from contextlib import aclosing
8
9
  from dataclasses import dataclass
9
10
  from typing import TYPE_CHECKING, Any, TypeGuard
10
11
 
@@ -212,22 +213,23 @@ class CodexThreadLifecycleRuntime:
212
213
  }
213
214
  }
214
215
  try:
215
- async for event in self._client.stream_events(stop_event=handle.stop_event):
216
- payload = self._payload_from_event(event, handle=handle)
217
- if payload is None:
218
- continue
219
- await enqueue_artifact_update(
220
- event_queue=event_queue,
221
- task_id=handle.task_id,
222
- context_id=handle.context_id,
223
- artifact_id=f"{handle.task_id}:thread-lifecycle",
224
- part=new_data_part(payload),
225
- append=append,
226
- last_chunk=None,
227
- artifact_metadata=metadata,
228
- event_metadata=metadata,
229
- )
230
- append = True
216
+ async with aclosing(self._client.stream_events(stop_event=handle.stop_event)) as events:
217
+ async for event in events:
218
+ payload = self._payload_from_event(event, handle=handle)
219
+ if payload is None:
220
+ continue
221
+ await enqueue_artifact_update(
222
+ event_queue=event_queue,
223
+ task_id=handle.task_id,
224
+ context_id=handle.context_id,
225
+ artifact_id=f"{handle.task_id}:thread-lifecycle",
226
+ part=new_data_part(payload),
227
+ append=append,
228
+ last_chunk=None,
229
+ artifact_metadata=metadata,
230
+ event_metadata=metadata,
231
+ )
232
+ append = True
231
233
  except asyncio.CancelledError:
232
234
  release_reason = "task_cancel"
233
235
  raise
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
- from contextlib import asynccontextmanager
4
+ from contextlib import AsyncExitStack, asynccontextmanager
5
5
  from typing import Any
6
6
 
7
7
  import uvicorn
@@ -288,22 +288,24 @@ def create_app(settings: Settings) -> FastAPI:
288
288
  persistence_summary["database_url"],
289
289
  persistence_summary["sqlite_tuning"],
290
290
  )
291
- await task_store_runtime.startup()
292
- await push_config_store_runtime.startup()
293
- await runtime_state_runtime.startup()
294
- try:
291
+ async with AsyncExitStack() as cleanup:
292
+ if shared_database_engine is not None:
293
+ cleanup.push_async_callback(shared_database_engine.dispose)
294
+ await task_store_runtime.startup()
295
+ cleanup.push_async_callback(task_store_runtime.shutdown)
296
+ await push_config_store_runtime.startup()
297
+ cleanup.push_async_callback(push_config_store_runtime.shutdown)
298
+ await runtime_state_runtime.startup()
299
+ cleanup.push_async_callback(runtime_state_runtime.shutdown)
300
+ cleanup.push_async_callback(client.close)
301
+ cleanup.push_async_callback(a2a_client_manager.close_all)
302
+ # Callbacks run in reverse order. Producers may still need clients
303
+ # and stores while unwinding, even if another cleanup callback fails.
304
+ cleanup.push_async_callback(handler.aclose)
295
305
  await client.restore_persisted_interrupt_requests()
296
306
  await client.startup_preflight()
297
307
  await thread_lifecycle_runtime.reconcile_persisted_watches()
298
308
  yield
299
- finally:
300
- await a2a_client_manager.close_all()
301
- await client.close()
302
- await runtime_state_runtime.shutdown()
303
- await push_config_store_runtime.shutdown()
304
- await task_store_runtime.shutdown()
305
- if shared_database_engine is not None:
306
- await shared_database_engine.dispose()
307
309
 
308
310
  context_builder = IdentityAwareCallContextBuilder()
309
311
  jsonrpc_methods = {
@@ -1,19 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
- import asyncio
4
3
  import json
5
4
  from collections.abc import Iterable
6
5
  from typing import Any, cast
7
6
 
8
- from a2a.server.events import EventConsumer
9
- from a2a.server.tasks import ResultAggregator, TaskManager
10
7
  from a2a.types import (
11
8
  Artifact,
12
9
  Message,
13
10
  Part,
14
11
  Task,
15
12
  TaskArtifactUpdateEvent,
16
- TaskState,
17
13
  TaskStatusUpdateEvent,
18
14
  )
19
15
  from google.protobuf.message import Message as ProtoMessage # type: ignore[import-untyped]
@@ -168,111 +164,6 @@ def apply_accepted_output_modes(
168
164
  return payload
169
165
 
170
166
 
171
- class NegotiatingResultAggregator(ResultAggregator):
172
- def __init__(
173
- self,
174
- task_manager: TaskManager,
175
- accepted_output_modes: Iterable[str] | None,
176
- ) -> None:
177
- super().__init__(task_manager)
178
- self._accepted_output_modes = normalize_accepted_output_modes(accepted_output_modes)
179
-
180
- def _transform_event(self, event: Any) -> Any | None:
181
- negotiated_event = apply_accepted_output_modes(event, self._accepted_output_modes)
182
- if negotiated_event is None:
183
- return None
184
- return annotate_output_negotiation_metadata(negotiated_event, self._accepted_output_modes)
185
-
186
- async def _persist_output_negotiation_metadata(self, event: Any) -> None:
187
- if not isinstance(event, TaskArtifactUpdateEvent):
188
- return
189
-
190
- accepted_output_modes = extract_accepted_output_modes_from_metadata(event.metadata)
191
- if accepted_output_modes is None:
192
- return
193
-
194
- task = await self.task_manager.ensure_task(event)
195
- merged_metadata = merge_output_negotiation_metadata(task.metadata, accepted_output_modes)
196
- if merged_metadata == task.metadata:
197
- return
198
- task.ClearField("metadata")
199
- if merged_metadata is not None:
200
- task.metadata.CopyFrom(merged_metadata)
201
- await self.task_manager._save_task(task)
202
-
203
- async def consume_and_emit(self, consumer: EventConsumer): # noqa: ANN201
204
- async for event in consumer.consume_all():
205
- transformed_event = self._transform_event(event)
206
- if transformed_event is None:
207
- continue
208
- await self._persist_output_negotiation_metadata(transformed_event)
209
- await self.task_manager.process(transformed_event)
210
- yield transformed_event
211
-
212
- async def consume_all(self, consumer: EventConsumer) -> Task | Message | None:
213
- async for event in consumer.consume_all():
214
- transformed_event = self._transform_event(event)
215
- if transformed_event is None:
216
- continue
217
- if isinstance(transformed_event, Message):
218
- self._message = transformed_event
219
- return transformed_event
220
- await self._persist_output_negotiation_metadata(transformed_event)
221
- await self.task_manager.process(transformed_event)
222
- return await self.task_manager.get_task()
223
-
224
- async def consume_and_break_on_interrupt(
225
- self,
226
- consumer: EventConsumer,
227
- blocking: bool = True,
228
- event_callback=None, # noqa: ANN001
229
- ) -> tuple[Task | Message | None, bool, asyncio.Task | None]:
230
- event_stream = consumer.consume_all()
231
- interrupted = False
232
- bg_task: asyncio.Task | None = None
233
- async for event in event_stream:
234
- transformed_event = self._transform_event(event)
235
- if transformed_event is None:
236
- continue
237
- if isinstance(transformed_event, Message):
238
- self._message = transformed_event
239
- return transformed_event, False, None
240
- await self._persist_output_negotiation_metadata(transformed_event)
241
- await self.task_manager.process(transformed_event)
242
-
243
- should_interrupt = False
244
- is_auth_required = (
245
- isinstance(transformed_event, Task | TaskStatusUpdateEvent)
246
- and transformed_event.status.state == TaskState.TASK_STATE_AUTH_REQUIRED
247
- )
248
- if is_auth_required:
249
- should_interrupt = True
250
- elif not blocking:
251
- should_interrupt = True
252
-
253
- if should_interrupt:
254
- bg_task = asyncio.create_task(
255
- self._continue_consuming(event_stream, event_callback)
256
- )
257
- interrupted = True
258
- break
259
- return await self.task_manager.get_task(), interrupted, bg_task
260
-
261
- async def _continue_consuming(
262
- self,
263
- event_stream,
264
- event_callback=None, # noqa: ANN001
265
- ) -> None:
266
- async for event in event_stream:
267
- transformed_event = self._transform_event(event)
268
- if transformed_event is None:
269
- continue
270
- await self._persist_output_negotiation_metadata(transformed_event)
271
- await self.task_manager.process(transformed_event)
272
- if event_callback:
273
- await event_callback()
274
-
275
-
276
167
  def _filter_task(task: Task, accepted_modes: frozenset[str]) -> Task:
277
168
  filtered_history: list[Message] = []
278
169
  for message in task.history or []: