Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
9f5a02c
implement task support?
DeagleGross Feb 6, 2026
0e642e1
some metadata + session store impl
DeagleGross Feb 6, 2026
a4238c2
address PR comments x1
DeagleGross Feb 11, 2026
f7ce096
API reivew
DeagleGross Feb 11, 2026
d1e5f99
llast changes
DeagleGross Feb 11, 2026
6ec30f0
More test
DeagleGross Feb 11, 2026
ed0fb38
Merge branch 'main' into dmkorolev/a2a-tasks
DeagleGross Feb 11, 2026
256121e
remove unsued import
DeagleGross Feb 11, 2026
45622d5
Merge branch 'dmkorolev/a2a-tasks' of https://github.com/microsoft/ag…
DeagleGross Feb 11, 2026
124407e
fix moq override
DeagleGross Feb 11, 2026
cef0b21
Merge branch 'main' into dmkorolev/a2a-tasks
DeagleGross Feb 12, 2026
2d30ce8
Merge branch 'main' into dmkorolev/a2a-tasks
DeagleGross Feb 20, 2026
1dceb1d
Merge branch 'dmkorolev/a2a-tasks' of https://github.com/microsoft/ag…
DeagleGross Feb 20, 2026
9966c42
refactoring
DeagleGross Feb 20, 2026
4c3b19c
ontaskupdated
DeagleGross Feb 20, 2026
febd97e
adjust to delegate
DeagleGross Feb 20, 2026
4a3b642
Merge branch 'main' into dmkorolev/a2a-tasks
DeagleGross Feb 20, 2026
b400bd8
Merge branch 'dmkorolev/a2a-tasks' of https://github.com/microsoft/ag…
DeagleGross Feb 20, 2026
5bb5939
fix encoding
DeagleGross Feb 20, 2026
5f63b8e
address PR comments: rework
DeagleGross Feb 23, 2026
579faaf
init 1
DeagleGross Feb 23, 2026
3de0627
renaming
DeagleGross Feb 23, 2026
ea428fb
fix tests
DeagleGross Feb 23, 2026
a2b47ac
fix comment
DeagleGross Feb 23, 2026
797f234
Merge branch 'main' into dmkorolev/a2a-tasks
DeagleGross Feb 23, 2026
d84213f
runmode rename
DeagleGross Feb 23, 2026
3aba021
rename
DeagleGross Feb 23, 2026
da94d01
rename
DeagleGross Feb 24, 2026
732900e
use exxperimental api, allow experimental on project level
DeagleGross Feb 24, 2026
a59ae7b
throw on refereceTaskIds
DeagleGross Feb 24, 2026
8f1836b
Merge branch 'main' into dmkorolev/a2a-tasks
DeagleGross Feb 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@

<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />

<PropertyGroup>
<InjectSharedThrow>true</InjectSharedThrow>
<InjectSharedDiagnosticIds>true</InjectSharedDiagnosticIds>
<InjectExperimentalAttributeOnLegacy>true</InjectExperimentalAttributeOnLegacy>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="A2A.AspNetCore" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Text.Json;

namespace Microsoft.Agents.AI.Hosting.A2A;

/// <summary>
/// Provides JSON serialization options for A2A Hosting APIs to support AOT and trimming.
/// </summary>
public static class A2AHostingJsonUtilities
{
/// <summary>
/// Gets the default <see cref="JsonSerializerOptions"/> instance used for A2A Hosting serialization.
/// </summary>
public static JsonSerializerOptions DefaultOptions { get; } = CreateDefaultOptions();

private static JsonSerializerOptions CreateDefaultOptions()
{
JsonSerializerOptions options = new(global::A2A.A2AJsonUtilities.DefaultOptions);

// Chain in the resolvers from both AgentAbstractionsJsonUtilities and the A2A SDK context.
// AgentAbstractionsJsonUtilities is first to ensure M.E.AI types (e.g. ResponseContinuationToken)
// are handled via its resolver, followed by the A2A SDK resolver for protocol types.
options.TypeInfoResolverChain.Clear();
options.TypeInfoResolverChain.Add(AgentAbstractionsJsonUtilities.DefaultOptions.TypeInfoResolver!);
options.TypeInfoResolverChain.Add(global::A2A.A2AJsonUtilities.DefaultOptions.TypeInfoResolver!);

options.MakeReadOnly();
return options;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) Microsoft. All rights reserved.

using A2A;

namespace Microsoft.Agents.AI.Hosting.A2A;

/// <summary>
/// Provides context for a custom A2A run mode decision.
/// </summary>
public sealed class A2ARunDecisionContext
{
internal A2ARunDecisionContext(MessageSendParams messageSendParams)
{
this.MessageSendParams = messageSendParams;
}

/// <summary>
/// Gets the parameters of the incoming A2A message that triggered this run.
/// </summary>
public MessageSendParams MessageSendParams { get; }
}
265 changes: 237 additions & 28 deletions dotnet/src/Microsoft.Agents.AI.Hosting.A2A/AIAgentExtensions.cs

Large diffs are not rendered by default.

105 changes: 105 additions & 0 deletions dotnet/src/Microsoft.Agents.AI.Hosting.A2A/AgentRunMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Shared.DiagnosticIds;

namespace Microsoft.Agents.AI.Hosting.A2A;

/// <summary>
/// Specifies how the A2A hosting layer determines whether to run <see cref="AIAgent"/> in background or not.
/// </summary>
[Experimental(DiagnosticIds.Experiments.AIResponseContinuations)]
public sealed class AgentRunMode : IEquatable<AgentRunMode>
{
private const string MessageValue = "message";
private const string TaskValue = "task";
private const string DynamicValue = "dynamic";

private readonly string _value;
private readonly Func<A2ARunDecisionContext, CancellationToken, ValueTask<bool>>? _runInBackground;

private AgentRunMode(string value, Func<A2ARunDecisionContext, CancellationToken, ValueTask<bool>>? runInBackground = null)
{
this._value = value;
this._runInBackground = runInBackground;
}

/// <summary>
/// Dissallows the background responses from the agent. Is equivalent to configuring <see cref="AgentRunOptions.AllowBackgroundResponses"/> as <c>false</c>.
/// In the A2A protocol terminology will make responses be returned as <c>AgentMessage</c>.
/// </summary>
public static AgentRunMode DisallowBackground => new(MessageValue);

/// <summary>
/// Allows the background responses from the agent. Is equivalent to configuring <see cref="AgentRunOptions.AllowBackgroundResponses"/> as <c>true</c>.
/// In the A2A protocol terminology will make responses be returned as <c>AgentTask</c> if the agent supports background responses, and as <c>AgentMessage</c> otherwise.
/// </summary>
public static AgentRunMode AllowBackgroundIfSupported => new(TaskValue);

/// <summary>
/// The agent run mode is decided by the supplied <paramref name="runInBackground"/> delegate.
/// The delegate receives an <see cref="A2ARunDecisionContext"/> with the incoming
/// message and returns a boolean specifying whether to run the agent in background mode.
/// <see langword="true"/> indicates that the agent should run in background mode and return an
/// <c>AgentTask</c> if the agent supports background mode; otherwise, it returns an <c>AgentMessage</c>
/// if the mode is not supported. <see langword="false"/> indicates that the agent should run in
/// non-background mode and return an <c>AgentMessage</c>.
/// </summary>
/// <param name="runInBackground">
/// An async delegate that decides whether the response should be wrapped in an <c>AgentTask</c>.
/// </param>
public static AgentRunMode AllowBackgroundWhen(Func<A2ARunDecisionContext, CancellationToken, ValueTask<bool>> runInBackground)
{
ArgumentNullException.ThrowIfNull(runInBackground);
return new(DynamicValue, runInBackground);
}

/// <summary>
/// Determines whether the agent response should be returned as an <c>AgentTask</c>.
/// </summary>
internal ValueTask<bool> ShouldRunInBackgroundAsync(A2ARunDecisionContext context, CancellationToken cancellationToken)
{
if (string.Equals(this._value, MessageValue, StringComparison.OrdinalIgnoreCase))
{
return ValueTask.FromResult(false);
}

if (string.Equals(this._value, TaskValue, StringComparison.OrdinalIgnoreCase))
{
return ValueTask.FromResult(true);
}

// Dynamic: delegate to custom callback.
if (this._runInBackground is not null)
{
return this._runInBackground(context, cancellationToken);
}

// No delegate provided — fall back to "message" behavior.
return ValueTask.FromResult(true);
}

/// <inheritdoc/>
public bool Equals(AgentRunMode? other) =>
other is not null && string.Equals(this._value, other._value, StringComparison.OrdinalIgnoreCase);

/// <inheritdoc/>
public override bool Equals(object? obj) => this.Equals(obj as AgentRunMode);

/// <inheritdoc/>
public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(this._value);

/// <inheritdoc/>
public override string ToString() => this._value;

/// <summary>Determines whether two <see cref="AgentRunMode"/> instances are equal.</summary>
public static bool operator ==(AgentRunMode? left, AgentRunMode? right) =>
left?.Equals(right) ?? right is null;

/// <summary>Determines whether two <see cref="AgentRunMode"/> instances are not equal.</summary>
public static bool operator !=(AgentRunMode? left, AgentRunMode? right) =>
!(left == right);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

using System.Collections.Generic;
using System.Text.Json;
using A2A;
using Microsoft.Extensions.AI;

namespace Microsoft.Agents.AI.Hosting.A2A.Converters;
Expand Down Expand Up @@ -37,7 +36,7 @@ internal static class AdditionalPropertiesDictionaryExtensions
continue;
}

metadata[kvp.Key] = JsonSerializer.SerializeToElement(kvp.Value, A2AJsonUtilities.DefaultOptions.GetTypeInfo(typeof(object)));
metadata[kvp.Key] = JsonSerializer.SerializeToElement(kvp.Value, A2AHostingJsonUtilities.DefaultOptions.GetTypeInfo(typeof(object)));
}

return metadata;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

<PropertyGroup>
<InjectSharedThrow>true</InjectSharedThrow>
<InjectSharedDiagnosticIds>true</InjectSharedDiagnosticIds>
<InjectExperimentalAttributeOnLegacy>true</InjectExperimentalAttributeOnLegacy>
</PropertyGroup>

<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
Expand Down
Loading
Loading