diff --git a/.github/workflows/publish-runtime.yml b/.github/workflows/publish-runtime.yml index 6a65bd10..e1fd659d 100644 --- a/.github/workflows/publish-runtime.yml +++ b/.github/workflows/publish-runtime.yml @@ -168,6 +168,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(