opencode-a2a 1.1.2__tar.gz → 1.2.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.
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/PKG-INFO +7 -5
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/README.md +4 -2
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/docs/compatibility.md +5 -2
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/docs/guide.md +18 -13
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/docs/maintainer-architecture.md +4 -5
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/pyproject.toml +9 -2
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/scripts/README.md +1 -0
- opencode_a2a-1.2.0/scripts/live_opencode_smoke.sh +286 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/auth.py +7 -2
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/config.py +15 -3
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/contracts/extensions/catalog.py +9 -9
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/execution/session_manager.py +74 -42
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/opencode_upstream_client.py +9 -1
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/server/agent_card.py +1 -1
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/server/application.py +17 -17
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/server/database.py +9 -14
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/server/middleware.py +15 -4
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/server/openapi.py +2 -2
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/server/task_store.py +1 -15
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/server/task_store_sdk_compat.py +1 -21
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a.egg-info/PKG-INFO +7 -5
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a.egg-info/SOURCES.txt +1 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a.egg-info/requires.txt +1 -1
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/config/test_settings.py +48 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/conftest.py +6 -16
- opencode_a2a-1.2.0/tests/execution/test_session_lock_lifecycle.py +68 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/server/test_agent_card.py +2 -3
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/server/test_app_behaviors.py +9 -9
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/server/test_call_context_builder.py +2 -2
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/server/test_state_store.py +0 -26
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/server/test_task_store_factory.py +2 -26
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/server/test_transport_contract.py +33 -33
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/upstream/test_opencode_upstream_client_params.py +92 -0
- opencode_a2a-1.2.0/uv.lock +1941 -0
- opencode_a2a-1.1.2/tests/execution/test_session_lock_lifecycle.py +0 -33
- opencode_a2a-1.1.2/uv.lock +0 -1644
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/.github/dependabot.yml +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/.github/workflows/dependency-review.yml +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/.github/workflows/publish.yml +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/.github/workflows/validate.yml +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/.gitignore +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/.pre-commit-config.yaml +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/.secrets.baseline +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/AGENTS.md +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/CODE_OF_CONDUCT.md +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/CONTRIBUTING.md +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/LICENSE +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/MANIFEST.in +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/SECURITY.md +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/SUPPORT.md +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/docs/architecture.md +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/docs/conformance-triage.md +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/docs/conformance.md +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/docs/extension-specifications.md +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/scripts/check_coverage.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/scripts/conformance.sh +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/scripts/dependency_health.sh +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/scripts/doctor.sh +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/scripts/find_thin_wrappers.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/scripts/health_common.sh +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/scripts/lint.sh +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/scripts/smoke_test_built_cli.sh +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/setup.cfg +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/__init__.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/a2a_protocol.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/a2a_utils.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/cli.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/client/__init__.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/client/agent_card.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/client/auth.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/client/client.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/client/config.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/client/error_mapping.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/client/errors.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/client/payload_text.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/client/polling.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/client/request_context.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/contracts/__init__.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/contracts/extensions/__init__.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/contracts/extensions/capabilities.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/contracts/extensions/compatibility.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/contracts/extensions/contract_docs.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/contracts/extensions/identifiers.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/contracts/extensions/private_params.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/contracts/extensions/public_params.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/execution/__init__.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/execution/coordinator.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/execution/event_helpers.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/execution/executor.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/execution/metrics.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/execution/stream_events.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/execution/stream_runtime.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/execution/stream_state.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/execution/tool_error_mapping.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/execution/tool_orchestration.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/execution/upstream_error_translator.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/extension_negotiation.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/interrupt_request_tracker.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/jsonrpc/__init__.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/jsonrpc/application.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/jsonrpc/dispatch.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/jsonrpc/error_responses.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/jsonrpc/handlers/__init__.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/jsonrpc/handlers/common.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/jsonrpc/handlers/interrupt_callbacks.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/jsonrpc/handlers/interrupt_queries.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/jsonrpc/handlers/provider_discovery.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/jsonrpc/handlers/session_control.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/jsonrpc/handlers/session_lifecycle.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/jsonrpc/handlers/session_queries.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/jsonrpc/handlers/workspace_control.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/jsonrpc/methods.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/jsonrpc/models.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/jsonrpc/params.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/metadata_access.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/output_modes.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/parsing.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/parts/__init__.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/parts/mapping.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/parts/text.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/profile/__init__.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/profile/runtime.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/protocol_versions.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/py.typed +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/runtime_state.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/sandbox_policy.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/server/__init__.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/server/client_manager.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/server/context_helpers.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/server/lifespan.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/server/migrations.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/server/request_parsing.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/server/rest_tasks.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/server/state_store.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/task_states.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/trace_context.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a/upstream_taxonomy.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a.egg-info/dependency_links.txt +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a.egg-info/entry_points.txt +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/src/opencode_a2a.egg-info/top_level.txt +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/__init__.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/client/__init__.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/client/test_agent_card.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/client/test_client_config.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/client/test_client_facade.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/client/test_error_mapping.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/client/test_payload_text.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/client/test_polling.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/client/test_request_context.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/config/__init__.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/contracts/__init__.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/contracts/test_extension_contract_consistency.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/execution/__init__.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/execution/test_agent_errors.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/execution/test_agent_helpers.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/execution/test_cancellation.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/execution/test_directory_validation.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/execution/test_metrics.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/execution/test_multipart_input.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/execution/test_opencode_agent_session_binding.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/execution/test_session_ownership.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/execution/test_streaming_output_contract_blocks.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/execution/test_streaming_output_contract_core.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/execution/test_streaming_output_contract_interrupts.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/execution/test_streaming_output_contract_logging.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/jsonrpc/__init__.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/jsonrpc/test_application_dispatch.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/jsonrpc/test_dispatch_registry.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/jsonrpc/test_error_responses.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/jsonrpc/test_jsonrpc_methods.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/jsonrpc/test_jsonrpc_params.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/jsonrpc/test_jsonrpc_unsupported_method.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/jsonrpc/test_opencode_session_extension_commands.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/jsonrpc/test_opencode_session_extension_interrupts.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/jsonrpc/test_opencode_session_extension_lifecycle.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/jsonrpc/test_opencode_session_extension_prompt_async.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/jsonrpc/test_opencode_session_extension_queries.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/jsonrpc/test_opencode_workspace_control_extension.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/package/__init__.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/package/test_logging.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/package/test_typing_contract.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/package/test_version.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/parts/__init__.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/parts/test_parts_text.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/profile/__init__.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/profile/test_profile_runtime.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/scripts/__init__.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/scripts/test_script_health_contract.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/server/__init__.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/server/test_a2a_client_manager.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/server/test_auth.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/server/test_cancel_contract.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/server/test_cli.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/server/test_context_helpers.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/server/test_database_app_persistence.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/server/test_output_negotiation.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/support/__init__.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/support/async_iterators.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/support/fake_client_errors.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/support/helpers.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/support/interrupt_clients.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/support/jsonrpc_error_assertions.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/support/session_extensions.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/support/session_query_client.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/support/settings.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/support/streaming_output.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/support/workspace_control_client.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/test_a2a_utils.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/test_metadata_access.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/test_parsing.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/test_trace_context.py +0 -0
- {opencode_a2a-1.1.2 → opencode_a2a-1.2.0}/tests/upstream/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opencode-a2a
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: OpenCode A2A runtime
|
|
5
5
|
Author: liujuanjuan1984@Intelligent-Internet
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -8,7 +8,7 @@ Project-URL: Homepage, https://github.com/Intelligent-Internet/opencode-a2a
|
|
|
8
8
|
Project-URL: Repository, https://github.com/Intelligent-Internet/opencode-a2a
|
|
9
9
|
Project-URL: Issues, https://github.com/Intelligent-Internet/opencode-a2a/issues
|
|
10
10
|
Keywords: a2a,opencode,fastapi,json-rpc,sse
|
|
11
|
-
Classifier: Development Status ::
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
12
|
Classifier: Intended Audience :: Developers
|
|
13
13
|
Classifier: Programming Language :: Python :: 3
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
@@ -19,7 +19,7 @@ Classifier: Topic :: Internet :: WWW/HTTP
|
|
|
19
19
|
Requires-Python: >=3.11
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
|
-
Requires-Dist: a2a-sdk==1.
|
|
22
|
+
Requires-Dist: a2a-sdk==1.1.2
|
|
23
23
|
Requires-Dist: aiosqlite<1.0,>=0.20
|
|
24
24
|
Requires-Dist: fastapi<1.0,>=0.139
|
|
25
25
|
Requires-Dist: httpx<1.0,>=0.27
|
|
@@ -133,9 +133,9 @@ curl http://127.0.0.1:8000/.well-known/agent-card.json
|
|
|
133
133
|
|
|
134
134
|
## Capabilities
|
|
135
135
|
|
|
136
|
-
- A2A HTTP+JSON endpoints such as `/
|
|
136
|
+
- A2A HTTP+JSON endpoints such as `/message:send` and `/message:stream`
|
|
137
137
|
- A2A JSON-RPC support on `POST /`
|
|
138
|
-
- SDK-owned A2A task surfaces such as `GET /
|
|
138
|
+
- SDK-owned A2A task surfaces such as `GET /tasks`, task push notification config routes, JSON-RPC `GetExtendedAgentCard`, and HTTP `GET /extendedAgentCard`
|
|
139
139
|
- Peering capabilities: can act as a client via `opencode-a2a call`
|
|
140
140
|
- Autonomous tool execution: supports `a2a_call` tool for outbound agent-to-agent communication
|
|
141
141
|
- SSE streaming with normalized `text`, `reasoning`, and `tool_call` blocks
|
|
@@ -201,6 +201,8 @@ This repository improves the service boundary around OpenCode, but it does not t
|
|
|
201
201
|
- Provider auth and default model configuration remain on the OpenCode side; deployment-time precedence details and HOME/XDG state impact are documented in [docs/guide.md](docs/guide.md#troubleshooting-provider-auth-state).
|
|
202
202
|
- Use `A2A_CLIENT_BEARER_TOKEN` for server-side outbound peer calls initiated by `a2a_call`.
|
|
203
203
|
- Deployment supervision is intentionally BYO. Use `systemd`, Docker, Kubernetes, or another supervisor if you need long-running operation.
|
|
204
|
+
- The supported persistence profile runs one `opencode-a2a` application process against its own local SQLite database. Do not run multiple Uvicorn workers or replicas against the same database file.
|
|
205
|
+
- PostgreSQL and other SQLAlchemy dialects are not part of the supported deployment matrix.
|
|
204
206
|
- For mutually untrusted tenants, run separate instance pairs with isolated users, containers, workspaces, credentials, and ports.
|
|
205
207
|
|
|
206
208
|
Read before deployment:
|
|
@@ -91,9 +91,9 @@ curl http://127.0.0.1:8000/.well-known/agent-card.json
|
|
|
91
91
|
|
|
92
92
|
## Capabilities
|
|
93
93
|
|
|
94
|
-
- A2A HTTP+JSON endpoints such as `/
|
|
94
|
+
- A2A HTTP+JSON endpoints such as `/message:send` and `/message:stream`
|
|
95
95
|
- A2A JSON-RPC support on `POST /`
|
|
96
|
-
- SDK-owned A2A task surfaces such as `GET /
|
|
96
|
+
- SDK-owned A2A task surfaces such as `GET /tasks`, task push notification config routes, JSON-RPC `GetExtendedAgentCard`, and HTTP `GET /extendedAgentCard`
|
|
97
97
|
- Peering capabilities: can act as a client via `opencode-a2a call`
|
|
98
98
|
- Autonomous tool execution: supports `a2a_call` tool for outbound agent-to-agent communication
|
|
99
99
|
- SSE streaming with normalized `text`, `reasoning`, and `tool_call` blocks
|
|
@@ -159,6 +159,8 @@ This repository improves the service boundary around OpenCode, but it does not t
|
|
|
159
159
|
- Provider auth and default model configuration remain on the OpenCode side; deployment-time precedence details and HOME/XDG state impact are documented in [docs/guide.md](docs/guide.md#troubleshooting-provider-auth-state).
|
|
160
160
|
- Use `A2A_CLIENT_BEARER_TOKEN` for server-side outbound peer calls initiated by `a2a_call`.
|
|
161
161
|
- Deployment supervision is intentionally BYO. Use `systemd`, Docker, Kubernetes, or another supervisor if you need long-running operation.
|
|
162
|
+
- The supported persistence profile runs one `opencode-a2a` application process against its own local SQLite database. Do not run multiple Uvicorn workers or replicas against the same database file.
|
|
163
|
+
- PostgreSQL and other SQLAlchemy dialects are not part of the supported deployment matrix.
|
|
162
164
|
- For mutually untrusted tenants, run separate instance pairs with isolated users, containers, workspaces, credentials, and ports.
|
|
163
165
|
|
|
164
166
|
Read before deployment:
|
|
@@ -7,6 +7,9 @@ This document defines the compatibility promises `opencode-a2a` currently uphold
|
|
|
7
7
|
- Python versions: 3.11, 3.12, 3.13
|
|
8
8
|
- A2A SDK line: `1.x.y`
|
|
9
9
|
- Supported A2A protocol line: `1.0`
|
|
10
|
+
- OpenCode runtime line: `1.18.x` (verified with `1.18.19`)
|
|
11
|
+
|
|
12
|
+
This is the single canonical place where the supported upstream OpenCode version line is declared. Keep it updated together with the live integration smoke check (`./scripts/live_opencode_smoke.sh`) and do not duplicate the version line elsewhere.
|
|
10
13
|
|
|
11
14
|
The repository currently pins one concrete SDK release in `pyproject.toml` within that v1 line. Upgrade the SDK deliberately rather than relying on floating dependency resolution. The SDK-owned core JSON-RPC method set follows that pinned release and is locked by repository tests so SDK upgrades trigger an explicit compatibility review.
|
|
12
15
|
|
|
@@ -41,7 +44,7 @@ External TCK runs and local conformance experiments are investigation inputs. Th
|
|
|
41
44
|
|
|
42
45
|
## Compatibility-Sensitive Surface
|
|
43
46
|
|
|
44
|
-
This repository
|
|
47
|
+
This repository ships as a beta project. Its documented public contracts are intended to remain stable, while implementation details and explicitly provider-private surfaces may continue to evolve. These declared surfaces should not drift silently:
|
|
45
48
|
|
|
46
49
|
- core A2A send / stream / task methods
|
|
47
50
|
- v1-only request/response payloads and enum values
|
|
@@ -88,7 +91,7 @@ Task-store behavior that should remain stable for clients:
|
|
|
88
91
|
- accepted output-mode negotiation for a task is persisted with the task so later reads keep the same filtered output contract
|
|
89
92
|
- adapter-managed migrations only own adapter state tables; SDK-managed task schema remains SDK-owned
|
|
90
93
|
|
|
91
|
-
The
|
|
94
|
+
The supported persistence profile is one application process using its own local SQLite database. Multiple Uvicorn workers or application replicas must not share that SQLite file. PostgreSQL and other SQLAlchemy dialects are not supported deployment targets; any apparent dialect compatibility is implementation latitude rather than a public promise.
|
|
92
95
|
|
|
93
96
|
## Extension Stability
|
|
94
97
|
|
|
@@ -5,9 +5,11 @@ This guide covers configuration, authentication, API behavior, streaming re-subs
|
|
|
5
5
|
## Transport Contracts
|
|
6
6
|
|
|
7
7
|
- The service supports both transports:
|
|
8
|
-
- HTTP+JSON (REST endpoints such as `/
|
|
8
|
+
- HTTP+JSON (REST endpoints such as `/message:send`)
|
|
9
9
|
- JSON-RPC (`POST /`)
|
|
10
10
|
- Agent Card exposes both HTTP+JSON and JSON-RPC through `supportedInterfaces`.
|
|
11
|
+
- HTTP+JSON REST endpoints are served at the root path (e.g. `/message:send`, `/tasks`). A2A 1.0 negotiates protocol version via the `A2A-Version` header, not a URL prefix, so the URL path carries no version segment.
|
|
12
|
+
- Breaking change for earlier releases: versions through v1.1.2 served the HTTP+JSON surface under a `/v1` prefix. Deployments upgrading from those releases must repoint clients, reverse proxies, and any `/v1`-prefixed routes to the root paths.
|
|
11
13
|
- The public Agent Card is intentionally slimmed to the minimum discovery surface; per-extension disclosure policy is defined in [`extension-specifications.md`](./extension-specifications.md).
|
|
12
14
|
- Detailed provider-private contracts are served through the authenticated extended card endpoint `/extendedAgentCard`.
|
|
13
15
|
- Agent Card responses emit weak `ETag` and `Cache-Control`; clients should revalidate cached cards instead of repeatedly fetching full payloads.
|
|
@@ -47,9 +49,10 @@ Key variables to understand protocol behavior:
|
|
|
47
49
|
- `A2A_INTERRUPT_REQUEST_TTL_SECONDS`: active retention window for the interrupt request binding registry used by `a2a.interrupt.*` callback methods. Default: `10800` seconds (`180` minutes).
|
|
48
50
|
- `A2A_INTERRUPT_REQUEST_TOMBSTONE_TTL_SECONDS`: retention window for expired interrupt tombstones after active TTL has elapsed. During this window, repeated replies keep returning `INTERRUPT_REQUEST_EXPIRED` instead of falling through to `INTERRUPT_REQUEST_NOT_FOUND`. Default: `600` seconds (`10` minutes).
|
|
49
51
|
- `A2A_CANCEL_ABORT_TIMEOUT_SECONDS`: best-effort timeout for upstream `session.abort` in cancel flow.
|
|
50
|
-
- `OPENCODE_TIMEOUT` / `OPENCODE_TIMEOUT_STREAM`: upstream request timeout and
|
|
51
|
-
- `OPENCODE_MAX_CONCURRENT_REQUESTS`:
|
|
52
|
-
- `OPENCODE_MAX_CONCURRENT_STREAMS`:
|
|
52
|
+
- `OPENCODE_TIMEOUT` / `OPENCODE_TIMEOUT_STREAM`: upstream request timeout and stream timeout. Defaults: `120` and `900` seconds.
|
|
53
|
+
- `OPENCODE_MAX_CONCURRENT_REQUESTS`: fast-fail concurrency limit for unary/control upstream calls. Default: `32`; `0` disables the limit explicitly.
|
|
54
|
+
- `OPENCODE_MAX_CONCURRENT_STREAMS`: fast-fail concurrency limit for long-lived upstream `/event` streams. Default: `8`; `0` disables the limit explicitly.
|
|
55
|
+
- `OPENCODE_AUTH_USERNAME` / `OPENCODE_AUTH_PASSWORD`: optional HTTP Basic credentials sent on every upstream OpenCode call. Set both when the upstream `opencode serve` is hardened with `OPENCODE_SERVER_PASSWORD`; the username defaults to `opencode` and must match `OPENCODE_SERVER_USERNAME` when that is overridden upstream. When unset, no `Authorization` header is sent.
|
|
53
56
|
- `A2A_CLIENT_TIMEOUT_SECONDS`: outbound client timeout. Default: `30` seconds.
|
|
54
57
|
- `A2A_CLIENT_CARD_FETCH_TIMEOUT_SECONDS`: outbound Agent Card fetch timeout. Default: `5` seconds.
|
|
55
58
|
- `A2A_CLIENT_USE_CLIENT_PREFERENCE`: whether the outbound client prefers its own transport choices.
|
|
@@ -174,11 +177,13 @@ With the default `database` backend, the unified lightweight persistence layer p
|
|
|
174
177
|
- pending preferred-session claims
|
|
175
178
|
- interrupt request bindings and tombstones
|
|
176
179
|
|
|
177
|
-
|
|
180
|
+
The supported persistence profile is one `opencode-a2a` application process using its own local SQLite database. Run Uvicorn with one worker and do not share the SQLite file between processes, containers, or replicas. Horizontal application scaling and PostgreSQL deployments are outside the supported deployment matrix. SQLAlchemy remains an internal implementation detail rather than a promise of dialect portability.
|
|
181
|
+
|
|
182
|
+
The runtime configures local durability-oriented SQLite connection settings (`WAL`, `busy_timeout`, `synchronous=NORMAL`) and creates missing parent directories for file-backed database paths.
|
|
178
183
|
|
|
179
184
|
The runtime automatically applies lightweight schema migrations for its custom state tables and records the applied version in `a2a_schema_version`. Schema-version writes are idempotent across concurrent first-start races, pending preferred-session claims now persist absolute `expires_at` timestamps, legacy rows without `expires_at` are pruned during migration instead of being reconstructed from historical TTL assumptions, and the built-in path currently targets the local SQLite deployment profile without requiring Alembic.
|
|
180
185
|
|
|
181
|
-
Database-backed task persistence also keeps the existing first-terminal-state-wins contract while tightening the SQLite path with an atomic terminal-write guard instead of relying only on process-local read-before-write checks.
|
|
186
|
+
Database-backed task persistence also keeps the existing first-terminal-state-wins contract while tightening the SQLite path with an atomic terminal-write guard instead of relying only on process-local read-before-write checks.
|
|
182
187
|
|
|
183
188
|
At startup, the runtime logs a concise persistence summary covering the active backend, the redacted database URL when applicable, the shared persistence scope, and whether the SQLite local durability profile is active.
|
|
184
189
|
|
|
@@ -223,7 +228,7 @@ If one deployment works while another fails against the same upstream provider,
|
|
|
223
228
|
## Streaming Contract
|
|
224
229
|
|
|
225
230
|
- Streaming is always enabled in this server profile; `message:stream` is part of the stable runtime baseline.
|
|
226
|
-
- Streaming (`/
|
|
231
|
+
- Streaming (`/message:stream`) emits an initial working `Task`, then incremental `TaskArtifactUpdateEvent` / `TaskStatusUpdateEvent` updates, and finally a terminal `TaskStatusUpdateEvent(final=true)`.
|
|
227
232
|
- Stream artifacts carry `artifact.metadata.shared.stream.block_type` with values `text` / `reasoning` / `tool_call`.
|
|
228
233
|
- Stream artifacts are scoped to logical output lanes rather than one shared catch-all artifact:
|
|
229
234
|
- text chunks use a stable text artifact ID
|
|
@@ -285,7 +290,7 @@ Current behavior:
|
|
|
285
290
|
- Shared metadata extension URIs such as session binding and streaming are listed under `extensions.extension_uris`.
|
|
286
291
|
- `all_jsonrpc_methods` is the runtime truth for the current deployment.
|
|
287
292
|
- The current SDK-owned core JSON-RPC surface includes `GetExtendedAgentCard` and `tasks/pushNotificationConfig/*`.
|
|
288
|
-
- The current SDK-owned REST surface also includes `GET /
|
|
293
|
+
- The current SDK-owned REST surface also includes `GET /tasks` and the task push notification config routes.
|
|
289
294
|
- The SDK-owned core JSON-RPC method set follows the pinned `a2a-sdk` release and is locked by repository tests; review that surface deliberately when upgrading the SDK.
|
|
290
295
|
- Push notification config routes/methods are currently exposed only because they are part of the SDK-owned core surface. This runtime does not configure a push config store or push sender, so push notification operations remain unsupported. REST routes currently return HTTP `501`, while JSON-RPC methods surface SDK-owned unsupported error envelopes.
|
|
291
296
|
|
|
@@ -483,7 +488,7 @@ Consumer guidance:
|
|
|
483
488
|
Minimal example:
|
|
484
489
|
|
|
485
490
|
```bash
|
|
486
|
-
curl -sS http://127.0.0.1:8000/
|
|
491
|
+
curl -sS http://127.0.0.1:8000/message:send \
|
|
487
492
|
-H 'content-type: application/json' \
|
|
488
493
|
-H 'Authorization: Bearer <your-token>' \
|
|
489
494
|
-d '{
|
|
@@ -535,7 +540,7 @@ Consumer guidance:
|
|
|
535
540
|
Minimal example:
|
|
536
541
|
|
|
537
542
|
```bash
|
|
538
|
-
curl -sS http://127.0.0.1:8000/
|
|
543
|
+
curl -sS http://127.0.0.1:8000/message:send \
|
|
539
544
|
-H 'content-type: application/json' \
|
|
540
545
|
-H 'Authorization: Bearer <your-token>' \
|
|
541
546
|
-d '{
|
|
@@ -1222,7 +1227,7 @@ curl -sS http://127.0.0.1:8000/ \
|
|
|
1222
1227
|
## Authentication Example (curl)
|
|
1223
1228
|
|
|
1224
1229
|
```bash
|
|
1225
|
-
curl -sS http://127.0.0.1:8000/
|
|
1230
|
+
curl -sS http://127.0.0.1:8000/message:send \
|
|
1226
1231
|
-H 'content-type: application/json' \
|
|
1227
1232
|
-H 'Authorization: Bearer <your-token>' \
|
|
1228
1233
|
-d '{
|
|
@@ -1256,7 +1261,7 @@ curl -sS http://127.0.0.1:8000/ \
|
|
|
1256
1261
|
|
|
1257
1262
|
## Streaming Re-Subscription (`SubscribeToTask`)
|
|
1258
1263
|
|
|
1259
|
-
If an SSE connection drops, use `GET /
|
|
1264
|
+
If an SSE connection drops, use `GET /tasks/{task_id}:subscribe` to re-subscribe while the task is still non-terminal.
|
|
1260
1265
|
|
|
1261
1266
|
## Cancellation Semantics (`CancelTask`)
|
|
1262
1267
|
|
|
@@ -1264,7 +1269,7 @@ If an SSE connection drops, use `GET /v1/tasks/{task_id}:subscribe` to re-subscr
|
|
|
1264
1269
|
- For running tasks, the service attempts upstream OpenCode `POST /session/{sessionID}/abort` to stop generation.
|
|
1265
1270
|
- Upstream interruption is best-effort: if upstream returns 404, network errors, or other HTTP errors, A2A cancellation still completes with `TaskState.TASK_STATE_CANCELED`.
|
|
1266
1271
|
- Idempotency contract: repeated `CancelTask` on an already `canceled` task returns the current terminal task state without error.
|
|
1267
|
-
- Terminal subscribe contract: calling `SubscribeToTask` or `GET /
|
|
1272
|
+
- Terminal subscribe contract: calling `SubscribeToTask` or `GET /tasks/{task_id}:subscribe` on a terminal task replays one terminal `Task` snapshot and then closes the stream.
|
|
1268
1273
|
- Terminal persistence contract: once a terminal task snapshot is persisted, this service treats it as immutable. Producers must emit final text and artifact updates before the terminal event, and any final usage or stream metadata must be attached to that terminal event itself. Late terminal-state mutations are rejected by the task-store write policy.
|
|
1269
1274
|
- These two semantics are also declared as machine-readable `service_behaviors` in the compatibility profile and wire contract extensions.
|
|
1270
1275
|
- At `A2A_LOG_LEVEL=DEBUG`, the service emits lightweight metric log records (`logger=opencode_a2a.execution.executor`):
|
|
@@ -21,12 +21,11 @@ flowchart TD
|
|
|
21
21
|
|
|
22
22
|
subgraph Upstream["OpenCode Upstream Layer"]
|
|
23
23
|
UpstreamClient["opencode_upstream_client.py"]
|
|
24
|
-
Invocation["invocation.py"]
|
|
25
24
|
end
|
|
26
25
|
|
|
27
26
|
subgraph Extensions["Extension / Contract Layer"]
|
|
28
27
|
Jsonrpc["jsonrpc/"]
|
|
29
|
-
Contracts["contracts/extensions
|
|
28
|
+
Contracts["contracts/extensions/"]
|
|
30
29
|
Profile["profile/runtime.py"]
|
|
31
30
|
end
|
|
32
31
|
|
|
@@ -61,7 +60,7 @@ flowchart TD
|
|
|
61
60
|
|
|
62
61
|
1. `jsonrpc/application.py` owns the adapter-specific JSON-RPC application boundary.
|
|
63
62
|
2. `jsonrpc/dispatch.py` and handler modules under `jsonrpc/handlers/` route provider-private methods.
|
|
64
|
-
3. `contracts/extensions
|
|
63
|
+
3. `contracts/extensions/` remains the SSOT for extension metadata exposed through Agent Card and OpenAPI.
|
|
65
64
|
4. Tests under `tests/contracts/` and `tests/jsonrpc/` guard contract drift.
|
|
66
65
|
|
|
67
66
|
### Outbound Peer Call Path
|
|
@@ -89,7 +88,7 @@ flowchart TD
|
|
|
89
88
|
|
|
90
89
|
### Extension and Contract Layer
|
|
91
90
|
|
|
92
|
-
- `contracts/extensions
|
|
91
|
+
- `contracts/extensions/`: SSOT for extension metadata, compatibility profile, and wire-contract payloads
|
|
93
92
|
- `jsonrpc/`: provider-private JSON-RPC extension surface
|
|
94
93
|
- `profile/runtime.py`: runtime profile that feeds Agent Card, OpenAPI, and compatibility metadata
|
|
95
94
|
- `protocol_versions.py`: protocol normalization and negotiation helpers
|
|
@@ -128,5 +127,5 @@ For maintainers new to the codebase, this order usually gives the fastest payoff
|
|
|
128
127
|
2. `docs/architecture.md`
|
|
129
128
|
3. `src/opencode_a2a/server/application.py`
|
|
130
129
|
4. `src/opencode_a2a/execution/executor.py`
|
|
131
|
-
5. `src/opencode_a2a/contracts/extensions
|
|
130
|
+
5. `src/opencode_a2a/contracts/extensions/`
|
|
132
131
|
6. `docs/guide.md`
|
|
@@ -14,7 +14,7 @@ authors = [
|
|
|
14
14
|
]
|
|
15
15
|
keywords = ["a2a", "opencode", "fastapi", "json-rpc", "sse"]
|
|
16
16
|
classifiers = [
|
|
17
|
-
"Development Status ::
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
18
|
"Intended Audience :: Developers",
|
|
19
19
|
"Programming Language :: Python :: 3",
|
|
20
20
|
"Programming Language :: Python :: 3.11",
|
|
@@ -24,7 +24,7 @@ classifiers = [
|
|
|
24
24
|
"Topic :: Internet :: WWW/HTTP",
|
|
25
25
|
]
|
|
26
26
|
dependencies = [
|
|
27
|
-
"a2a-sdk==1.
|
|
27
|
+
"a2a-sdk==1.1.2",
|
|
28
28
|
"aiosqlite>=0.20,<1.0",
|
|
29
29
|
"fastapi>=0.139,<1.0",
|
|
30
30
|
"httpx>=0.27,<1.0",
|
|
@@ -68,6 +68,13 @@ quote-style = "double"
|
|
|
68
68
|
|
|
69
69
|
[tool.uv]
|
|
70
70
|
package = true
|
|
71
|
+
# Keep transitive dependency floors above versions with known vulnerabilities:
|
|
72
|
+
# - click < 8.3.3: PYSEC-2026-2132 (pulled in via uvicorn)
|
|
73
|
+
# - pyasn1 < 0.6.4: PYSEC-2026-3455/3456/3457 (pulled in via google-auth <- a2a-sdk)
|
|
74
|
+
constraint-dependencies = [
|
|
75
|
+
"click>=8.3.3",
|
|
76
|
+
"pyasn1>=0.6.4",
|
|
77
|
+
]
|
|
71
78
|
|
|
72
79
|
[tool.setuptools_scm]
|
|
73
80
|
fallback_version = "0+unknown"
|
|
@@ -13,6 +13,7 @@ Executable scripts live in this directory. This file is the entry index for the
|
|
|
13
13
|
|
|
14
14
|
- [`doctor.sh`](./doctor.sh): primary local development regression entrypoint with explicit fix/verify/package phases (uv sync + dependency compatibility + lint + mypy + tests + coverage + built-wheel smoke test)
|
|
15
15
|
- [`conformance.sh`](./conformance.sh): local/manual external A2A conformance experiment entrypoint; caches the official TCK, can launch a dummy-backed local SUT, and preserves raw artifacts under `run/conformance/`
|
|
16
|
+
- [`live_opencode_smoke.sh`](./live_opencode_smoke.sh): local/manual live integration smoke against a real OpenCode runtime; boots an isolated password-hardened `opencode serve` plus the opencode-a2a runtime in throwaway directories and verifies upstream auth, JSON-RPC extension reads, and a streaming prompt round-trip
|
|
16
17
|
- [`dependency_health.sh`](./dependency_health.sh): development dependency review entrypoint (`sync`/`pip check` + outdated + dev audit), while blocking CI/publish audits focus on runtime dependencies
|
|
17
18
|
- [`check_coverage.py`](./check_coverage.py): enforces the overall coverage floor and per-file minimums for critical modules
|
|
18
19
|
- [`find_thin_wrappers.py`](./find_thin_wrappers.py): static analysis helper that reports local functions with low caller counts and flags likely thin forwarding wrappers for manual abstraction review
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Run a live opencode-a2a integration smoke against a real OpenCode runtime.
|
|
3
|
+
# Everything runs inside a temporary, isolated environment: a throwaway HOME/XDG
|
|
4
|
+
# for the upstream OpenCode process, a throwaway workspace directory, and
|
|
5
|
+
# ephemeral ports. No repository data directory or user OpenCode state is read
|
|
6
|
+
# or written, and all artifacts are removed on exit.
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
# shellcheck source=./health_common.sh
|
|
10
|
+
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/health_common.sh"
|
|
11
|
+
|
|
12
|
+
usage() {
|
|
13
|
+
cat <<'EOF'
|
|
14
|
+
Usage:
|
|
15
|
+
bash ./scripts/live_opencode_smoke.sh
|
|
16
|
+
|
|
17
|
+
Purpose:
|
|
18
|
+
Boot a real `opencode serve` (hardened with a server password), start the
|
|
19
|
+
opencode-a2a runtime against it, and verify the adapter's upstream
|
|
20
|
+
adaptation end-to-end: authenticated upstream connectivity, JSON-RPC
|
|
21
|
+
extension read methods, a streaming prompt round-trip, and that upstream
|
|
22
|
+
auth is actually enforced.
|
|
23
|
+
|
|
24
|
+
Isolation:
|
|
25
|
+
The script creates a fresh temporary root (mktemp -d) used for the OpenCode
|
|
26
|
+
process HOME/XDG directories, the OpenCode workspace, and the adapter's
|
|
27
|
+
working directory (SQLite state). Everything is removed on exit; no valid
|
|
28
|
+
data directory is touched.
|
|
29
|
+
|
|
30
|
+
Requirements:
|
|
31
|
+
- opencode CLI on PATH (or OPENCODE_BIN), any 1.18.x build
|
|
32
|
+
- uv, git, curl, python3
|
|
33
|
+
|
|
34
|
+
Selected environment variables:
|
|
35
|
+
OPENCODE_BIN Override the opencode binary path (default: opencode from PATH)
|
|
36
|
+
LIVE_SMOKE_UPSTREAM_PORT Fixed upstream port (default: ephemeral)
|
|
37
|
+
LIVE_SMOKE_A2A_PORT Fixed adapter port (default: ephemeral)
|
|
38
|
+
LIVE_SMOKE_SKIP_PROMPT=1 Skip the streaming prompt round-trip (network/model dependent)
|
|
39
|
+
LIVE_SMOKE_SKIP_SYNC=1 Skip uv sync for this repository
|
|
40
|
+
LIVE_SMOKE_KEEP_ARTIFACTS=1 Keep the temporary root and print its path
|
|
41
|
+
EOF
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
|
45
|
+
usage
|
|
46
|
+
exit 0
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
if [[ "$#" -gt 0 ]]; then
|
|
50
|
+
echo "Expected no positional arguments" >&2
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
55
|
+
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
56
|
+
|
|
57
|
+
if ! command -v uv >/dev/null 2>&1; then
|
|
58
|
+
echo "uv not found in PATH" >&2
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
if ! command -v curl >/dev/null 2>&1; then
|
|
63
|
+
echo "curl not found in PATH" >&2
|
|
64
|
+
exit 1
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
if ! command -v git >/dev/null 2>&1; then
|
|
68
|
+
echo "git not found in PATH" >&2
|
|
69
|
+
exit 1
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
if ! command -v python3 >/dev/null 2>&1; then
|
|
73
|
+
echo "python3 not found in PATH" >&2
|
|
74
|
+
exit 1
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
opencode_bin="${OPENCODE_BIN:-}"
|
|
78
|
+
if [[ -z "${opencode_bin}" ]]; then
|
|
79
|
+
if command -v opencode >/dev/null 2>&1; then
|
|
80
|
+
opencode_bin="$(command -v opencode)"
|
|
81
|
+
else
|
|
82
|
+
echo "opencode CLI not found in PATH; install the 1.18.x line or set OPENCODE_BIN." >&2
|
|
83
|
+
exit 1
|
|
84
|
+
fi
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
opencode_version="$("${opencode_bin}" --version 2>/dev/null | tr -d '[:space:]')"
|
|
88
|
+
if [[ -z "${opencode_version}" ]]; then
|
|
89
|
+
echo "Failed to read opencode version from ${opencode_bin}" >&2
|
|
90
|
+
exit 1
|
|
91
|
+
fi
|
|
92
|
+
echo "[live-smoke] opencode binary: ${opencode_bin} (${opencode_version})"
|
|
93
|
+
|
|
94
|
+
if [[ "${LIVE_SMOKE_SKIP_SYNC:-0}" != "1" ]]; then
|
|
95
|
+
run_shared_repo_health_prerequisites "live-smoke"
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
free_port() {
|
|
99
|
+
python3 - <<'PY'
|
|
100
|
+
import socket
|
|
101
|
+
|
|
102
|
+
with socket.socket() as sock:
|
|
103
|
+
sock.bind(("127.0.0.1", 0))
|
|
104
|
+
print(sock.getsockname()[1])
|
|
105
|
+
PY
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
upstream_port="${LIVE_SMOKE_UPSTREAM_PORT:-$(free_port)}"
|
|
109
|
+
a2a_port="${LIVE_SMOKE_A2A_PORT:-$(free_port)}"
|
|
110
|
+
upstream_url="http://127.0.0.1:${upstream_port}"
|
|
111
|
+
a2a_url="http://127.0.0.1:${a2a_port}"
|
|
112
|
+
upstream_username="opencode"
|
|
113
|
+
# Non-secret throwaway credential for the isolated smoke instance only.
|
|
114
|
+
upstream_password="live-smoke-password" # pragma: allowlist secret
|
|
115
|
+
a2a_bearer_token="live-smoke-token" # pragma: allowlist secret
|
|
116
|
+
|
|
117
|
+
run_dir="$(mktemp -d)"
|
|
118
|
+
opencode_home="${run_dir}/opencode-home"
|
|
119
|
+
workspace_dir="${run_dir}/workspace"
|
|
120
|
+
adapter_dir="${run_dir}/adapter"
|
|
121
|
+
upstream_log="${run_dir}/opencode.log"
|
|
122
|
+
adapter_log="${run_dir}/adapter.log"
|
|
123
|
+
stream_result="${run_dir}/stream-result.jsonl"
|
|
124
|
+
mkdir -p "${opencode_home}" "${workspace_dir}" "${adapter_dir}"
|
|
125
|
+
|
|
126
|
+
cleanup() {
|
|
127
|
+
local exit_code="$1"
|
|
128
|
+
if [[ -n "${adapter_pid:-}" ]] && kill -0 "${adapter_pid}" >/dev/null 2>&1; then
|
|
129
|
+
kill "${adapter_pid}" >/dev/null 2>&1 || true
|
|
130
|
+
wait "${adapter_pid}" >/dev/null 2>&1 || true
|
|
131
|
+
fi
|
|
132
|
+
if [[ -n "${upstream_pid:-}" ]] && kill -0 "${upstream_pid}" >/dev/null 2>&1; then
|
|
133
|
+
kill "${upstream_pid}" >/dev/null 2>&1 || true
|
|
134
|
+
wait "${upstream_pid}" >/dev/null 2>&1 || true
|
|
135
|
+
fi
|
|
136
|
+
if [[ "${LIVE_SMOKE_KEEP_ARTIFACTS:-0}" == "1" ]]; then
|
|
137
|
+
echo "[live-smoke] artifacts kept at ${run_dir}"
|
|
138
|
+
else
|
|
139
|
+
rm -rf "${run_dir}"
|
|
140
|
+
fi
|
|
141
|
+
exit "${exit_code}"
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
trap 'cleanup $?' EXIT
|
|
145
|
+
|
|
146
|
+
(
|
|
147
|
+
cd "${workspace_dir}"
|
|
148
|
+
git init -q
|
|
149
|
+
printf 'live smoke workspace\n' > README.md
|
|
150
|
+
git add README.md
|
|
151
|
+
git -c user.name="live-smoke" -c user.email="live-smoke@localhost" commit -qm init
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
echo "[live-smoke] starting upstream opencode on ${upstream_url}"
|
|
155
|
+
HOME="${opencode_home}" \
|
|
156
|
+
XDG_CONFIG_HOME="${opencode_home}/.config" \
|
|
157
|
+
XDG_DATA_HOME="${opencode_home}/.local/share" \
|
|
158
|
+
XDG_CACHE_HOME="${opencode_home}/.cache" \
|
|
159
|
+
OPENCODE_SERVER_PASSWORD="${upstream_password}" \
|
|
160
|
+
"${opencode_bin}" serve --hostname 127.0.0.1 --port "${upstream_port}" \
|
|
161
|
+
>"${upstream_log}" 2>&1 &
|
|
162
|
+
upstream_pid="$!"
|
|
163
|
+
|
|
164
|
+
upstream_ready=""
|
|
165
|
+
for _ in $(seq 1 60); do
|
|
166
|
+
if ! kill -0 "${upstream_pid}" >/dev/null 2>&1; then
|
|
167
|
+
echo "[live-smoke] upstream opencode exited before becoming ready" >&2
|
|
168
|
+
cat "${upstream_log}" >&2
|
|
169
|
+
exit 1
|
|
170
|
+
fi
|
|
171
|
+
if curl -fsS --max-time 5 -u "${upstream_username}:${upstream_password}" \
|
|
172
|
+
"${upstream_url}/doc" >/dev/null 2>&1; then
|
|
173
|
+
upstream_ready="1"
|
|
174
|
+
break
|
|
175
|
+
fi
|
|
176
|
+
sleep 0.5
|
|
177
|
+
done
|
|
178
|
+
if [[ -z "${upstream_ready}" ]]; then
|
|
179
|
+
echo "[live-smoke] upstream opencode did not become ready at ${upstream_url}" >&2
|
|
180
|
+
cat "${upstream_log}" >&2
|
|
181
|
+
exit 1
|
|
182
|
+
fi
|
|
183
|
+
echo "[live-smoke] upstream opencode ready (${opencode_version})"
|
|
184
|
+
|
|
185
|
+
echo "[live-smoke] starting opencode-a2a on ${a2a_url}"
|
|
186
|
+
adapter_bin="${ROOT_DIR}/.venv/bin/opencode-a2a"
|
|
187
|
+
if [[ ! -x "${adapter_bin}" ]]; then
|
|
188
|
+
echo "[live-smoke] ${adapter_bin} not found; run without LIVE_SMOKE_SKIP_SYNC=1 (or sync the repository first)." >&2
|
|
189
|
+
exit 1
|
|
190
|
+
fi
|
|
191
|
+
|
|
192
|
+
OPENCODE_BASE_URL="${upstream_url}" \
|
|
193
|
+
OPENCODE_WORKSPACE_ROOT="${workspace_dir}" \
|
|
194
|
+
OPENCODE_AUTH_USERNAME="${upstream_username}" \
|
|
195
|
+
OPENCODE_AUTH_PASSWORD="${upstream_password}" \
|
|
196
|
+
A2A_STATIC_AUTH_CREDENTIALS="[{\"scheme\":\"bearer\",\"token\":\"${a2a_bearer_token}\",\"principal\":\"automation\"}]" \
|
|
197
|
+
A2A_HOST="127.0.0.1" \
|
|
198
|
+
A2A_PORT="${a2a_port}" \
|
|
199
|
+
A2A_PUBLIC_URL="${a2a_url}" \
|
|
200
|
+
A2A_TASK_STORE_DATABASE_URL="sqlite+aiosqlite:///${run_dir}/adapter/opencode-a2a.db" \
|
|
201
|
+
"${adapter_bin}" serve >"${adapter_log}" 2>&1 &
|
|
202
|
+
adapter_pid="$!"
|
|
203
|
+
|
|
204
|
+
a2a_ready=""
|
|
205
|
+
for _ in $(seq 1 60); do
|
|
206
|
+
if ! kill -0 "${adapter_pid}" >/dev/null 2>&1; then
|
|
207
|
+
echo "[live-smoke] adapter exited before becoming ready" >&2
|
|
208
|
+
cat "${adapter_log}" >&2
|
|
209
|
+
exit 1
|
|
210
|
+
fi
|
|
211
|
+
if curl -fsS --max-time 5 -H "Authorization: Bearer ${a2a_bearer_token}" \
|
|
212
|
+
"${a2a_url}/health" >/dev/null 2>&1; then
|
|
213
|
+
a2a_ready="1"
|
|
214
|
+
break
|
|
215
|
+
fi
|
|
216
|
+
sleep 0.5
|
|
217
|
+
done
|
|
218
|
+
if [[ -z "${a2a_ready}" ]]; then
|
|
219
|
+
echo "[live-smoke] adapter did not become ready at ${a2a_url}" >&2
|
|
220
|
+
cat "${adapter_log}" >&2
|
|
221
|
+
exit 1
|
|
222
|
+
fi
|
|
223
|
+
echo "[live-smoke] adapter ready"
|
|
224
|
+
|
|
225
|
+
auth_header="Authorization: Bearer ${a2a_bearer_token}"
|
|
226
|
+
extension_header="A2A-Extensions: urn:opencode-a2a:extension:session-management:v1, urn:opencode-a2a:extension:workspace-control:v1, urn:opencode-a2a:extension:provider-discovery:v1"
|
|
227
|
+
|
|
228
|
+
jsonrpc_probe() {
|
|
229
|
+
local payload="$1"
|
|
230
|
+
curl -sS --max-time 30 -H "${auth_header}" -H "${extension_header}" \
|
|
231
|
+
-H "Content-Type: application/json" \
|
|
232
|
+
-d "${payload}" "${a2a_url}/"
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
echo "[live-smoke] probe: upstream auth is enforced"
|
|
236
|
+
unauth_status="$(
|
|
237
|
+
curl -sS --max-time 10 -o /dev/null -w '%{http_code}' \
|
|
238
|
+
"${upstream_url}/session/status?directory=${workspace_dir}"
|
|
239
|
+
)"
|
|
240
|
+
if [[ "${unauth_status}" != "401" ]]; then
|
|
241
|
+
echo "[live-smoke] FAIL: upstream /session/status without credentials returned ${unauth_status}, expected 401" >&2
|
|
242
|
+
exit 1
|
|
243
|
+
fi
|
|
244
|
+
echo "[live-smoke] PASS: upstream auth enforced (401 without credentials)"
|
|
245
|
+
|
|
246
|
+
echo "[live-smoke] probe: opencode.projects.current"
|
|
247
|
+
projects_result="$(jsonrpc_probe '{"jsonrpc":"2.0","id":1,"method":"opencode.projects.current","params":{}}')"
|
|
248
|
+
if ! python3 -c 'import json,sys; d=json.load(sys.stdin); sys.exit(0 if isinstance(d.get("result", {}).get("item", {}).get("id"), str) else 1)' \
|
|
249
|
+
<<<"${projects_result}"; then
|
|
250
|
+
echo "[live-smoke] FAIL: opencode.projects.current returned unexpected result: ${projects_result}" >&2
|
|
251
|
+
exit 1
|
|
252
|
+
fi
|
|
253
|
+
echo "[live-smoke] PASS: opencode.projects.current"
|
|
254
|
+
|
|
255
|
+
echo "[live-smoke] probe: opencode.sessions.status"
|
|
256
|
+
sessions_result="$(jsonrpc_probe '{"jsonrpc":"2.0","id":2,"method":"opencode.sessions.status","params":{}}')"
|
|
257
|
+
if ! python3 -c 'import json,sys; d=json.load(sys.stdin); sys.exit(0 if "items" in d.get("result", {}) else 1)' \
|
|
258
|
+
<<<"${sessions_result}"; then
|
|
259
|
+
echo "[live-smoke] FAIL: opencode.sessions.status returned unexpected result: ${sessions_result}" >&2
|
|
260
|
+
exit 1
|
|
261
|
+
fi
|
|
262
|
+
echo "[live-smoke] PASS: opencode.sessions.status"
|
|
263
|
+
|
|
264
|
+
if [[ "${LIVE_SMOKE_SKIP_PROMPT:-0}" != "1" ]]; then
|
|
265
|
+
echo "[live-smoke] probe: streaming prompt round-trip"
|
|
266
|
+
curl -sS -N \
|
|
267
|
+
-H "${auth_header}" \
|
|
268
|
+
-H "Content-Type: application/json" \
|
|
269
|
+
-d '{"jsonrpc":"2.0","id":10,"method":"SendStreamingMessage","params":{"message":{"messageId":"live-smoke-msg","role":"ROLE_USER","parts":[{"text":"Reply with exactly: smoke-ok"}]}}}' \
|
|
270
|
+
"${a2a_url}/" --max-time 180 >"${stream_result}"
|
|
271
|
+
if ! grep -q '"TASK_STATE_COMPLETED"' "${stream_result}"; then
|
|
272
|
+
echo "[live-smoke] FAIL: streaming prompt did not reach TASK_STATE_COMPLETED" >&2
|
|
273
|
+
tail -c 2000 "${stream_result}" >&2
|
|
274
|
+
exit 1
|
|
275
|
+
fi
|
|
276
|
+
if ! grep -q 'smoke-ok' "${stream_result}"; then
|
|
277
|
+
echo "[live-smoke] FAIL: streaming prompt reply did not contain the expected text" >&2
|
|
278
|
+
tail -c 2000 "${stream_result}" >&2
|
|
279
|
+
exit 1
|
|
280
|
+
fi
|
|
281
|
+
echo "[live-smoke] PASS: streaming prompt round-trip"
|
|
282
|
+
else
|
|
283
|
+
echo "[live-smoke] skip: streaming prompt round-trip (LIVE_SMOKE_SKIP_PROMPT=1)"
|
|
284
|
+
fi
|
|
285
|
+
|
|
286
|
+
echo "[live-smoke] ALL CHECKS PASSED (opencode ${opencode_version})"
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import base64
|
|
4
4
|
import binascii
|
|
5
|
+
import secrets
|
|
5
6
|
from dataclasses import dataclass
|
|
6
7
|
|
|
7
8
|
from starlette.requests import Request
|
|
@@ -83,7 +84,7 @@ def authenticate_static_credential(
|
|
|
83
84
|
for credential in credentials:
|
|
84
85
|
if credential.auth_scheme != "bearer" or credential.token is None:
|
|
85
86
|
continue
|
|
86
|
-
if auth_value
|
|
87
|
+
if secrets.compare_digest(auth_value, credential.token):
|
|
87
88
|
return AuthenticatedPrincipal(
|
|
88
89
|
identity=credential.principal,
|
|
89
90
|
auth_scheme="bearer",
|
|
@@ -102,7 +103,11 @@ def authenticate_static_credential(
|
|
|
102
103
|
for credential in credentials:
|
|
103
104
|
if credential.auth_scheme != "basic":
|
|
104
105
|
continue
|
|
105
|
-
if credential.username
|
|
106
|
+
if credential.username is None or credential.password is None:
|
|
107
|
+
continue
|
|
108
|
+
username_matches = secrets.compare_digest(username, credential.username)
|
|
109
|
+
password_matches = secrets.compare_digest(password, credential.password)
|
|
110
|
+
if username_matches and password_matches:
|
|
106
111
|
return AuthenticatedPrincipal(
|
|
107
112
|
identity=credential.principal,
|
|
108
113
|
auth_scheme="basic",
|
|
@@ -154,17 +154,23 @@ class Settings(BaseSettings):
|
|
|
154
154
|
opencode_system: str | None = Field(default=None, alias="OPENCODE_SYSTEM")
|
|
155
155
|
opencode_variant: str | None = Field(default=None, alias="OPENCODE_VARIANT")
|
|
156
156
|
opencode_timeout: float = Field(default=120.0, alias="OPENCODE_TIMEOUT")
|
|
157
|
-
opencode_timeout_stream: float | None = Field(default=
|
|
157
|
+
opencode_timeout_stream: float | None = Field(default=900.0, alias="OPENCODE_TIMEOUT_STREAM")
|
|
158
158
|
opencode_max_concurrent_requests: int = Field(
|
|
159
|
-
default=
|
|
159
|
+
default=32,
|
|
160
160
|
ge=0,
|
|
161
161
|
alias="OPENCODE_MAX_CONCURRENT_REQUESTS",
|
|
162
162
|
)
|
|
163
163
|
opencode_max_concurrent_streams: int = Field(
|
|
164
|
-
default=
|
|
164
|
+
default=8,
|
|
165
165
|
ge=0,
|
|
166
166
|
alias="OPENCODE_MAX_CONCURRENT_STREAMS",
|
|
167
167
|
)
|
|
168
|
+
opencode_auth_username: str = Field(default="opencode", alias="OPENCODE_AUTH_USERNAME")
|
|
169
|
+
opencode_auth_password: str | None = Field(
|
|
170
|
+
default=None,
|
|
171
|
+
alias="OPENCODE_AUTH_PASSWORD",
|
|
172
|
+
repr=False,
|
|
173
|
+
)
|
|
168
174
|
|
|
169
175
|
# A2A settings
|
|
170
176
|
a2a_public_url: str = Field(default="http://127.0.0.1:8000", alias="A2A_PUBLIC_URL")
|
|
@@ -292,6 +298,12 @@ class Settings(BaseSettings):
|
|
|
292
298
|
raise ValueError(
|
|
293
299
|
"A2A_TASK_STORE_DATABASE_URL is required when A2A_TASK_STORE_BACKEND=database"
|
|
294
300
|
)
|
|
301
|
+
if (
|
|
302
|
+
self.a2a_task_store_backend == "database"
|
|
303
|
+
and self.a2a_task_store_database_url
|
|
304
|
+
and not self.a2a_task_store_database_url.startswith("sqlite+aiosqlite:")
|
|
305
|
+
):
|
|
306
|
+
raise ValueError("A2A_TASK_STORE_DATABASE_URL must use the sqlite+aiosqlite scheme")
|
|
295
307
|
if self.a2a_static_auth_credentials:
|
|
296
308
|
if not any(credential.enabled for credential in self.a2a_static_auth_credentials):
|
|
297
309
|
raise ValueError(
|