Skip to content

Commit 249e2ca

Browse files
committed
Merge branch 'main' of https://github.com/a2aproject/a2a-dotnet into agentCardProvider
# Conflicts: # src/A2A.AspNetCore/A2AEndpointRouteBuilderExtensions.cs
2 parents 5af8c77 + 6d9428b commit 249e2ca

File tree

95 files changed

+2749
-590
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

95 files changed

+2749
-590
lines changed

.github/copilot-instructions.md

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# A2A .NET SDK Copilot Instructions
2+
3+
This file contains coding guidelines and requirements for maintaining the A2A .NET SDK. These instructions help ensure consistency and correctness when implementing features, especially discriminator-based serialization.
4+
5+
## Discriminator Converter Implementation Requirements
6+
7+
### Overview
8+
The A2A SDK uses a custom JSON discriminator pattern for polymorphic serialization of types like `A2AEvent`, `Part`, and `FileContent`. This approach allows for specific error handling with `A2AException` and error codes instead of the default `NotSupportedException` from System.Text.Json.
9+
10+
### String Constant Patterns
11+
12+
When implementing discriminator-based serialization:
13+
14+
1. **Use internal static classes for kind constants** instead of enums
15+
- Create static classes like `A2AEventKind`, `PartKind`, `FileContentKind`
16+
- Use `const string` fields with lowercase kebab-case values
17+
- Include XML documentation linking to related types
18+
19+
```csharp
20+
public static class PartKind
21+
{
22+
/// <summary>
23+
/// A text part containing plain textual content.
24+
/// </summary>
25+
/// <seealso cref="TextPart"/>
26+
public const string Text = "text";
27+
}
28+
```
29+
30+
2. **Use BaseKindDiscriminatorConverter**
31+
- Inherit from `BaseKindDiscriminatorConverter<TBase>`
32+
- Implement `KindToTypeMapping` as `IReadOnlyDictionary<string, Type>`
33+
- Implement `DisplayName` property for error messages
34+
35+
```csharp
36+
internal class PartConverterViaKindDiscriminator<T> : BaseKindDiscriminatorConverter<T> where T : Part
37+
{
38+
protected override IReadOnlyDictionary<string, Type> KindToTypeMapping { get; } = new Dictionary<string, Type>
39+
{
40+
[PartKind.Text] = typeof(TextPart),
41+
[PartKind.File] = typeof(FilePart),
42+
[PartKind.Data] = typeof(DataPart)
43+
};
44+
45+
protected override string DisplayName { get; } = "part";
46+
}
47+
```
48+
49+
3. **Primary constructor usage for derived types**
50+
- Use primary constructors in derived classes
51+
- Pass the appropriate string constant to the base constructor
52+
53+
```csharp
54+
public sealed class TextPart() : Part(PartKind.Text)
55+
{
56+
// Implementation
57+
}
58+
```
59+
60+
### Error Handling Requirements
61+
62+
1. **Use specific error messages**
63+
- Missing discriminator: "Missing required 'kind' discriminator for {TypeName}"
64+
- Invalid discriminator type: "Invalid 'kind' discriminator for {TypeName}: '{value}'"
65+
- Unknown kind value: "Unknown {displayName} kind: '{kindValue}'"
66+
- Deserialization failure: "Failed to deserialize '{kindValue}' {displayName}"
67+
68+
2. **Use appropriate A2AErrorCode**
69+
- All discriminator-related errors should use `A2AErrorCode.InvalidRequest`
70+
71+
### Maintenance Guidelines
72+
73+
#### When Adding New Discriminated Types
74+
75+
1. **Create the kind constants class**
76+
- Use static class with const string fields
77+
- Follow kebab-case naming convention
78+
- Add comprehensive XML documentation
79+
80+
2. **Create the base class**
81+
- Use primary constructor accepting string kind parameter
82+
- Add JsonConverter attribute pointing to your converter
83+
- Include JsonDerivedType attributes for all derived types
84+
- Add discriminator property with proper JsonPropertyName and JsonPropertyOrder
85+
86+
3. **Create the converter**
87+
- Inherit from `BaseKindDiscriminatorConverter<TBase>`
88+
- Implement required abstract members
89+
- Use dictionary mapping for kind-to-type relationships
90+
91+
4. **Create derived classes**
92+
- Use primary constructors
93+
- Pass appropriate kind constant to base constructor
94+
95+
#### When Adding New Derived Types
96+
97+
1. **Add new kind constant** to the appropriate kind constants class
98+
2. **Add mapping entry** in the converter's KindToTypeMapping
99+
3. **Add JsonDerivedType attribute** to the base class
100+
4. **Implement the derived class** with primary constructor
101+
5. **Add comprehensive tests** for serialization/deserialization
102+
103+
### JSON Serialization Guidelines
104+
105+
1. **Property naming**: Use camelCase for JSON property names
106+
2. **Property ordering**: Discriminator property should have `JsonPropertyOrder(int.MinValue)`
107+
3. **Required properties**: Mark discriminators with `JsonRequired`
108+
4. **Converter placement**: Use `JsonConverter` attribute on base classes, not derived classes
109+
110+
### Testing Requirements
111+
112+
When implementing discriminator converters, ensure:
113+
114+
1. **Successful serialization** of all derived types
115+
2. **Successful deserialization** with valid kind values
116+
3. **Proper error handling** for missing/invalid/unknown kind values
117+
4. **Round-trip compatibility** (serialize then deserialize equals original)
118+
5. **Backward compatibility** with existing JSON payloads
119+
120+
## Performance Considerations
121+
122+
1. **Use readonly dictionaries** for kind-to-type mappings
123+
2. **Avoid repeated allocations** in converter hot paths
124+
3. **Cache type info** when possible
125+
4. **Use culture-invariant operations** for string handling
126+
127+
## Implemention guidelines
128+
129+
1. Follow **principle of least privilege/visibility** for new types
130+
131+
## Example Implementation
132+
133+
See the existing implementations of `A2AEvent`, `Part`, and `FileContent` hierarchies as reference patterns for implementing new discriminated types.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: "Conventional Commits"
2+
3+
on:
4+
pull_request:
5+
types:
6+
- opened
7+
- edited
8+
- synchronize
9+
10+
permissions:
11+
contents: read
12+
13+
jobs:
14+
main:
15+
permissions:
16+
pull-requests: read
17+
statuses: write
18+
name: Validate PR Title
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: semantic-pull-request
22+
uses: amannn/[email protected]
23+
env:
24+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
25+
with:
26+
validateSingleCommit: false

.github/workflows/copilot-setup-steps.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ jobs:
2626
# If you do not check out your code, Copilot will do this for you.
2727
steps:
2828
- name: Checkout code
29-
uses: actions/checkout@v4
29+
uses: actions/checkout@v5
3030

3131
- name: Setup .NET 9.x
32-
uses: actions/setup-dotnet@v4
32+
uses: actions/setup-dotnet@v5
3333
with:
3434
dotnet-version: 9.x

.github/workflows/dotnet-build.yml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,29 @@ jobs:
2020
- dotnet-version: 9.x
2121
framework: net9.0
2222
steps:
23-
- uses: actions/checkout@v4
23+
- uses: actions/checkout@v5
2424
- name: Setup .NET 9.x
25-
uses: actions/setup-dotnet@v4
25+
uses: actions/setup-dotnet@v5
2626
with:
2727
dotnet-version: 9.x
2828
- name: Setup .NET ${{ matrix.dotnet-version }}
29-
uses: actions/setup-dotnet@v4
29+
uses: actions/setup-dotnet@v5
3030
with:
3131
dotnet-version: ${{ matrix.dotnet-version }}
3232
- name: Restore dependencies
3333
run: dotnet restore
3434
- name: BuildLibs
3535
run: dotnet build ./src/A2ALibs.slnx --framework ${{ matrix.framework }}
36+
- name: BuildNetStandard
37+
run: dotnet build ./src/A2A/A2A.csproj --framework netstandard2.0
3638
- name: TestLibs
3739
run: dotnet test ./src/A2ALibs.slnx --no-build --verbosity normal --framework ${{ matrix.framework }}
3840
buildSamples:
3941
runs-on: ubuntu-latest
4042
steps:
41-
- uses: actions/checkout@v4
43+
- uses: actions/checkout@v5
4244
- name: Setup .NET 9.x
43-
uses: actions/setup-dotnet@v4
45+
uses: actions/setup-dotnet@v5
4446
with:
4547
dotnet-version: 9.x
4648
- name: Restore dependencies

.github/workflows/release.yaml

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,23 @@ jobs:
4444

4545
steps:
4646
- name: Clone the repo
47-
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
47+
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
4848
with:
4949
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
5050

5151
- name: Set up .NET
52-
uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1
52+
uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
5353
with:
54-
dotnet-version: 9.0.x
54+
dotnet-version: |
55+
8.0.x
56+
9.0.x
5557
5658
- name: Build
5759
run: dotnet build --configuration ${{ matrix.configuration }}
5860

61+
- name: Test
62+
run: dotnet test --no-build --configuration ${{ matrix.configuration }}
63+
5964
- name: Pack
6065
run: dotnet pack --configuration ${{ matrix.configuration }}
6166

@@ -70,10 +75,10 @@ jobs:
7075
version_suffix_args: ${{ github.event_name != 'release' && format('--version-suffix "{0}"', inputs.version_suffix_override || format('ci.{0}', github.run_number)) || '' }}
7176

7277
steps:
73-
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
78+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
7479

7580
- name: Setup .NET
76-
uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1
81+
uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
7782
with:
7883
dotnet-version: |
7984
9.0.x
@@ -100,15 +105,18 @@ jobs:
100105
packages: write
101106

102107
steps:
103-
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
108+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
104109

105110
- name: Setup .NET
106-
uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1
111+
uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
107112
with:
108113
dotnet-version: 9.0.x
109114

110115
- name: Download build artifacts
111-
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
116+
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
117+
with:
118+
name: build-artifacts
119+
path: ${{ github.workspace }}/build-artifacts
112120

113121
- name: Authenticate to GitHub registry
114122
run: dotnet nuget add source
@@ -135,15 +143,18 @@ jobs:
135143
packages: write
136144

137145
steps:
138-
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
146+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
139147

140148
- name: Setup .NET
141-
uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1
149+
uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
142150
with:
143151
dotnet-version: 9.0.x
144152

145153
- name: Download build artifacts
146-
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
154+
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
155+
with:
156+
name: build-artifacts
157+
path: ${{ github.workspace }}/build-artifacts
147158

148159
- name: Upload release asset
149160
run: gh release upload ${{ github.event.release.tag_name }}
@@ -160,19 +171,22 @@ jobs:
160171
permissions: { }
161172

162173
steps:
163-
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
174+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
164175

165176
- name: Setup .NET
166-
uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1
177+
uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
167178
with:
168179
dotnet-version: 9.0.x
169180

170181
- name: Download build artifacts
171-
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
182+
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
183+
with:
184+
name: build-artifacts
185+
path: ${{ github.workspace }}/build-artifacts
172186

173187
- name: Publish to NuGet.org (Releases only)
174188
run: dotnet nuget push
175189
${{github.workspace}}/build-artifacts/packages/*.nupkg
176190
--source https://api.nuget.org/v3/index.json
177191
--api-key ${{ secrets.NUGET_KEY_A2A }}
178-
--skip-duplicate
192+
--skip-duplicate

.vscode/extensions.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"recommendations": [
3-
"ms-dotnettools.csharp"
3+
"ms-dotnettools.csharp",
4+
"anweber.vscode-httpyac"
45
]
56
}

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

A2A.slnx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<Solution>
22
<Folder Name="/.github/">
3+
<File Path=".github/copilot-instructions.md" />
34
<File Path=".github/dependabot.yml" />
45
</Folder>
56
<Folder Name="/.github/workflows/">
@@ -15,9 +16,9 @@
1516
<Project Path="samples/SemanticKernelAgent/SemanticKernelAgent.csproj" />
1617
</Folder>
1718
<Folder Name="/src/">
18-
<Project Path="src/A2A/A2A.csproj" />
19-
<Project Path="src/A2A.AspNetCore/A2A.AspNetCore.csproj" />
2019
<File Path="src/Directory.Build.props" />
20+
<Project Path="src/A2A.AspNetCore/A2A.AspNetCore.csproj" />
21+
<Project Path="src/A2A/A2A.csproj" />
2122
</Folder>
2223
<Folder Name="/Solution Items/">
2324
<File Path=".editorconfig" />

Directory.Build.props

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
<Project>
22

3-
<PropertyGroup>
4-
<LangVersion>latest</LangVersion>
5-
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
6-
<Nullable>enable</Nullable>
7-
<ImplicitUsings>enable</ImplicitUsings>
8-
<AnalysisLevel>latest</AnalysisLevel>
9-
<AnalysisMode>recommended</AnalysisMode>
10-
<IsPackable>false</IsPackable>
11-
</PropertyGroup>
3+
<PropertyGroup>
4+
<LangVersion>latest</LangVersion>
5+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
6+
<Nullable>enable</Nullable>
7+
<ImplicitUsings>enable</ImplicitUsings>
8+
<AnalysisLevel>latest</AnalysisLevel>
9+
<AnalysisMode>recommended</AnalysisMode>
10+
<IsPackable>false</IsPackable>
11+
</PropertyGroup>
1212

13+
<PropertyGroup>
14+
<SignAssembly>true</SignAssembly>
15+
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)/Open.snk</AssemblyOriginatorKeyFile>
16+
</PropertyGroup>
1317
</Project>

0 commit comments

Comments
 (0)