From 6c661bb533ce4727d7bda25abdf8fbb81172e107 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 4 May 2026 17:54:10 -0700 Subject: [PATCH 1/3] Fix GetCallerBaggagePairs: resolve userId across all channels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit userId was only set from AadObjectId, which is null on non-Teams channels and A2A calls. Add fallback chain: AadObjectId → AgenticUserId → From.Id Port of microsoft/Agent365-dotnet#246 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../Extensions/TurnContextExtensions.cs | 2 +- .../Extensions/TurnContextExtensionsTests.cs | 92 +++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 test/Microsoft.OpenTelemetry.Agent365.Tests/Hosting/Extensions/TurnContextExtensionsTests.cs diff --git a/src/Microsoft.OpenTelemetry/Agent365/Hosting/Extensions/TurnContextExtensions.cs b/src/Microsoft.OpenTelemetry/Agent365/Hosting/Extensions/TurnContextExtensions.cs index 5ea8f8b..4c309ea 100644 --- a/src/Microsoft.OpenTelemetry/Agent365/Hosting/Extensions/TurnContextExtensions.cs +++ b/src/Microsoft.OpenTelemetry/Agent365/Hosting/Extensions/TurnContextExtensions.cs @@ -20,7 +20,7 @@ public static class TurnContextExtensions /// public static IEnumerable> GetCallerBaggagePairs(this ITurnContext turnContext) { - yield return new KeyValuePair(OpenTelemetryConstants.UserIdKey, turnContext.Activity?.From?.AadObjectId); + yield return new KeyValuePair(OpenTelemetryConstants.UserIdKey, turnContext.Activity?.From?.AadObjectId ?? turnContext.Activity?.From?.AgenticUserId ?? turnContext.Activity?.From?.Id); yield return new KeyValuePair(OpenTelemetryConstants.UserNameKey, turnContext.Activity?.From?.Name); } diff --git a/test/Microsoft.OpenTelemetry.Agent365.Tests/Hosting/Extensions/TurnContextExtensionsTests.cs b/test/Microsoft.OpenTelemetry.Agent365.Tests/Hosting/Extensions/TurnContextExtensionsTests.cs new file mode 100644 index 0000000..56023f6 --- /dev/null +++ b/test/Microsoft.OpenTelemetry.Agent365.Tests/Hosting/Extensions/TurnContextExtensionsTests.cs @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using FluentAssertions; +using Microsoft.Agents.A365.Observability.Hosting.Extensions; +using Microsoft.Agents.A365.Observability.Runtime.Tracing.Scopes; +using Microsoft.Agents.Builder; +using Microsoft.Agents.Core.Models; +using Moq; + +namespace Microsoft.Agents.A365.Observability.Hosting.Tests.Extensions; + +[TestClass] +public class TurnContextExtensionsTests +{ + [TestMethod] + public void GetCallerBaggagePairs_AadObjectIdSet_ReturnsAadObjectId() + { + // Arrange – Teams-like channel where AadObjectId, AgenticUserId, and From.Id are all set + var turnContext = CreateTurnContext( + fromId: "from-id", + aadObjectId: "aad-object-id", + agenticUserId: "agentic-user-id"); + + // Act + var pairs = turnContext.GetCallerBaggagePairs().ToDictionary(p => p.Key, p => p.Value); + + // Assert – AadObjectId takes precedence + pairs[OpenTelemetryConstants.UserIdKey].Should().Be("aad-object-id"); + } + + [TestMethod] + public void GetCallerBaggagePairs_NoAadObjectId_FallsBackToAgenticUserId() + { + // Arrange – A2A scenario where AadObjectId is null but AgenticUserId is set + var turnContext = CreateTurnContext( + fromId: "from-id", + aadObjectId: null, + agenticUserId: "agentic-user-id"); + + // Act + var pairs = turnContext.GetCallerBaggagePairs().ToDictionary(p => p.Key, p => p.Value); + + // Assert – falls back to AgenticUserId + pairs[OpenTelemetryConstants.UserIdKey].Should().Be("agentic-user-id"); + } + + [TestMethod] + public void GetCallerBaggagePairs_NoAadObjectIdNoAgenticUserId_FallsBackToFromId() + { + // Arrange – non-Teams channel where only From.Id is available + var turnContext = CreateTurnContext( + fromId: "from-id", + aadObjectId: null, + agenticUserId: null); + + // Act + var pairs = turnContext.GetCallerBaggagePairs().ToDictionary(p => p.Key, p => p.Value); + + // Assert – falls back to From.Id + pairs[OpenTelemetryConstants.UserIdKey].Should().Be("from-id"); + } + + private static ITurnContext CreateTurnContext( + string? fromId = "caller-id", + string? aadObjectId = "caller-aad", + string? agenticUserId = null, + string? fromName = "Caller") + { + var from = new ChannelAccount + { + Id = fromId, + Name = fromName, + AadObjectId = aadObjectId, + }; + + // Set AgenticUserId via reflection or property if available + if (agenticUserId != null) + { + from.AgenticUserId = agenticUserId; + } + + var mockActivity = new Mock(); + mockActivity.Setup(a => a.Type).Returns("message"); + mockActivity.Setup(a => a.From).Returns(from); + + var mockTurnContext = new Mock(); + mockTurnContext.Setup(tc => tc.Activity).Returns(mockActivity.Object); + + return mockTurnContext.Object; + } +} From 3e8ce4fdf08071902be3b91ae09a7df00fd314e1 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 4 May 2026 18:32:34 -0700 Subject: [PATCH 2/3] Address Copilot review feedback: clean up misleading comments Co-Authored-By: Claude Opus 4.6 (1M context) --- .../Hosting/Extensions/TurnContextExtensionsTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Microsoft.OpenTelemetry.Agent365.Tests/Hosting/Extensions/TurnContextExtensionsTests.cs b/test/Microsoft.OpenTelemetry.Agent365.Tests/Hosting/Extensions/TurnContextExtensionsTests.cs index 56023f6..21b17ad 100644 --- a/test/Microsoft.OpenTelemetry.Agent365.Tests/Hosting/Extensions/TurnContextExtensionsTests.cs +++ b/test/Microsoft.OpenTelemetry.Agent365.Tests/Hosting/Extensions/TurnContextExtensionsTests.cs @@ -74,7 +74,7 @@ private static ITurnContext CreateTurnContext( AadObjectId = aadObjectId, }; - // Set AgenticUserId via reflection or property if available + // Set AgenticUserId if (agenticUserId != null) { from.AgenticUserId = agenticUserId; From 1f294cd9e1710642a215fa431266ebb0b9e84f48 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 4 May 2026 18:54:31 -0700 Subject: [PATCH 3/3] Address review: extract from variable, add GUID test to match .NET PR #246 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../Hosting/Extensions/TurnContextExtensions.cs | 5 +++-- .../Extensions/TurnContextExtensionsTests.cs | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.OpenTelemetry/Agent365/Hosting/Extensions/TurnContextExtensions.cs b/src/Microsoft.OpenTelemetry/Agent365/Hosting/Extensions/TurnContextExtensions.cs index 4c309ea..f187824 100644 --- a/src/Microsoft.OpenTelemetry/Agent365/Hosting/Extensions/TurnContextExtensions.cs +++ b/src/Microsoft.OpenTelemetry/Agent365/Hosting/Extensions/TurnContextExtensions.cs @@ -20,8 +20,9 @@ public static class TurnContextExtensions /// public static IEnumerable> GetCallerBaggagePairs(this ITurnContext turnContext) { - yield return new KeyValuePair(OpenTelemetryConstants.UserIdKey, turnContext.Activity?.From?.AadObjectId ?? turnContext.Activity?.From?.AgenticUserId ?? turnContext.Activity?.From?.Id); - yield return new KeyValuePair(OpenTelemetryConstants.UserNameKey, turnContext.Activity?.From?.Name); + var from = turnContext.Activity?.From; + yield return new KeyValuePair(OpenTelemetryConstants.UserIdKey, from?.AadObjectId ?? from?.AgenticUserId ?? from?.Id); + yield return new KeyValuePair(OpenTelemetryConstants.UserNameKey, from?.Name); } /// diff --git a/test/Microsoft.OpenTelemetry.Agent365.Tests/Hosting/Extensions/TurnContextExtensionsTests.cs b/test/Microsoft.OpenTelemetry.Agent365.Tests/Hosting/Extensions/TurnContextExtensionsTests.cs index 21b17ad..f10f6d5 100644 --- a/test/Microsoft.OpenTelemetry.Agent365.Tests/Hosting/Extensions/TurnContextExtensionsTests.cs +++ b/test/Microsoft.OpenTelemetry.Agent365.Tests/Hosting/Extensions/TurnContextExtensionsTests.cs @@ -45,6 +45,22 @@ public void GetCallerBaggagePairs_NoAadObjectId_FallsBackToAgenticUserId() pairs[OpenTelemetryConstants.UserIdKey].Should().Be("agentic-user-id"); } + [TestMethod] + public void GetCallerBaggagePairs_NoAadObjectId_FallsBackToGuidAgenticUserId() + { + // Arrange – A2A scenario where AgenticUserId is a GUID + var turnContext = CreateTurnContext( + fromId: "29:1sH5NArUwkWAX", + aadObjectId: null, + agenticUserId: "bef730f4-d6f5-4ffb-b759-26ffa449ed7e"); + + // Act + var pairs = turnContext.GetCallerBaggagePairs().ToDictionary(p => p.Key, p => p.Value); + + // Assert – falls back to AgenticUserId (GUID format) + pairs[OpenTelemetryConstants.UserIdKey].Should().Be("bef730f4-d6f5-4ffb-b759-26ffa449ed7e"); + } + [TestMethod] public void GetCallerBaggagePairs_NoAadObjectIdNoAgenticUserId_FallsBackToFromId() {