From c94911708f108bb6efb0c6701fe52c40358fb0fb Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 4 May 2026 17:54:03 -0700 Subject: [PATCH 1/2] Fix get_caller_pairs: 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 aad_object_id, which is None on non-Teams channels and A2A calls. Add fallback chain: aad_object_id → agentic_user_id → frm.id Port of microsoft/Agent365-dotnet#246 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../a365/hosting/scope_helpers/utils.py | 2 +- .../scope_helpers/test_scope_helper_utils.py | 45 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py index a02da39d..9476f5ed 100644 --- a/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py +++ b/src/microsoft/opentelemetry/a365/hosting/scope_helpers/utils.py @@ -37,7 +37,7 @@ def get_caller_pairs(activity: Activity) -> Iterator[tuple[str, Any]]: frm = activity.from_property if not frm: return - yield USER_ID_KEY, frm.aad_object_id + yield USER_ID_KEY, frm.aad_object_id or frm.agentic_user_id or frm.id yield USER_NAME_KEY, frm.name yield USER_EMAIL_KEY, frm.agentic_user_id diff --git a/tests/a365/hosting/scope_helpers/test_scope_helper_utils.py b/tests/a365/hosting/scope_helpers/test_scope_helper_utils.py index 8d0ee845..5e9606f7 100644 --- a/tests/a365/hosting/scope_helpers/test_scope_helper_utils.py +++ b/tests/a365/hosting/scope_helpers/test_scope_helper_utils.py @@ -83,6 +83,51 @@ def test_get_channel_pairs(): assert (CHANNEL_LINK_KEY, None) in result +def test_get_caller_pairs_fallback_to_frm_id(): + """Test get_caller_pairs falls back to frm.id when aad_object_id is None (non-Teams channel).""" + from_account = ChannelAccount( + id="slack-user-123", + name="Slack User", + agentic_user_id=None, + aad_object_id=None, + ) + activity = Activity(type="message", from_property=from_account) + + result = list(get_caller_pairs(activity)) + + assert (USER_ID_KEY, "slack-user-123") in result + + +def test_get_caller_pairs_fallback_to_agentic_user_id(): + """Test get_caller_pairs falls back to agentic_user_id for A2A when aad_object_id is None.""" + from_account = ChannelAccount( + id="raw-id", + name="Agent Caller", + agentic_user_id="agent-auid-456", + aad_object_id=None, + ) + activity = Activity(type="message", from_property=from_account) + + result = list(get_caller_pairs(activity)) + + assert (USER_ID_KEY, "agent-auid-456") in result + + +def test_get_caller_pairs_aad_object_id_takes_precedence(): + """Test get_caller_pairs uses aad_object_id when all identifiers are set.""" + from_account = ChannelAccount( + id="raw-id", + name="Teams User", + agentic_user_id="agent-auid", + aad_object_id="aad-wins", + ) + activity = Activity(type="message", from_property=from_account) + + result = list(get_caller_pairs(activity)) + + assert (USER_ID_KEY, "aad-wins") in result + + def test_get_conversation_pairs(): """Test get_conversation_pairs extracts conversation information.""" conversation = ConversationAccount(id="conversation-123") From 375cd2122407fe929169e47151597217b28284ea Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 4 May 2026 18:54:30 -0700 Subject: [PATCH 2/2] Add GUID AgenticUserId test to match .NET PR #246 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../scope_helpers/test_scope_helper_utils.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/a365/hosting/scope_helpers/test_scope_helper_utils.py b/tests/a365/hosting/scope_helpers/test_scope_helper_utils.py index 5e9606f7..07f5a63f 100644 --- a/tests/a365/hosting/scope_helpers/test_scope_helper_utils.py +++ b/tests/a365/hosting/scope_helpers/test_scope_helper_utils.py @@ -128,6 +128,19 @@ def test_get_caller_pairs_aad_object_id_takes_precedence(): assert (USER_ID_KEY, "aad-wins") in result +def test_get_caller_pairs_a2a_guid_agentic_user_id(): + """Test userId resolves to GUID AgenticUserId in A2A scenario.""" + from_account = ChannelAccount( + id="29:1sH5NArUwkWAX", + name="Agent Caller", + agentic_user_id="bef730f4-d6f5-4ffb-b759-26ffa449ed7e", + aad_object_id=None, + ) + activity = Activity(type="message", from_property=from_account) + result = list(get_caller_pairs(activity)) + assert (USER_ID_KEY, "bef730f4-d6f5-4ffb-b759-26ffa449ed7e") in result + + def test_get_conversation_pairs(): """Test get_conversation_pairs extracts conversation information.""" conversation = ConversationAccount(id="conversation-123")