opencode-a2a 1.1.0__tar.gz → 1.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/.github/dependabot.yml +5 -0
  2. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/AGENTS.md +2 -0
  3. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/PKG-INFO +9 -9
  4. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/README.md +6 -6
  5. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/pyproject.toml +2 -2
  6. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/a2a_protocol.py +0 -1
  7. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/a2a_utils.py +0 -4
  8. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/cli.py +15 -10
  9. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/execution/coordinator.py +1 -1
  10. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/execution/executor.py +0 -43
  11. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/extension_negotiation.py +2 -4
  12. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/jsonrpc/handlers/interrupt_queries.py +14 -13
  13. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/output_modes.py +20 -44
  14. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/server/application.py +16 -13
  15. opencode_a2a-1.1.2/src/opencode_a2a/server/context_helpers.py +76 -0
  16. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/server/middleware.py +3 -3
  17. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/server/task_store.py +2 -3
  18. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/server/task_store_sdk_compat.py +3 -4
  19. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a.egg-info/PKG-INFO +9 -9
  20. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a.egg-info/SOURCES.txt +2 -0
  21. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a.egg-info/requires.txt +2 -2
  22. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/execution/test_agent_helpers.py +33 -0
  23. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/execution/test_metrics.py +4 -3
  24. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/server/test_cli.py +28 -6
  25. opencode_a2a-1.1.2/tests/server/test_context_helpers.py +61 -0
  26. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/server/test_output_negotiation.py +61 -0
  27. opencode_a2a-1.1.2/tests/test_a2a_utils.py +100 -0
  28. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/uv.lock +79 -69
  29. opencode_a2a-1.1.0/src/opencode_a2a/server/context_helpers.py +0 -97
  30. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/.github/workflows/dependency-review.yml +0 -0
  31. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/.github/workflows/publish.yml +0 -0
  32. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/.github/workflows/validate.yml +0 -0
  33. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/.gitignore +0 -0
  34. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/.pre-commit-config.yaml +0 -0
  35. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/.secrets.baseline +0 -0
  36. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/CODE_OF_CONDUCT.md +0 -0
  37. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/CONTRIBUTING.md +0 -0
  38. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/LICENSE +0 -0
  39. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/MANIFEST.in +0 -0
  40. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/SECURITY.md +0 -0
  41. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/SUPPORT.md +0 -0
  42. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/docs/architecture.md +0 -0
  43. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/docs/compatibility.md +0 -0
  44. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/docs/conformance-triage.md +0 -0
  45. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/docs/conformance.md +0 -0
  46. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/docs/extension-specifications.md +0 -0
  47. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/docs/guide.md +0 -0
  48. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/docs/maintainer-architecture.md +0 -0
  49. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/scripts/README.md +0 -0
  50. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/scripts/check_coverage.py +0 -0
  51. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/scripts/conformance.sh +0 -0
  52. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/scripts/dependency_health.sh +0 -0
  53. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/scripts/doctor.sh +0 -0
  54. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/scripts/find_thin_wrappers.py +0 -0
  55. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/scripts/health_common.sh +0 -0
  56. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/scripts/lint.sh +0 -0
  57. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/scripts/smoke_test_built_cli.sh +0 -0
  58. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/setup.cfg +0 -0
  59. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/__init__.py +0 -0
  60. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/auth.py +0 -0
  61. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/client/__init__.py +0 -0
  62. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/client/agent_card.py +0 -0
  63. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/client/auth.py +0 -0
  64. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/client/client.py +0 -0
  65. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/client/config.py +0 -0
  66. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/client/error_mapping.py +0 -0
  67. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/client/errors.py +0 -0
  68. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/client/payload_text.py +0 -0
  69. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/client/polling.py +0 -0
  70. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/client/request_context.py +0 -0
  71. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/config.py +0 -0
  72. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/contracts/__init__.py +0 -0
  73. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/contracts/extensions/__init__.py +0 -0
  74. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/contracts/extensions/capabilities.py +0 -0
  75. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/contracts/extensions/catalog.py +0 -0
  76. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/contracts/extensions/compatibility.py +0 -0
  77. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/contracts/extensions/contract_docs.py +0 -0
  78. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/contracts/extensions/identifiers.py +0 -0
  79. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/contracts/extensions/private_params.py +0 -0
  80. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/contracts/extensions/public_params.py +0 -0
  81. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/execution/__init__.py +0 -0
  82. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/execution/event_helpers.py +0 -0
  83. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/execution/metrics.py +0 -0
  84. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/execution/session_manager.py +0 -0
  85. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/execution/stream_events.py +0 -0
  86. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/execution/stream_runtime.py +0 -0
  87. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/execution/stream_state.py +0 -0
  88. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/execution/tool_error_mapping.py +0 -0
  89. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/execution/tool_orchestration.py +0 -0
  90. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/execution/upstream_error_translator.py +0 -0
  91. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/interrupt_request_tracker.py +0 -0
  92. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/jsonrpc/__init__.py +0 -0
  93. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/jsonrpc/application.py +0 -0
  94. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/jsonrpc/dispatch.py +0 -0
  95. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/jsonrpc/error_responses.py +0 -0
  96. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/jsonrpc/handlers/__init__.py +0 -0
  97. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/jsonrpc/handlers/common.py +0 -0
  98. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/jsonrpc/handlers/interrupt_callbacks.py +0 -0
  99. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/jsonrpc/handlers/provider_discovery.py +0 -0
  100. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/jsonrpc/handlers/session_control.py +0 -0
  101. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/jsonrpc/handlers/session_lifecycle.py +0 -0
  102. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/jsonrpc/handlers/session_queries.py +0 -0
  103. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/jsonrpc/handlers/workspace_control.py +0 -0
  104. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/jsonrpc/methods.py +0 -0
  105. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/jsonrpc/models.py +0 -0
  106. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/jsonrpc/params.py +0 -0
  107. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/metadata_access.py +0 -0
  108. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/opencode_upstream_client.py +0 -0
  109. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/parsing.py +0 -0
  110. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/parts/__init__.py +0 -0
  111. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/parts/mapping.py +0 -0
  112. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/parts/text.py +0 -0
  113. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/profile/__init__.py +0 -0
  114. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/profile/runtime.py +0 -0
  115. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/protocol_versions.py +0 -0
  116. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/py.typed +0 -0
  117. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/runtime_state.py +0 -0
  118. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/sandbox_policy.py +0 -0
  119. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/server/__init__.py +0 -0
  120. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/server/agent_card.py +0 -0
  121. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/server/client_manager.py +0 -0
  122. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/server/database.py +0 -0
  123. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/server/lifespan.py +0 -0
  124. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/server/migrations.py +0 -0
  125. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/server/openapi.py +0 -0
  126. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/server/request_parsing.py +0 -0
  127. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/server/rest_tasks.py +0 -0
  128. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/server/state_store.py +0 -0
  129. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/task_states.py +0 -0
  130. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/trace_context.py +0 -0
  131. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a/upstream_taxonomy.py +0 -0
  132. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a.egg-info/dependency_links.txt +0 -0
  133. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a.egg-info/entry_points.txt +0 -0
  134. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/src/opencode_a2a.egg-info/top_level.txt +0 -0
  135. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/__init__.py +0 -0
  136. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/client/__init__.py +0 -0
  137. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/client/test_agent_card.py +0 -0
  138. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/client/test_client_config.py +0 -0
  139. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/client/test_client_facade.py +0 -0
  140. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/client/test_error_mapping.py +0 -0
  141. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/client/test_payload_text.py +0 -0
  142. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/client/test_polling.py +0 -0
  143. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/client/test_request_context.py +0 -0
  144. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/config/__init__.py +0 -0
  145. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/config/test_settings.py +0 -0
  146. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/conftest.py +0 -0
  147. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/contracts/__init__.py +0 -0
  148. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/contracts/test_extension_contract_consistency.py +0 -0
  149. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/execution/__init__.py +0 -0
  150. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/execution/test_agent_errors.py +0 -0
  151. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/execution/test_cancellation.py +0 -0
  152. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/execution/test_directory_validation.py +0 -0
  153. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/execution/test_multipart_input.py +0 -0
  154. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/execution/test_opencode_agent_session_binding.py +0 -0
  155. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/execution/test_session_lock_lifecycle.py +0 -0
  156. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/execution/test_session_ownership.py +0 -0
  157. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/execution/test_streaming_output_contract_blocks.py +0 -0
  158. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/execution/test_streaming_output_contract_core.py +0 -0
  159. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/execution/test_streaming_output_contract_interrupts.py +0 -0
  160. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/execution/test_streaming_output_contract_logging.py +0 -0
  161. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/jsonrpc/__init__.py +0 -0
  162. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/jsonrpc/test_application_dispatch.py +0 -0
  163. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/jsonrpc/test_dispatch_registry.py +0 -0
  164. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/jsonrpc/test_error_responses.py +0 -0
  165. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/jsonrpc/test_jsonrpc_methods.py +0 -0
  166. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/jsonrpc/test_jsonrpc_params.py +0 -0
  167. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/jsonrpc/test_jsonrpc_unsupported_method.py +0 -0
  168. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/jsonrpc/test_opencode_session_extension_commands.py +0 -0
  169. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/jsonrpc/test_opencode_session_extension_interrupts.py +0 -0
  170. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/jsonrpc/test_opencode_session_extension_lifecycle.py +0 -0
  171. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/jsonrpc/test_opencode_session_extension_prompt_async.py +0 -0
  172. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/jsonrpc/test_opencode_session_extension_queries.py +0 -0
  173. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/jsonrpc/test_opencode_workspace_control_extension.py +0 -0
  174. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/package/__init__.py +0 -0
  175. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/package/test_logging.py +0 -0
  176. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/package/test_typing_contract.py +0 -0
  177. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/package/test_version.py +0 -0
  178. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/parts/__init__.py +0 -0
  179. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/parts/test_parts_text.py +0 -0
  180. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/profile/__init__.py +0 -0
  181. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/profile/test_profile_runtime.py +0 -0
  182. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/scripts/__init__.py +0 -0
  183. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/scripts/test_script_health_contract.py +0 -0
  184. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/server/__init__.py +0 -0
  185. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/server/test_a2a_client_manager.py +0 -0
  186. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/server/test_agent_card.py +0 -0
  187. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/server/test_app_behaviors.py +0 -0
  188. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/server/test_auth.py +0 -0
  189. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/server/test_call_context_builder.py +0 -0
  190. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/server/test_cancel_contract.py +0 -0
  191. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/server/test_database_app_persistence.py +0 -0
  192. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/server/test_state_store.py +0 -0
  193. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/server/test_task_store_factory.py +0 -0
  194. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/server/test_transport_contract.py +0 -0
  195. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/support/__init__.py +0 -0
  196. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/support/async_iterators.py +0 -0
  197. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/support/fake_client_errors.py +0 -0
  198. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/support/helpers.py +0 -0
  199. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/support/interrupt_clients.py +0 -0
  200. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/support/jsonrpc_error_assertions.py +0 -0
  201. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/support/session_extensions.py +0 -0
  202. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/support/session_query_client.py +0 -0
  203. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/support/settings.py +0 -0
  204. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/support/streaming_output.py +0 -0
  205. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/support/workspace_control_client.py +0 -0
  206. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/test_metadata_access.py +0 -0
  207. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/test_parsing.py +0 -0
  208. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/test_trace_context.py +0 -0
  209. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/upstream/__init__.py +0 -0
  210. {opencode_a2a-1.1.0 → opencode_a2a-1.1.2}/tests/upstream/test_opencode_upstream_client_params.py +0 -0
@@ -5,6 +5,11 @@ updates:
5
5
  directory: "/"
6
6
  schedule:
7
7
  interval: "weekly"
8
+ ignore:
9
+ # a2a-sdk still constrains protobuf to <7, so major protobuf bumps are unsatisfiable.
10
+ - dependency-name: "protobuf"
11
+ update-types:
12
+ - "version-update:semver-major"
8
13
  open-pull-requests-limit: 1
9
14
  commit-message:
10
15
  prefix: "deps"
@@ -11,6 +11,8 @@ The following rules apply to coding agent collaboration in this repository. Thes
11
11
 
12
12
  - Follow the Git, Issue, and PR workflow defined in [CONTRIBUTING.md](CONTRIBUTING.md).
13
13
  - Use `gh` CLI for all issue/PR operations (reading, writing, and comments). Do not edit through the web UI.
14
+ - Do not use the Codex GitHub connector for any repository operation. Use `gh` CLI exclusively for issue, PR, review, comment, and metadata workflows.
15
+ - If `gh` CLI cannot complete a required GitHub operation because of permissions, authentication, or environment limits, stop and ask the human to perform or unblock the operation. Do not fall back to the Codex GitHub connector.
14
16
  - Create a new tracking issue for any development task that does not already have one.
15
17
  - Link the issue explicitly in PR descriptions (e.g., `Closes #xx`).
16
18
  - Keep status updates synchronized to the relevant issue/PR to avoid duplicate manual work.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opencode-a2a
3
- Version: 1.1.0
3
+ Version: 1.1.2
4
4
  Summary: OpenCode A2A runtime
5
5
  Author: liujuanjuan1984@Intelligent-Internet
6
6
  License-Expression: Apache-2.0
@@ -21,10 +21,10 @@ Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
22
  Requires-Dist: a2a-sdk==1.0.2
23
23
  Requires-Dist: aiosqlite<1.0,>=0.20
24
- Requires-Dist: fastapi<1.0,>=0.110
24
+ Requires-Dist: fastapi<1.0,>=0.139
25
25
  Requires-Dist: httpx<1.0,>=0.27
26
26
  Requires-Dist: pydantic<3.0,>=2.6
27
- Requires-Dist: pydantic-settings<3.0,>=2.2
27
+ Requires-Dist: pydantic-settings<3.0,>=2.14.2
28
28
  Requires-Dist: protobuf<7.0,>=6.33.5
29
29
  Requires-Dist: sqlalchemy<3.0,>=2.0
30
30
  Requires-Dist: sse-starlette<4.0,>=2.1
@@ -43,12 +43,12 @@ Dynamic: license-file
43
43
  # opencode-a2a
44
44
 
45
45
  ```text
46
- ___ ____ _ _ ____ _
47
- / _ \ _ __ ___ _ __ / ___|___ __| | ___ / \ |___ \ / \
48
- | | | | '_ \ / _ \ '_ \| | / _ \ / _` |/ _ \_____ / _ \ __) | / _ \
49
- | |_| | |_) | __/ | | | |__| (_) | (_| | __/_____/ ___ \ / __/ / ___ \
50
- \___/| .__/ \___|_| |_|\____\___/ \__,_|\___| /_/ \_\_____/_/ \_\
51
- |_|
46
+ ██████╗ ██████╗ ███████╗███╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗ █████╗ ██████╗ █████╗
47
+ ██╔═══██╗██╔══██╗██╔════╝████╗ ██║██╔════╝██╔═══██╗██╔══██╗██╔════╝ ██╔══██╗╚════██╗██╔══██╗
48
+ ██║ ██║██████╔╝█████╗ ██╔██╗ ██║██║ ██║ ██║██║ ██║█████╗█████╗███████║ █████╔╝███████║
49
+ ██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║██║ ██║ ██║██║ ██║██╔══╝╚════╝██╔══██║██╔═══╝ ██╔══██║
50
+ ╚██████╔╝██║ ███████╗██║ ╚████║╚██████╗╚██████╔╝██████╔╝███████╗ ██║ ██║███████╗██║ ██║
51
+ ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
52
52
  ```
53
53
 
54
54
  > Expose OpenCode through A2A.
@@ -1,12 +1,12 @@
1
1
  # opencode-a2a
2
2
 
3
3
  ```text
4
- ___ ____ _ _ ____ _
5
- / _ \ _ __ ___ _ __ / ___|___ __| | ___ / \ |___ \ / \
6
- | | | | '_ \ / _ \ '_ \| | / _ \ / _` |/ _ \_____ / _ \ __) | / _ \
7
- | |_| | |_) | __/ | | | |__| (_) | (_| | __/_____/ ___ \ / __/ / ___ \
8
- \___/| .__/ \___|_| |_|\____\___/ \__,_|\___| /_/ \_\_____/_/ \_\
9
- |_|
4
+ ██████╗ ██████╗ ███████╗███╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗ █████╗ ██████╗ █████╗
5
+ ██╔═══██╗██╔══██╗██╔════╝████╗ ██║██╔════╝██╔═══██╗██╔══██╗██╔════╝ ██╔══██╗╚════██╗██╔══██╗
6
+ ██║ ██║██████╔╝█████╗ ██╔██╗ ██║██║ ██║ ██║██║ ██║█████╗█████╗███████║ █████╔╝███████║
7
+ ██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║██║ ██║ ██║██║ ██║██╔══╝╚════╝██╔══██║██╔═══╝ ██╔══██║
8
+ ╚██████╔╝██║ ███████╗██║ ╚████║╚██████╗╚██████╔╝██████╔╝███████╗ ██║ ██║███████╗██║ ██║
9
+ ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
10
10
  ```
11
11
 
12
12
  > Expose OpenCode through A2A.
@@ -26,10 +26,10 @@ classifiers = [
26
26
  dependencies = [
27
27
  "a2a-sdk==1.0.2",
28
28
  "aiosqlite>=0.20,<1.0",
29
- "fastapi>=0.110,<1.0",
29
+ "fastapi>=0.139,<1.0",
30
30
  "httpx>=0.27,<1.0",
31
31
  "pydantic>=2.6,<3.0",
32
- "pydantic-settings>=2.2,<3.0",
32
+ "pydantic-settings>=2.14.2,<3.0",
33
33
  "protobuf>=6.33.5,<7.0",
34
34
  "sqlalchemy>=2.0,<3.0",
35
35
  "sse-starlette>=2.1,<4.0",
@@ -5,5 +5,4 @@ from a2a.utils.constants import AGENT_CARD_WELL_KNOWN_PATH as SDK_AGENT_CARD_WEL
5
5
 
6
6
  AGENT_CARD_WELL_KNOWN_PATH = SDK_AGENT_CARD_WELL_KNOWN_PATH
7
7
  EXTENDED_AGENT_CARD_PATH = "/extendedAgentCard"
8
- EXTENDED_AGENT_CARD_PATHS = (EXTENDED_AGENT_CARD_PATH,)
9
8
  CORE_JSONRPC_METHODS = tuple(JsonRpcDispatcher.METHOD_TO_MODEL)
@@ -16,10 +16,6 @@ def clone_proto(message: ProtoT) -> ProtoT:
16
16
  return cloned
17
17
 
18
18
 
19
- def proto_equals(left: ProtoMessage, right: ProtoMessage) -> bool:
20
- return bool(left == right)
21
-
22
-
23
19
  def _to_proto_value(value: Any) -> Value:
24
20
  proto_value = Value()
25
21
  if value is None:
@@ -15,12 +15,18 @@ from .config import Settings
15
15
  from .server.application import main as serve_main
16
16
 
17
17
  CLI_BRAND_BANNER = (
18
- " ___ ____ _ _ ____ _ \n"
19
- " / _ \\ _ __ ___ _ __ / ___|___ __| | ___ / \\ |___ \\ / \\ \n"
20
- "| | | | '_ \\ / _ \\ '_ \\| | / _ \\ / _` |/ _ \\_____ / _ \\ __) | / _ \\ \n"
21
- "| |_| | |_) | __/ | | | |__| (_) | (_| | __/_____/ ___ \\ / __/ / ___ \\ \n"
22
- " \\___/| .__/ \\___|_| |_|\\____\\___/ \\__,_|\\___| /_/ \\_\\_____/_/ \\_\\\n"
23
- " |_| "
18
+ " ██████╗ ██████╗ ███████╗███╗ ██╗ ██████╗ ██████╗"
19
+ " ██████╗ ███████╗ █████╗ ██████╗ █████╗ \n"
20
+ "██╔═══██╗██╔══██╗██╔════╝████╗ ██║██╔════╝██╔═══██╗"
21
+ "██╔══██╗██╔════╝ ██╔══██╗╚════██╗██╔══██╗\n"
22
+ "██║ ██║██████╔╝█████╗ ██╔██╗ ██║██║ ██║ ██║"
23
+ "██║ ██║█████╗█████╗███████║ █████╔╝███████║\n"
24
+ "██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║██║ ██║ ██║"
25
+ "██║ ██║██╔══╝╚════╝██╔══██║██╔═══╝ ██╔══██║\n"
26
+ "╚██████╔╝██║ ███████╗██║ ╚████║╚██████╗╚██████╔╝"
27
+ "██████╔╝███████╗ ██║ ██║███████╗██║ ██║\n"
28
+ " ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝ ╚═════╝ ╚═════╝"
29
+ " ╚═════╝ ╚══════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝"
24
30
  )
25
31
  PROJECT_REPOSITORY_URL = "https://github.com/Intelligent-Internet/opencode-a2a"
26
32
  HELP_FLAGS = frozenset({"-h", "--help"})
@@ -95,9 +101,8 @@ CALL_HELP = (
95
101
  " Service base URLs also work, but card URLs are the preferred example form."
96
102
  )
97
103
 
98
- ROOT_HELP_EPILOG = f"{OPENCODE_SETUP_HELP}\n\n{SERVE_ENVIRONMENT_HELP}\n\n{CALL_HELP}"
104
+ ROOT_HELP_EPILOG = "Command-specific help:\n opencode-a2a serve --help\n opencode-a2a call --help"
99
105
  SERVE_HELP_EPILOG = f"{OPENCODE_SETUP_HELP}\n\n{SERVE_ENVIRONMENT_HELP}"
100
- CALL_HELP_EPILOG = CALL_HELP
101
106
 
102
107
 
103
108
  class RootHelpFormatter(
@@ -224,7 +229,7 @@ def build_parser() -> argparse.ArgumentParser:
224
229
  CLI_BRAND_BANNER
225
230
  + "\n\n"
226
231
  + f"repo: {PROJECT_REPOSITORY_URL}\n"
227
- + "uv tool install --upgrade opencode-a2a\n"
232
+ + "uv tool install --upgrade opencode-a2a\n\n"
228
233
  + ROOT_DESCRIPTION
229
234
  ),
230
235
  formatter_class=RootHelpFormatter,
@@ -258,7 +263,7 @@ def build_parser() -> argparse.ArgumentParser:
258
263
  help="Call an A2A agent.",
259
264
  description="Call an A2A agent using the A2A protocol. A2A Protocol 1.0 only.",
260
265
  formatter_class=argparse.RawDescriptionHelpFormatter,
261
- epilog=CALL_HELP_EPILOG,
266
+ epilog=CALL_HELP,
262
267
  )
263
268
  call_parser.add_argument(
264
269
  "agent_url",
@@ -280,7 +280,7 @@ class ExecutionCoordinator:
280
280
  if self._prepared.streaming_request:
281
281
  self._stream_terminal_signal = asyncio.get_running_loop().create_future()
282
282
  self._stream_task = asyncio.create_task(
283
- self._executor._consume_opencode_stream(
283
+ self._executor._stream_runtime.consume(
284
284
  session_id=self._session_id,
285
285
  identity=self._prepared.identity,
286
286
  task_id=self._task_id,
@@ -46,12 +46,6 @@ from .event_helpers import _enqueue_artifact_update
46
46
  from .metrics import emit_metric
47
47
  from .session_manager import SessionManager
48
48
  from .stream_runtime import StreamRuntime
49
- from .stream_state import (
50
- _StreamOutputState,
51
- )
52
- from .upstream_error_translator import (
53
- _StreamTerminalSignal,
54
- )
55
49
 
56
50
  logger = logging.getLogger(__name__)
57
51
  _TEXT_PLAIN_MEDIA_TYPE = "text/plain"
@@ -497,40 +491,3 @@ class OpencodeAgentExecutor(AgentExecutor):
497
491
  # JSON-RPC transport sets method in call context state.
498
492
  method = call_context.state.get("method")
499
493
  return method == "SendStreamingMessage"
500
-
501
- async def _consume_opencode_stream(
502
- self,
503
- *,
504
- session_id: str,
505
- identity: str,
506
- task_id: str,
507
- context_id: str,
508
- artifact_id: str,
509
- stream_state: _StreamOutputState,
510
- event_queue: EventQueue,
511
- stop_event: asyncio.Event,
512
- terminal_signal: asyncio.Future[_StreamTerminalSignal],
513
- directory: str | None = None,
514
- workspace_id: str | None = None,
515
- allow_structured_output: bool = True,
516
- emit_session_metadata: bool = True,
517
- emit_streaming_metadata: bool = True,
518
- emit_interrupt_metadata: bool = True,
519
- ) -> None:
520
- await self._stream_runtime.consume(
521
- session_id=session_id,
522
- identity=identity,
523
- task_id=task_id,
524
- context_id=context_id,
525
- artifact_id=artifact_id,
526
- stream_state=stream_state,
527
- event_queue=event_queue,
528
- stop_event=stop_event,
529
- terminal_signal=terminal_signal,
530
- directory=directory,
531
- workspace_id=workspace_id,
532
- allow_structured_output=allow_structured_output,
533
- emit_session_metadata=emit_session_metadata,
534
- emit_streaming_metadata=emit_streaming_metadata,
535
- emit_interrupt_metadata=emit_interrupt_metadata,
536
- )
@@ -26,8 +26,7 @@ from .contracts.extensions import (
26
26
  WORKSPACE_CONTROL_METHODS,
27
27
  )
28
28
 
29
- _STREAMING_SHARED_METADATA_KEYS = frozenset({"stream", "progress", "usage"})
30
- _INTERRUPT_SHARED_METADATA_KEYS = frozenset({"interrupt"})
29
+ _STREAMING_SHARED_METADATA_KEYS = ("stream", "progress", "usage")
31
30
 
32
31
  JSONRPC_EXTENSION_URI_BY_METHOD: dict[str, str] = {
33
32
  **{method: SESSION_MANAGEMENT_EXTENSION_URI for method in SESSION_METHODS.values()},
@@ -107,8 +106,7 @@ def _set_filtered_metadata(
107
106
  for key in _STREAMING_SHARED_METADATA_KEYS:
108
107
  filtered_shared.pop(key, None)
109
108
  if INTERRUPT_CALLBACK_EXTENSION_URI not in requested_extensions:
110
- for key in _INTERRUPT_SHARED_METADATA_KEYS:
111
- filtered_shared.pop(key, None)
109
+ filtered_shared.pop("interrupt", None)
112
110
  if filtered_shared:
113
111
  filtered_metadata["shared"] = filtered_shared
114
112
  else:
@@ -10,18 +10,6 @@ from ..models import JSONRPCRequest
10
10
  from .common import build_internal_error_response, build_success_response, reject_unknown_fields
11
11
 
12
12
 
13
- def _binding_to_result_item(binding: Any) -> dict[str, Any]:
14
- return {
15
- "request_id": binding.request_id,
16
- "session_id": binding.session_id,
17
- "interrupt_type": binding.interrupt_type,
18
- "task_id": binding.task_id,
19
- "context_id": binding.context_id,
20
- "details": dict(binding.details) if isinstance(binding.details, dict) else None,
21
- "expires_at": binding.expires_at,
22
- }
23
-
24
-
25
13
  async def handle_interrupt_query_request(
26
14
  context: ExtensionHandlerContext,
27
15
  base_request: JSONRPCRequest,
@@ -58,5 +46,18 @@ async def handle_interrupt_query_request(
58
46
  return build_success_response(
59
47
  context,
60
48
  base_request.id,
61
- {"items": [_binding_to_result_item(item) for item in items]},
49
+ {
50
+ "items": [
51
+ {
52
+ "request_id": item.request_id,
53
+ "session_id": item.session_id,
54
+ "interrupt_type": item.interrupt_type,
55
+ "task_id": item.task_id,
56
+ "context_id": item.context_id,
57
+ "details": dict(item.details) if isinstance(item.details, dict) else None,
58
+ "expires_at": item.expires_at,
59
+ }
60
+ for item in items
61
+ ]
62
+ },
62
63
  )
@@ -32,35 +32,21 @@ OUTPUT_NEGOTIATION_ACCEPTED_OUTPUT_MODES_FIELD = "accepted_output_modes"
32
32
  _OPENCODE_METADATA_KEY = "opencode"
33
33
  _APPLICATION_JSON_MEDIA_TYPE = "application/json"
34
34
  _TEXT_PLAIN_MEDIA_TYPE = "text/plain"
35
- _STREAM_METADATA_SHARED_KEY = "shared"
36
- _STREAM_METADATA_STREAM_KEY = "stream"
37
- _STREAM_METADATA_BLOCK_TYPE_KEY = "block_type"
38
35
 
39
36
 
40
- def _accepted_output_modes_source(source: Any) -> Iterable[str] | None:
37
+ def normalize_accepted_output_modes(source: Any) -> tuple[str, ...] | None:
41
38
  if source is None:
42
39
  return None
43
-
44
40
  accepted = getattr(source, "accepted_output_modes", None) or getattr(
45
41
  source, "acceptedOutputModes", None
46
42
  )
47
43
  if accepted is not None:
48
44
  source = accepted
49
-
50
- if isinstance(source, str | bytes | bytearray | dict):
51
- return None
52
- if not isinstance(source, Iterable):
53
- return None
54
- return cast(Iterable[str], source)
55
-
56
-
57
- def normalize_accepted_output_modes(source: Any) -> tuple[str, ...] | None:
58
- accepted = _accepted_output_modes_source(source)
59
- if accepted is None:
45
+ if isinstance(source, str | bytes | bytearray | dict) or not isinstance(source, Iterable):
60
46
  return None
61
47
 
62
48
  normalized: list[str] = []
63
- for value in accepted:
49
+ for value in cast(Iterable[str], source):
64
50
  if not isinstance(value, str):
65
51
  continue
66
52
  mode = value.strip().lower()
@@ -194,9 +180,11 @@ def apply_accepted_output_modes(
194
180
  return replace_artifact_event_artifact(payload, artifact)
195
181
 
196
182
  if isinstance(payload, TaskStatusUpdateEvent):
197
- message = None
198
- if payload.status.HasField("message"):
199
- message = _filter_optional_message(payload.status.message, normalized)
183
+ message = (
184
+ _filter_message(payload.status.message, normalized)
185
+ if payload.status.HasField("message")
186
+ else None
187
+ )
200
188
  return replace_status_event_message(payload, message)
201
189
 
202
190
  if isinstance(payload, Task):
@@ -217,13 +205,13 @@ def _extract_artifact_stream_block_type(artifact: Artifact | None) -> str | None
217
205
  metadata = _normalize_metadata_mapping(artifact.metadata)
218
206
  if not metadata:
219
207
  return None
220
- shared = metadata.get(_STREAM_METADATA_SHARED_KEY)
208
+ shared = metadata.get("shared")
221
209
  if not isinstance(shared, dict):
222
210
  return None
223
- stream = shared.get(_STREAM_METADATA_STREAM_KEY)
211
+ stream = shared.get("stream")
224
212
  if not isinstance(stream, dict):
225
213
  return None
226
- block_type = stream.get(_STREAM_METADATA_BLOCK_TYPE_KEY)
214
+ block_type = stream.get("block_type")
227
215
  return block_type.strip() if isinstance(block_type, str) and block_type.strip() else None
228
216
 
229
217
 
@@ -375,7 +363,7 @@ class NegotiatingResultAggregator(ResultAggregator):
375
363
  def _filter_task(task: Task, accepted_output_modes: Collection[str]) -> Task:
376
364
  updated = clone_proto(task)
377
365
  if updated.status.HasField("message"):
378
- filtered_message = _filter_optional_message(updated.status.message, accepted_output_modes)
366
+ filtered_message = _filter_message(updated.status.message, accepted_output_modes)
379
367
  if filtered_message is None:
380
368
  updated.status.ClearField("message")
381
369
  else:
@@ -404,15 +392,6 @@ def _filter_task(task: Task, accepted_output_modes: Collection[str]) -> Task:
404
392
  return updated
405
393
 
406
394
 
407
- def _filter_optional_message(
408
- message: Message | None,
409
- accepted_output_modes: Collection[str],
410
- ) -> Message | None:
411
- if message is None:
412
- return None
413
- return _filter_message(message, accepted_output_modes)
414
-
415
-
416
395
  def _filter_message(
417
396
  message: Message,
418
397
  accepted_output_modes: Collection[str],
@@ -439,7 +418,14 @@ def _filter_parts(
439
418
  ) -> list[Part]:
440
419
  filtered: list[Part] = []
441
420
  for part in parts:
442
- media_type = _part_media_type(part)
421
+ if part.HasField("text"):
422
+ media_type = _TEXT_PLAIN_MEDIA_TYPE
423
+ elif part.HasField("data"):
424
+ media_type = _APPLICATION_JSON_MEDIA_TYPE
425
+ elif part.HasField("raw") or part.HasField("url"):
426
+ media_type = part.media_type or "application/octet-stream"
427
+ else:
428
+ media_type = None
443
429
  if media_type is None or accepts_output_mode(accepted_output_modes, media_type):
444
430
  filtered.append(part)
445
431
  continue
@@ -459,13 +445,3 @@ def _filter_parts(
459
445
  )
460
446
  )
461
447
  return filtered
462
-
463
-
464
- def _part_media_type(part: Part) -> str | None:
465
- if part.HasField("text"):
466
- return _TEXT_PLAIN_MEDIA_TYPE
467
- if part.HasField("data"):
468
- return _APPLICATION_JSON_MEDIA_TYPE
469
- if part.HasField("raw") or part.HasField("url"):
470
- return part.media_type or "application/octet-stream"
471
- return None
@@ -259,17 +259,6 @@ class OpencodeRequestHandler(LegacyRequestHandler):
259
259
  return "Task store unavailable while deleting task state."
260
260
  return "Task store unavailable."
261
261
 
262
- @classmethod
263
- def _task_store_failure_metadata(cls, operation: str) -> dict[str, dict[str, dict[str, str]]]:
264
- return {
265
- "opencode": {
266
- "error": {
267
- "type": TASK_STORE_ERROR_TYPE,
268
- "operation": operation,
269
- }
270
- }
271
- }
272
-
273
262
  @classmethod
274
263
  def _task_store_server_error(cls, exc: TaskStoreOperationError) -> InternalError:
275
264
  return InternalError(message=cls._task_store_failure_message(exc.operation))
@@ -295,7 +284,14 @@ class OpencodeRequestHandler(LegacyRequestHandler):
295
284
  context_id=context_id,
296
285
  status=TaskStatus(state=TaskState.TASK_STATE_FAILED, message=error_message),
297
286
  history=[error_message],
298
- metadata=cls._task_store_failure_metadata(operation),
287
+ metadata={
288
+ "opencode": {
289
+ "error": {
290
+ "type": TASK_STORE_ERROR_TYPE,
291
+ "operation": operation,
292
+ }
293
+ }
294
+ },
299
295
  )
300
296
 
301
297
  @classmethod
@@ -322,7 +318,14 @@ class OpencodeRequestHandler(LegacyRequestHandler):
322
318
  task_id=task_id,
323
319
  context_id=context_id,
324
320
  status=TaskStatus(state=TaskState.TASK_STATE_FAILED),
325
- metadata=cls._task_store_failure_metadata(operation),
321
+ metadata={
322
+ "opencode": {
323
+ "error": {
324
+ "type": TASK_STORE_ERROR_TYPE,
325
+ "operation": operation,
326
+ }
327
+ }
328
+ },
326
329
  ),
327
330
  )
328
331
 
@@ -0,0 +1,76 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping, MutableMapping
4
+
5
+ from a2a.auth.user import UnauthenticatedUser, User
6
+ from a2a.server.context import ServerCallContext
7
+
8
+
9
+ class AuthenticatedIdentityUser(User):
10
+ def __init__(self, identity: str) -> None:
11
+ self._identity = identity
12
+
13
+ @property
14
+ def is_authenticated(self) -> bool:
15
+ return True
16
+
17
+ @property
18
+ def user_name(self) -> str:
19
+ return self._identity
20
+
21
+
22
+ def normalize_server_call_context(context: ServerCallContext | None) -> ServerCallContext:
23
+ if context is None:
24
+ return ServerCallContext()
25
+
26
+ raw_state = getattr(context, "state", None)
27
+ raw_requested_extensions = getattr(context, "requested_extensions", None)
28
+ raw_tenant = getattr(context, "tenant", "")
29
+ raw_user = getattr(context, "user", None)
30
+
31
+ if isinstance(raw_state, MutableMapping):
32
+ state = raw_state
33
+ elif isinstance(raw_state, Mapping):
34
+ state = dict(raw_state)
35
+ else:
36
+ state = {}
37
+
38
+ if raw_requested_extensions is None:
39
+ requested_extensions: set[str] = set()
40
+ elif isinstance(raw_requested_extensions, set):
41
+ requested_extensions = raw_requested_extensions
42
+ elif isinstance(raw_requested_extensions, str):
43
+ requested_extensions = {raw_requested_extensions}
44
+ else:
45
+ try:
46
+ requested_extensions = {str(value) for value in raw_requested_extensions}
47
+ except TypeError:
48
+ requested_extensions = set()
49
+
50
+ tenant = raw_tenant if isinstance(raw_tenant, str) else ""
51
+ identity = state.get("identity")
52
+ normalized_identity = identity if isinstance(identity, str) else None
53
+ if normalized_identity and (
54
+ not isinstance(raw_user, User) or isinstance(raw_user, UnauthenticatedUser)
55
+ ):
56
+ user: User = AuthenticatedIdentityUser(normalized_identity)
57
+ elif isinstance(raw_user, User):
58
+ user = raw_user
59
+ else:
60
+ user = UnauthenticatedUser()
61
+
62
+ if (
63
+ isinstance(context, ServerCallContext)
64
+ and raw_state is state
65
+ and raw_user is user
66
+ and raw_tenant == tenant
67
+ and raw_requested_extensions == requested_extensions
68
+ ):
69
+ return context
70
+
71
+ return ServerCallContext(
72
+ state=state,
73
+ user=user,
74
+ tenant=tenant,
75
+ requested_extensions=requested_extensions,
76
+ )
@@ -14,7 +14,7 @@ from starlette.responses import StreamingResponse
14
14
 
15
15
  from ..a2a_protocol import (
16
16
  AGENT_CARD_WELL_KNOWN_PATH,
17
- EXTENDED_AGENT_CARD_PATHS,
17
+ EXTENDED_AGENT_CARD_PATH,
18
18
  )
19
19
  from ..auth import (
20
20
  authenticate_static_credential,
@@ -289,7 +289,7 @@ def install_runtime_middlewares(
289
289
  path = request.url.path
290
290
  if path == AGENT_CARD_WELL_KNOWN_PATH:
291
291
  supported_extensions = PUBLIC_EXTENSION_URIS
292
- elif path in EXTENDED_AGENT_CARD_PATHS:
292
+ elif path == EXTENDED_AGENT_CARD_PATH:
293
293
  supported_extensions = ALL_EXTENSION_URIS
294
294
  elif path == "/" or path.startswith("/v1/"):
295
295
  supported_extensions = PUBLIC_EXTENSION_URIS
@@ -313,7 +313,7 @@ def install_runtime_middlewares(
313
313
 
314
314
  path = request.url.path
315
315
  is_public_card = path == AGENT_CARD_WELL_KNOWN_PATH
316
- is_extended_card = path in EXTENDED_AGENT_CARD_PATHS
316
+ is_extended_card = path == EXTENDED_AGENT_CARD_PATH
317
317
  if not is_public_card and not is_extended_card:
318
318
  return await call_next(request)
319
319
 
@@ -14,7 +14,6 @@ from a2a.server.tasks.task_store import TaskStore
14
14
  from a2a.types import ListTasksRequest, ListTasksResponse, Task
15
15
  from sqlalchemy.engine import make_url
16
16
 
17
- from ..a2a_utils import proto_equals
18
17
  from ..config import Settings
19
18
  from ..task_states import TERMINAL_TASK_STATES
20
19
  from .context_helpers import normalize_server_call_context
@@ -67,7 +66,7 @@ class FirstTerminalStateWinsPolicy(TaskWritePolicy):
67
66
  persist=False,
68
67
  reason="state_overwrite_after_terminal_persistence",
69
68
  )
70
- if not proto_equals(incoming, existing):
69
+ if incoming != existing:
71
70
  return TaskPersistenceDecision(
72
71
  persist=False,
73
72
  reason="late_mutation_after_terminal_persistence",
@@ -238,7 +237,7 @@ class PolicyAwareTaskStore(TaskStoreDecorator):
238
237
  if (
239
238
  existing is not None
240
239
  and existing.status.state in TERMINAL_TASK_STATES
241
- and proto_equals(existing, task)
240
+ and existing == task
242
241
  ):
243
242
  return
244
243
  raise RuntimeError(
@@ -71,13 +71,12 @@ class DatabaseTaskStoreCompat:
71
71
  self._task_store.engine.url.render_as_string(hide_password=True)
72
72
  )
73
73
  table_name = self._shape.task_model.__table__.name
74
- required_indexes = frozenset({f"idx_{table_name}_owner_last_updated"})
75
74
  async with self._task_store.engine.begin() as conn:
76
75
  await conn.run_sync(
77
76
  lambda sync_conn: _validate_sdk_task_table_schema(
78
77
  sync_conn,
79
78
  table_name=table_name,
80
- required_indexes=required_indexes,
79
+ required_index=f"idx_{table_name}_owner_last_updated",
81
80
  database_url=database_url,
82
81
  )
83
82
  )
@@ -247,7 +246,7 @@ def _validate_sdk_task_table_schema(
247
246
  connection: Any,
248
247
  *,
249
248
  table_name: str,
250
- required_indexes: frozenset[str],
249
+ required_index: str,
251
250
  database_url: str,
252
251
  ) -> None:
253
252
  inspector = inspect(connection)
@@ -257,7 +256,7 @@ def _validate_sdk_task_table_schema(
257
256
  existing_columns = {column["name"] for column in inspector.get_columns(table_name)}
258
257
  existing_indexes = {index["name"] for index in inspector.get_indexes(table_name)}
259
258
  missing_columns = sorted(_REQUIRED_SCHEMA_COLUMNS - existing_columns)
260
- missing_indexes = sorted(required_indexes - existing_indexes)
259
+ missing_indexes = [] if required_index in existing_indexes else [required_index]
261
260
  if not missing_columns and not missing_indexes:
262
261
  return
263
262