From 851fd21fb1d78a32a74734d5786aa02224294a2f Mon Sep 17 00:00:00 2001 From: Hongming Wang Date: Mon, 27 Apr 2026 07:54:23 -0700 Subject: [PATCH] =?UTF-8?q?fix(workspace):=20rename=20supported=5Fprotocol?= =?UTF-8?q?s=20=E2=86=92=20supported=5Finterfaces=20(a2a-sdk=201.0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CRITICAL: every workspace boot since the a2a-sdk 1.0 migration (#1974) has been crashing at AgentCard construction with: ValueError: Protocol message AgentCard has no "supported_protocols" field The protobuf field is `supported_interfaces` (plural, interfaces — see a2a-sdk types/a2a_pb2.pyi:189). The 0.3→1.0 migration left the kwarg as `supported_protocols`, which doesn't exist in the 1.0 schema, so the constructor raises before any subsequent line of main runs. Why this hid for so long: - publish-runtime.yml's smoke step only IMPORTED molecule_runtime.main; importing the module is fine, only CONSTRUCTING the AgentCard fails - The user-visible symptom is "Workspace failed: " with empty last_sample_error, indistinguishable from generic boot timeouts - The state_transition_history=True bug (fixed in #2179) was a sibling of this — same migration, same class, just caught first Fix is symmetric with #2179: 1. workspace/main.py: rename the kwarg + comment explaining why 2. .github/workflows/publish-runtime.yml: extend the smoke block to instantiate AgentCard with the exact production call shape, so the next field-rename of this class fails at publish time instead of breaking every workspace startup Verification: - Constructed AgentCard against fresh a2a-sdk 1.0.2 in a clean venv with the corrected kwarg → succeeds - Constructed it with the original `supported_protocols` kwarg → fails immediately with the exact error production sees - Smoke test pinned to mirror main.py's exact call shape; main.py + smoke must stay in lockstep going forward Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/publish-runtime.yml | 44 +++++++++++++++++++++++++++ workspace/main.py | 14 +++++++-- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-runtime.yml b/.github/workflows/publish-runtime.yml index b7c77f82..69187951 100644 --- a/.github/workflows/publish-runtime.yml +++ b/.github/workflows/publish-runtime.yml @@ -158,6 +158,50 @@ jobs: assert callable(get_adapter), 'adapters.get_adapter must be callable' assert hasattr(BaseAdapter, 'name'), 'BaseAdapter interface broken' assert hasattr(AdapterConfig, '__init__'), 'AdapterConfig dataclass missing' + + # Call-shape smoke for AgentCard. Pure imports don't catch + # field-shape regressions in upstream SDKs that only surface + # at construction time. Two bugs of this exact class shipped + # since the a2a-sdk 1.0 migration: + # - state_transition_history=True (fixed in #2179) + # - supported_protocols=[...] (the protobuf field is + # supported_interfaces — caused every workspace boot + # to crash with `ValueError: Protocol message AgentCard + # has no "supported_protocols" field`; fixed alongside + # this smoke) + # + # This block instantiates the EXACT classes main.py uses, + # with the EXACT keyword arguments. If a future a2a-sdk + # upgrade renames any of supported_interfaces / streaming / + # push_notifications / etc., the publish fails here instead + # of breaking every workspace startup. main.py and this + # smoke MUST stay in lockstep — adding a kwarg to one + # without mirroring it here is the regression vector. + from a2a.types import AgentCard, AgentCapabilities, AgentSkill, AgentInterface + AgentCard( + name='smoke-agent', + description='publish-runtime smoke test', + version='0.0.0-smoke', + supported_interfaces=[ + AgentInterface(protocol_binding='https://a2a.g/v1', url='http://localhost:8080'), + ], + capabilities=AgentCapabilities( + streaming=True, + push_notifications=False, + ), + skills=[ + AgentSkill( + id='smoke-skill', + name='Smoke', + description='no-op', + tags=['smoke'], + examples=['noop'], + ), + ], + default_input_modes=['text/plain', 'application/json'], + default_output_modes=['text/plain', 'application/json'], + ) + print('✓ AgentCard call-shape smoke passed') print('✓ smoke import passed') " diff --git a/workspace/main.py b/workspace/main.py index 16b57d7d..f861a411 100644 --- a/workspace/main.py +++ b/workspace/main.py @@ -175,14 +175,24 @@ async def main(): # pragma: no cover machine_ip = os.environ.get("HOSTNAME", get_machine_ip()) workspace_url = f"http://{machine_ip}:{port}" - # v1: AgentCard.url removed; put url+protocol in supported_protocols instead. + # v1: AgentCard.url removed; put url+protocol in supported_interfaces instead. # v1: AgentCapabilities.inputModes/outputModes removed; move to AgentCard.default_*. # v1: pushNotifications → push_notifications (Pydantic field name) + # + # AgentCard's protocol message uses `supported_interfaces` (plural, + # interfaces — see a2a-sdk types/a2a_pb2.pyi:189). The 0.3.x→1.0 + # migration in #1974 originally used `supported_protocols`, which + # the protobuf doesn't expose at all — every workspace boot since + # then crashed with `ValueError: Protocol message AgentCard has no + # "supported_protocols" field`. The crash didn't surface in the + # publish-runtime smoke because the smoke only IMPORTS + # molecule_runtime.main, never CALLS the AgentCard constructor. + # Don't rename back. agent_card = AgentCard( name=config.name, description=config.description or config.name, version=config.version, - supported_protocols=[ + supported_interfaces=[ AgentInterface(protocol_binding="https://a2a.g/v1", url=workspace_url) ], capabilities=AgentCapabilities(