// Copyright (c) Microsoft. All rights reserved. using System; using System.Diagnostics.CodeAnalysis; using Microsoft.Shared.DiagnosticIds; using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI.Foundry.Hosting; /// /// Extension methods for reading and writing the associated /// with an in a Foundry hosted agent. /// /// /// The hosted session context is written exactly once by the hosting layer when a session is created, /// and is validated against the live request on every subsequent invocation. The /// method is intentionally so that only the hosting layer can establish the /// identity values; consumers (such as implementations) read the values /// through the public accessor. /// [Experimental(DiagnosticIds.Experiments.AIOpenAIResponses)] public static class HostedSessionContextExtensions { /// /// The well-known key used to store the /// on a session. /// /// /// Exposed as a constant so consumers can correlate persisted state across processes. /// External code must not write to this key directly; use from the /// hosting assembly instead. /// public const string StateKey = "Microsoft.Agents.AI.Foundry.Hosting.HostedSessionContext"; /// /// Gets the previously written by the hosting layer /// for this session, if any. /// /// The session to read from. /// /// The for the session, or when the /// session was not produced by a hosted agent (or the value has not yet been written). /// /// Thrown when is . public static HostedSessionContext? GetHostedContext(this AgentSession session) { Throw.IfNull(session); return session.StateBag.TryGetValue(StateKey, out var context, HostedSessionJsonUtilities.DefaultOptions) ? context : null; } /// /// Writes the for this session. /// /// The session to write to. /// The hosted session context to associate with . /// /// Internal to the hosting assembly. Consumers must not invoke this method directly; the hosting /// layer is the single writer and uses validation against the live request to detect any tampering /// that does occur via lower-level APIs. Throws when a context has already been written for this /// session to enforce the write-once contract. /// /// Thrown when or is . /// Thrown when this session already carries a . internal static void SetHostedContext(this AgentSession session, HostedSessionContext context) { Throw.IfNull(session); Throw.IfNull(context); if (session.StateBag.TryGetValue(StateKey, out _, HostedSessionJsonUtilities.DefaultOptions)) { throw new InvalidOperationException( $"A {nameof(HostedSessionContext)} has already been written to this session. " + "The hosted session identity is write-once; resumed sessions must validate against the existing context, not overwrite it."); } session.StateBag.SetValue(StateKey, context, HostedSessionJsonUtilities.DefaultOptions); } }