From 7d8620b6f988a467390b94512befb9d3f8f5bade Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:44:07 +0000 Subject: [PATCH 1/3] Initial plan From 731b12c7410c17dc01f7112fa480bb2e96e6238f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:46:57 +0000 Subject: [PATCH 2/3] fix: preserve legacy capabilities in metadata after struct assignment Co-authored-by: techbuzzz <6938100+techbuzzz@users.noreply.github.com> --- internal/a2a/models/agent_card.go | 22 ++-- .../agent_card_metadata_preservation_test.go | 121 ++++++++++++++++++ 2 files changed, 134 insertions(+), 9 deletions(-) create mode 100644 tests/a2a/agent_card_metadata_preservation_test.go diff --git a/internal/a2a/models/agent_card.go b/internal/a2a/models/agent_card.go index 76aea0e..4bb8ea8 100644 --- a/internal/a2a/models/agent_card.go +++ b/internal/a2a/models/agent_card.go @@ -105,16 +105,14 @@ func (a *AgentCard) UnmarshalJSON(data []byte) error { // Check if capabilities is present and try legacy formats first var hasLegacyCap bool + var legacyCapabilities []Capability if capRaw, exists := raw["capabilities"]; exists { // Try legacy array format []Capability var legacyCaps []Capability if err := json.Unmarshal(capRaw, &legacyCaps); err == nil && len(legacyCaps) > 0 { // Convert legacy array to new Capabilities object structure - // Store in metadata for backward compatibility - if a.Metadata == nil { - a.Metadata = make(map[string]any) - } - a.Metadata["legacyCapabilities"] = legacyCaps + // Store in a temporary variable to preserve across struct assignment + legacyCapabilities = legacyCaps hasLegacyCap = true } else if err == nil { // Empty array, still legacy format @@ -147,10 +145,8 @@ func (a *AgentCard) UnmarshalJSON(data []byte) error { Description: description, }) } - if a.Metadata == nil { - a.Metadata = make(map[string]any) - } - a.Metadata["legacyCapabilities"] = legacyCaps + // Store in a temporary variable to preserve across struct assignment + legacyCapabilities = legacyCaps hasLegacyCap = true } } else { @@ -177,6 +173,14 @@ func (a *AgentCard) UnmarshalJSON(data []byte) error { return err } *a = AgentCard(*aux) + + // Restore legacy capabilities after struct assignment + if len(legacyCapabilities) > 0 { + if a.Metadata == nil { + a.Metadata = make(map[string]any) + } + a.Metadata["legacyCapabilities"] = legacyCapabilities + } } else { // New schema format, unmarshal normally aux := &Alias{} diff --git a/tests/a2a/agent_card_metadata_preservation_test.go b/tests/a2a/agent_card_metadata_preservation_test.go new file mode 100644 index 0000000..2b198a7 --- /dev/null +++ b/tests/a2a/agent_card_metadata_preservation_test.go @@ -0,0 +1,121 @@ +package a2a + +import ( + "encoding/json" + "testing" + + "github.com/techbuzzz/agent-shaker/internal/a2a/models" +) + +func TestAgentCardUnmarshal_MetadataPreservation(t *testing.T) { + // Test that legacy capabilities are preserved in metadata after struct assignment + jsonData := `{ + "name": "Test Agent", + "version": "1.0.0", + "capabilities": [ + { + "type": "task", + "description": "Task execution" + }, + { + "type": "streaming", + "description": "SSE streaming" + } + ], + "endpoints": [], + "metadata": { + "existingKey": "existingValue" + } + }` + + var card models.AgentCard + err := json.Unmarshal([]byte(jsonData), &card) + if err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + + // Check if metadata exists + if card.Metadata == nil { + t.Fatal("Metadata is nil") + } + + // Check if legacy capabilities are preserved + legacyCaps, ok := card.Metadata["legacyCapabilities"] + if !ok { + t.Fatal("legacyCapabilities not found in metadata - the fix failed!") + } + + caps, ok := legacyCaps.([]models.Capability) + if !ok { + t.Fatalf("legacyCapabilities is not []Capability, got: %T", legacyCaps) + } + + if len(caps) != 2 { + t.Fatalf("Expected 2 legacy capabilities, got %d", len(caps)) + } + + if caps[0].Type != "task" || caps[0].Description != "Task execution" { + t.Errorf("First capability incorrect: %+v", caps[0]) + } + + if caps[1].Type != "streaming" || caps[1].Description != "SSE streaming" { + t.Errorf("Second capability incorrect: %+v", caps[1]) + } + + // Check if existing metadata is also preserved + if existingVal, ok := card.Metadata["existingKey"].(string); !ok || existingVal != "existingValue" { + t.Errorf("Existing metadata not preserved correctly: %v", card.Metadata["existingKey"]) + } + + t.Log("✓ Legacy capabilities correctly preserved in metadata after struct assignment") +} + +func TestAgentCardUnmarshal_ObjectFormat_MetadataPreservation(t *testing.T) { + // Test that legacy object format capabilities are also preserved + jsonData := `{ + "name": "Test Agent", + "version": "1.0.0", + "capabilities": { + "task": "Task execution", + "streaming": "SSE streaming", + "artifacts": "Artifact sharing" + }, + "endpoints": [], + "metadata": { + "customField": "customValue" + } + }` + + var card models.AgentCard + err := json.Unmarshal([]byte(jsonData), &card) + if err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + + // Check if metadata exists + if card.Metadata == nil { + t.Fatal("Metadata is nil") + } + + // Check if legacy capabilities are preserved + legacyCaps, ok := card.Metadata["legacyCapabilities"] + if !ok { + t.Fatal("legacyCapabilities not found in metadata - the fix failed!") + } + + caps, ok := legacyCaps.([]models.Capability) + if !ok { + t.Fatalf("legacyCapabilities is not []Capability, got: %T", legacyCaps) + } + + if len(caps) != 3 { + t.Fatalf("Expected 3 legacy capabilities, got %d", len(caps)) + } + + // Check if existing metadata is also preserved + if customVal, ok := card.Metadata["customField"].(string); !ok || customVal != "customValue" { + t.Errorf("Existing metadata not preserved correctly: %v", card.Metadata["customField"]) + } + + t.Log("✓ Legacy object format capabilities correctly preserved in metadata") +} From 490fe3c20273d40fa4e18f17f389e2034ea9d82f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:47:53 +0000 Subject: [PATCH 3/3] style: remove extra blank line for consistency Co-authored-by: techbuzzz <6938100+techbuzzz@users.noreply.github.com> --- internal/a2a/models/agent_card.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/a2a/models/agent_card.go b/internal/a2a/models/agent_card.go index 4bb8ea8..97586d0 100644 --- a/internal/a2a/models/agent_card.go +++ b/internal/a2a/models/agent_card.go @@ -173,7 +173,7 @@ func (a *AgentCard) UnmarshalJSON(data []byte) error { return err } *a = AgentCard(*aux) - + // Restore legacy capabilities after struct assignment if len(legacyCapabilities) > 0 { if a.Metadata == nil {