Description
In the A2A Agent, there is a function to create the A2A Message
private static AgentMessage CreateA2AMessage(A2AAgentSession typedSession, IEnumerable<ChatMessage> messages)
{
var a2aMessage = messages.ToA2AMessage();
// Linking the message to the existing conversation, if any.
// See: https://github.com/a2aproject/A2A/blob/main/docs/topics/life-of-a-task.md#group-related-interactions
a2aMessage.ContextId = typedSession.ContextId;
// Link the message as a follow-up to an existing task, if any.
// See: https://github.com/a2aproject/A2A/blob/main/docs/topics/life-of-a-task.md#task-refinements
a2aMessage.ReferenceTaskIds = typedSession.TaskId is null ? null : [typedSession.TaskId];
return a2aMessage;
}
I would expect that if I set the TaskId in the A2AAgentSession, that it is assigned to the a2aMessage.TaskId field, indicating that the task needs further updates.
The current implementation implies that if the A2A server returns a taskId, that the task is completed and so subsequent calls its added as the ReferenceTaskId. However that is not correct, as the status of a task may not be terminal and so it can be updated by passing setting the TaskId on the agentMessage.
I propose making it clear on the A2AAgentSession what value you are setting related to a Task, the TaskId or the ReferenceTaskIds so its up to the caller to control.
The work around for me in the mean time, is to not use the MS Agent Framework/A2A integration and just use the A2A SDK directly so I can control how the message is built.
Code Sample
public sealed class A2AAgentSession : AgentSession
{
internal A2AAgentSession()
{
}
[JsonConstructor]
internal A2AAgentSession(string? contextId, string? taskId, string[]? referenceTaskIds, AgentSessionStateBag? stateBag) : base(stateBag ?? new())
{
this.ContextId = contextId;
this.TaskId = taskId;
this.ReferenceTaskIds = referenceTaskIds;
}
/// <summary>
/// Gets the ID for the current conversation with the A2A agent.
/// </summary>
[JsonPropertyName("contextId")]
public string? ContextId { get; internal set; }
/// <summary>
/// Gets the ID for the task the agent is currently working on.
/// </summary>
[JsonPropertyName("taskId")]
public string? TaskId { get; internal set; }
/// <summary>
/// Gets the IDs for the tasks the agent is currently referencing.
/// </summary>
[JsonPropertyName("referenceTaskIds")]
public string[]? ReferenceTaskIds { get; internal set; }
/// <inheritdoc/>
internal JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null)
{
var jso = jsonSerializerOptions ?? A2AJsonUtilities.DefaultOptions;
return JsonSerializer.SerializeToElement(this, jso.GetTypeInfo(typeof(A2AAgentSession)));
}
internal static A2AAgentSession Deserialize(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null)
{
if (serializedState.ValueKind != JsonValueKind.Object)
{
throw new ArgumentException("The serialized session state must be a JSON object.", nameof(serializedState));
}
var jso = jsonSerializerOptions ?? A2AJsonUtilities.DefaultOptions;
return serializedState.Deserialize(jso.GetTypeInfo(typeof(A2AAgentSession))) as A2AAgentSession
?? new A2AAgentSession();
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string DebuggerDisplay =>
$"ContextId = {this.ContextId}, TaskId = {this.TaskId}, StateBag Count = {this.StateBag.Count}";
}
then in A2AAgent
private static AgentMessage CreateA2AMessage(A2AAgentSession typedSession, IEnumerable<ChatMessage> messages)
{
var a2aMessage = messages.ToA2AMessage();
// Linking the message to the existing conversation, if any.
// See: https://github.com/a2aproject/A2A/blob/main/docs/topics/life-of-a-task.md#group-related-interactions
a2aMessage.ContextId = typedSession.ContextId;
a2aMesasage.taskId = typedSession.TaskId;
// Link the message as a follow-up to an existing task, if any.
// See: https://github.com/a2aproject/A2A/blob/main/docs/topics/life-of-a-task.md#task-refinements
a2aMessage.ReferenceTaskIds = typedSession.ReferenceTaskIds is null ? null : typedSession.ReferenceTaskIds ;
return a2aMessage;
}
Language/SDK
.NET
Description
In the A2A Agent, there is a function to create the A2A Message
I would expect that if I set the TaskId in the A2AAgentSession, that it is assigned to the a2aMessage.TaskId field, indicating that the task needs further updates.
The current implementation implies that if the A2A server returns a taskId, that the task is completed and so subsequent calls its added as the ReferenceTaskId. However that is not correct, as the status of a task may not be terminal and so it can be updated by passing setting the TaskId on the agentMessage.
I propose making it clear on the A2AAgentSession what value you are setting related to a Task, the TaskId or the ReferenceTaskIds so its up to the caller to control.
The work around for me in the mean time, is to not use the MS Agent Framework/A2A integration and just use the A2A SDK directly so I can control how the message is built.
Code Sample
public sealed class A2AAgentSession : AgentSession { internal A2AAgentSession() { } [JsonConstructor] internal A2AAgentSession(string? contextId, string? taskId, string[]? referenceTaskIds, AgentSessionStateBag? stateBag) : base(stateBag ?? new()) { this.ContextId = contextId; this.TaskId = taskId; this.ReferenceTaskIds = referenceTaskIds; } /// <summary> /// Gets the ID for the current conversation with the A2A agent. /// </summary> [JsonPropertyName("contextId")] public string? ContextId { get; internal set; } /// <summary> /// Gets the ID for the task the agent is currently working on. /// </summary> [JsonPropertyName("taskId")] public string? TaskId { get; internal set; } /// <summary> /// Gets the IDs for the tasks the agent is currently referencing. /// </summary> [JsonPropertyName("referenceTaskIds")] public string[]? ReferenceTaskIds { get; internal set; } /// <inheritdoc/> internal JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null) { var jso = jsonSerializerOptions ?? A2AJsonUtilities.DefaultOptions; return JsonSerializer.SerializeToElement(this, jso.GetTypeInfo(typeof(A2AAgentSession))); } internal static A2AAgentSession Deserialize(JsonElement serializedState, JsonSerializerOptions? jsonSerializerOptions = null) { if (serializedState.ValueKind != JsonValueKind.Object) { throw new ArgumentException("The serialized session state must be a JSON object.", nameof(serializedState)); } var jso = jsonSerializerOptions ?? A2AJsonUtilities.DefaultOptions; return serializedState.Deserialize(jso.GetTypeInfo(typeof(A2AAgentSession))) as A2AAgentSession ?? new A2AAgentSession(); } [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DebuggerDisplay => $"ContextId = {this.ContextId}, TaskId = {this.TaskId}, StateBag Count = {this.StateBag.Count}"; } then in A2AAgent private static AgentMessage CreateA2AMessage(A2AAgentSession typedSession, IEnumerable<ChatMessage> messages) { var a2aMessage = messages.ToA2AMessage(); // Linking the message to the existing conversation, if any. // See: https://github.com/a2aproject/A2A/blob/main/docs/topics/life-of-a-task.md#group-related-interactions a2aMessage.ContextId = typedSession.ContextId; a2aMesasage.taskId = typedSession.TaskId; // Link the message as a follow-up to an existing task, if any. // See: https://github.com/a2aproject/A2A/blob/main/docs/topics/life-of-a-task.md#task-refinements a2aMessage.ReferenceTaskIds = typedSession.ReferenceTaskIds is null ? null : typedSession.ReferenceTaskIds ; return a2aMessage; }Language/SDK
.NET