// Copyright (c) Microsoft. All rights reserved. using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.DurableTask.Entities; namespace Microsoft.Agents.AI.DurableTask; /// /// Represents an agent session ID, which is used to identify a long-running agent session. /// [JsonConverter(typeof(AgentSessionIdJsonConverter))] public readonly struct AgentSessionId : IEquatable { private const string EntityNamePrefix = "dafx-"; private readonly EntityInstanceId _entityId; /// /// Initializes a new instance of the struct. /// /// The name of the agent that owns the session (case-insensitive). /// The unique key of the agent session (case-sensitive). public AgentSessionId(string name, string key) { this.Name = name; this._entityId = new EntityInstanceId(ToEntityName(name), key); } /// /// Gets the name of the agent that owns the session. Names are case-insensitive. /// public string Name { get; } /// /// Gets the unique key of the agent session. Keys are case-sensitive and are used to identify the session. /// public string Key => this._entityId.Key; /// /// Converts an agent name to its underlying entity name representation. /// /// The agent name. /// The entity name used by Durable Task for this agent. internal static string ToEntityName(string name) => $"{EntityNamePrefix}{name}"; /// /// Converts the to an . /// /// The representation of the . internal EntityInstanceId ToEntityId() => this._entityId; /// /// Creates a new with the specified name and a randomly generated key. /// /// The name of the agent that owns the session. /// A new with the specified name and a random key. public static AgentSessionId WithRandomKey(string name) => new(name, Guid.NewGuid().ToString("N")); /// /// Determines whether two instances are equal. /// /// The first to compare. /// The second to compare. /// true if the two instances are equal; otherwise, false. public static bool operator ==(AgentSessionId left, AgentSessionId right) => left._entityId == right._entityId; /// /// Determines whether two instances are not equal. /// /// The first to compare. /// The second to compare. /// true if the two instances are not equal; otherwise, false. public static bool operator !=(AgentSessionId left, AgentSessionId right) => left._entityId != right._entityId; /// /// Determines whether the specified is equal to the current . /// /// The to compare with the current . /// true if the specified is equal to the current ; otherwise, false. public bool Equals(AgentSessionId other) => this == other; /// /// Determines whether the specified object is equal to the current . /// /// The object to compare with the current . /// true if the specified object is equal to the current ; otherwise, false. public override bool Equals(object? obj) => obj is AgentSessionId other && this == other; /// /// Returns the hash code for this . /// /// A hash code for the current . public override int GetHashCode() => this._entityId.GetHashCode(); /// /// Returns a string representation of this in the form of @name@key. /// /// A string representation of the current . public override string ToString() => this._entityId.ToString(); /// /// Converts the string representation of an agent session ID to its equivalent. /// The input string must be in the form of @name@key. /// /// A string containing an agent session ID to convert. /// A equivalent to the agent session ID contained in . /// Thrown when is not a valid agent session ID format. public static AgentSessionId Parse(string sessionIdString) { EntityInstanceId entityId = EntityInstanceId.FromString(sessionIdString); if (!entityId.Name.StartsWith(EntityNamePrefix, StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException($"'{sessionIdString}' is not a valid agent session ID.", nameof(sessionIdString)); } return new AgentSessionId(entityId.Name[EntityNamePrefix.Length..], entityId.Key); } /// /// Implicitly converts an to an . /// This conversion is useful for entity API interoperability. /// /// The to convert. /// The equivalent . public static implicit operator EntityInstanceId(AgentSessionId agentSessionId) => agentSessionId.ToEntityId(); /// /// Implicitly converts an to an . /// /// The to convert. /// The equivalent . [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1065:Do not raise exceptions in unexpected locations", Justification = "Implicit conversion must validate format.")] public static implicit operator AgentSessionId(EntityInstanceId entityId) { if (!entityId.Name.StartsWith(EntityNamePrefix, StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException($"'{entityId}' is not a valid agent session ID.", nameof(entityId)); } return new AgentSessionId(entityId.Name[EntityNamePrefix.Length..], entityId.Key); } /// /// Custom JSON converter for to ensure proper serialization and deserialization. /// public sealed class AgentSessionIdJsonConverter : JsonConverter { /// public override AgentSessionId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType != JsonTokenType.String) { throw new JsonException("Expected string value"); } string value = reader.GetString() ?? string.Empty; return Parse(value); } /// public override void Write(Utf8JsonWriter writer, AgentSessionId value, JsonSerializerOptions options) { writer.WriteStringValue(value.ToString()); } } }