diff --git a/packages/agents-a365-observability-hosting/src/utils/TurnContextUtils.ts b/packages/agents-a365-observability-hosting/src/utils/TurnContextUtils.ts index acc7285b..7781095b 100644 --- a/packages/agents-a365-observability-hosting/src/utils/TurnContextUtils.ts +++ b/packages/agents-a365-observability-hosting/src/utils/TurnContextUtils.ts @@ -27,10 +27,10 @@ export function getCallerBaggagePairs(turnContext: TurnContext): Array<[string, return []; } const from = turnContext.activity.from; - + const upn = from.agenticUserId; const pairs: Array<[string, string | undefined]> = [ - [OpenTelemetryConstants.USER_ID_KEY, from.aadObjectId], + [OpenTelemetryConstants.USER_ID_KEY, from.aadObjectId || from.agenticUserId || from.id], [OpenTelemetryConstants.USER_NAME_KEY, from.name], [OpenTelemetryConstants.USER_EMAIL_KEY, upn], [OpenTelemetryConstants.GEN_AI_CALLER_AGENT_APPLICATION_ID_KEY, from.agenticAppBlueprintId] diff --git a/tests/jest.config.cjs b/tests/jest.config.cjs index 3627b5ff..be68efed 100644 --- a/tests/jest.config.cjs +++ b/tests/jest.config.cjs @@ -71,6 +71,7 @@ module.exports = { '^@microsoft/agents-a365-observability$': '/packages/agents-a365-observability/src', '^@microsoft/agents-a365-observability-extensions-langchain$': '/packages/agents-a365-observability-extensions-langchain/src', '^@microsoft/agents-a365-observability-extensions-openai$': '/packages/agents-a365-observability-extensions-openai/src', + '^@microsoft/agents-a365-observability-hosting$': '/packages/agents-a365-observability-hosting/src', '^@microsoft/agents-a365-observability-tokencache$': '/packages/agents-a365-observability-tokencache/src', '^@microsoft/agents-a365-tooling$': '/packages/agents-a365-tooling/src', '^@microsoft/agents-a365-tooling-extensions-claude$': '/packages/agents-a365-tooling-extensions-claude/src', diff --git a/tests/observability/extension/hosting/BaggageBuilderUtils.test.ts b/tests/observability/extension/hosting/BaggageBuilderUtils.test.ts index 023d2d03..23983744 100644 --- a/tests/observability/extension/hosting/BaggageBuilderUtils.test.ts +++ b/tests/observability/extension/hosting/BaggageBuilderUtils.test.ts @@ -45,7 +45,7 @@ describe('BaggageBuilderUtils', () => { expect(result).toBe(builder); // Validate every expected OpenTelemetry baggage key and value const asObj = Object.fromEntries(capturedPairs); - expect(asObj[OpenTelemetryConstants.USER_ID_KEY]).toBeUndefined(); + expect(asObj[OpenTelemetryConstants.USER_ID_KEY]).toBe('agentic-user-1'); expect(asObj[OpenTelemetryConstants.USER_NAME_KEY]).toBe('User One'); expect(asObj[OpenTelemetryConstants.USER_EMAIL_KEY]).toBe('agentic-user-1'); expect(asObj[OpenTelemetryConstants.GEN_AI_AGENT_ID_KEY]).toBe('agent-app-1'); diff --git a/tests/observability/extension/hosting/TurnContextUtils.test.ts b/tests/observability/extension/hosting/TurnContextUtils.test.ts index 675c01ce..8d811c0a 100644 --- a/tests/observability/extension/hosting/TurnContextUtils.test.ts +++ b/tests/observability/extension/hosting/TurnContextUtils.test.ts @@ -32,6 +32,58 @@ describe('TurnContextUtils', () => { expect(pairs.length).toBeGreaterThan(0); }); + it('should fall back to from.id for userId when aadObjectId is undefined (non-Teams channel)', () => { + const ctx = { + activity: { + from: { id: 'user1', name: 'User One' }, + recipient: { id: 'agent1', name: 'Agent One' }, + conversation: { id: 'conv-1' }, + }, + } as any; + const pairs = getCallerBaggagePairs(ctx); + const obj = Object.fromEntries(pairs); + expect(obj[OpenTelemetryConstants.USER_ID_KEY]).toBe('user1'); + }); + + it('should fall back to agenticUserId for userId when aadObjectId is undefined (A2A)', () => { + const ctx = { + activity: { + from: { id: 'user1', name: 'User One', agenticUserId: 'agentic-user-1' }, + recipient: { id: 'agent1', name: 'Agent One' }, + conversation: { id: 'conv-1' }, + }, + } as any; + const pairs = getCallerBaggagePairs(ctx); + const obj = Object.fromEntries(pairs); + expect(obj[OpenTelemetryConstants.USER_ID_KEY]).toBe('agentic-user-1'); + }); + + it('should prefer aadObjectId for userId when all three fields are set', () => { + const ctx = { + activity: { + from: { id: 'user1', name: 'User One', aadObjectId: 'aad-123', agenticUserId: 'agentic-user-1' }, + recipient: { id: 'agent1', name: 'Agent One' }, + conversation: { id: 'conv-1' }, + }, + } as any; + const pairs = getCallerBaggagePairs(ctx); + const obj = Object.fromEntries(pairs); + expect(obj[OpenTelemetryConstants.USER_ID_KEY]).toBe('aad-123'); + }); + + it('should resolve userId to agenticUserId when it is a GUID (A2A with GUID agenticUserId)', () => { + const ctx = { + activity: { + from: { id: 'user1', name: 'User One', agenticUserId: 'bef730f4-d6f5-4ffb-b759-26ffa449ed7e' }, + recipient: { id: 'agent1', name: 'Agent One' }, + conversation: { id: 'conv-1' }, + }, + } as any; + const pairs = getCallerBaggagePairs(ctx); + const obj = Object.fromEntries(pairs); + expect(obj[OpenTelemetryConstants.USER_ID_KEY]).toBe('bef730f4-d6f5-4ffb-b759-26ffa449ed7e'); + }); + it('should get target agent baggage pairs', () => { const pairs = getTargetAgentBaggagePairs(mockTurnContext); expect(Array.isArray(pairs)).toBe(true);