From 87f70e260ca75fa1faa6e039512762664e4714cf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Dec 2025 21:43:26 +0000 Subject: [PATCH 1/5] Initial plan From 446587c50c5f24c8d58a320b8b275dab455170d0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Dec 2025 21:53:31 +0000 Subject: [PATCH 2/5] Add platformId to agent details for observability Co-authored-by: fpfp100 <126631706+fpfp100@users.noreply.github.com> --- .../src/tracing/constants.ts | 1 + .../src/tracing/contracts.ts | 6 ++++ .../src/tracing/middleware/BaggageBuilder.ts | 10 ++++++ .../src/tracing/scopes/OpenTelemetryScope.ts | 1 + .../observability/core/BaggageBuilder.test.ts | 12 +++++++ tests/observability/core/scopes.test.ts | 33 +++++++++++++++++++ 6 files changed, 63 insertions(+) diff --git a/packages/agents-a365-observability/src/tracing/constants.ts b/packages/agents-a365-observability/src/tracing/constants.ts index e8576ed2..21dc0308 100644 --- a/packages/agents-a365-observability/src/tracing/constants.ts +++ b/packages/agents-a365-observability/src/tracing/constants.ts @@ -42,6 +42,7 @@ export class OpenTelemetryConstants { public static readonly GEN_AI_AGENT_ID_KEY = 'gen_ai.agent.id'; public static readonly GEN_AI_AGENT_NAME_KEY = 'gen_ai.agent.name'; public static readonly GEN_AI_AGENT_DESCRIPTION_KEY = 'gen_ai.agent.description'; + public static readonly GEN_AI_AGENT_PLATFORM_ID_KEY = 'gen_ai.agent.platform_id'; public static readonly GEN_AI_CONVERSATION_ID_KEY = 'gen_ai.conversation.id'; public static readonly GEN_AI_CONVERSATION_ITEM_LINK_KEY = 'gen_ai.conversation.item.link'; public static readonly GEN_AI_TOKEN_TYPE_KEY = 'gen_ai.token.type'; diff --git a/packages/agents-a365-observability/src/tracing/contracts.ts b/packages/agents-a365-observability/src/tracing/contracts.ts index a72f0251..fc776c2a 100644 --- a/packages/agents-a365-observability/src/tracing/contracts.ts +++ b/packages/agents-a365-observability/src/tracing/contracts.ts @@ -109,6 +109,9 @@ export interface AgentDetails { /** Optional icon identifier or URL for visual representation of the agent */ iconUri?: string; + + /** Optional platform identifier for the agent */ + platformId?: string; } /** @@ -175,6 +178,9 @@ export interface EnhancedAgentDetails extends AgentDetails { /** The client IP address for the agent user */ agentClientIP?: string; + + /** Optional platform identifier for the agent */ + platformId?: string; } /** diff --git a/packages/agents-a365-observability/src/tracing/middleware/BaggageBuilder.ts b/packages/agents-a365-observability/src/tracing/middleware/BaggageBuilder.ts index 5df88300..fe55289f 100644 --- a/packages/agents-a365-observability/src/tracing/middleware/BaggageBuilder.ts +++ b/packages/agents-a365-observability/src/tracing/middleware/BaggageBuilder.ts @@ -149,6 +149,16 @@ export class BaggageBuilder { return this; } + /** + * Set the agent platform ID baggage value. + * @param value The agent platform ID + * @returns Self for method chaining + */ + agentPlatformId(value: string | null | undefined): BaggageBuilder { + this.set(OpenTelemetryConstants.GEN_AI_AGENT_PLATFORM_ID_KEY, value); + return this; + } + /** * Set the session description baggage value. * @param value The session description diff --git a/packages/agents-a365-observability/src/tracing/scopes/OpenTelemetryScope.ts b/packages/agents-a365-observability/src/tracing/scopes/OpenTelemetryScope.ts index 2089418b..727fb4d7 100644 --- a/packages/agents-a365-observability/src/tracing/scopes/OpenTelemetryScope.ts +++ b/packages/agents-a365-observability/src/tracing/scopes/OpenTelemetryScope.ts @@ -59,6 +59,7 @@ export abstract class OpenTelemetryScope implements Disposable { this.setTagMaybe(OpenTelemetryConstants.GEN_AI_AGENT_ID_KEY, agentDetails.agentId); this.setTagMaybe(OpenTelemetryConstants.GEN_AI_AGENT_NAME_KEY, agentDetails.agentName); this.setTagMaybe(OpenTelemetryConstants.GEN_AI_AGENT_DESCRIPTION_KEY, agentDetails.agentDescription); + this.setTagMaybe(OpenTelemetryConstants.GEN_AI_AGENT_PLATFORM_ID_KEY, agentDetails.platformId); this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CONVERSATION_ID_KEY, agentDetails.conversationId); this.setTagMaybe(OpenTelemetryConstants.GEN_AI_ICON_URI_KEY, agentDetails.iconUri); this.setTagMaybe(OpenTelemetryConstants.GEN_AI_AGENT_AUID_KEY, agentDetails.agentAUID); diff --git a/tests/observability/core/BaggageBuilder.test.ts b/tests/observability/core/BaggageBuilder.test.ts index 808147c5..681d1798 100644 --- a/tests/observability/core/BaggageBuilder.test.ts +++ b/tests/observability/core/BaggageBuilder.test.ts @@ -39,11 +39,23 @@ describe('BaggageBuilder', () => { .agentId('agent-456') .correlationId('corr-789') .agentName('TestAgent') + .agentPlatformId('platform-xyz-123') .conversationId('conv-001'); const scope = builder.build(); expect(scope).toBeInstanceOf(BaggageScope); }); + + it('should set agent platform ID', () => { + const builder = new BaggageBuilder(); + builder.agentPlatformId('platform-abc-456'); + + const scope = builder.build(); + expect(scope).toBeInstanceOf(BaggageScope); + + const bag = propagation.getBaggage((scope as any).contextWithBaggage); + expect(bag?.getEntry(OpenTelemetryConstants.GEN_AI_AGENT_PLATFORM_ID_KEY)?.value).toBe('platform-abc-456'); + }); }); describe('setPairs', () => { diff --git a/tests/observability/core/scopes.test.ts b/tests/observability/core/scopes.test.ts index 8e334a27..89f426ce 100644 --- a/tests/observability/core/scopes.test.ts +++ b/tests/observability/core/scopes.test.ts @@ -80,6 +80,19 @@ describe('Scopes', () => { scope?.dispose(); }); + it('should create scope with platformId', () => { + const invokeAgentDetails: InvokeAgentDetails = { + agentId: 'test-agent', + agentName: 'Test Agent', + platformId: 'platform-xyz-123' + }; + + const scope = InvokeAgentScope.start(invokeAgentDetails, testTenantDetails); + + expect(scope).toBeInstanceOf(InvokeAgentScope); + scope?.dispose(); + }); + it('should create scope with caller details', () => { const invokeAgentDetails: InvokeAgentDetails = { agentId: 'test-agent', @@ -125,6 +138,26 @@ describe('Scopes', () => { scope?.dispose(); }); + it('should propagate platformId in span attributes', () => { + const spy = jest.spyOn(OpenTelemetryScope.prototype as any, 'setTagMaybe'); + const invokeAgentDetails: InvokeAgentDetails = { + agentId: 'test-agent', + agentName: 'Test Agent', + platformId: 'test-platform-123' + }; + + const scope = InvokeAgentScope.start(invokeAgentDetails, testTenantDetails); + expect(scope).toBeInstanceOf(InvokeAgentScope); + + const calls = spy.mock.calls.map(args => ({ key: args[0], val: args[1] })); + expect(calls).toEqual(expect.arrayContaining([ + expect.objectContaining({ key: OpenTelemetryConstants.GEN_AI_AGENT_PLATFORM_ID_KEY, val: 'test-platform-123' }) + ])); + + scope?.dispose(); + spy.mockRestore(); + }); + it('should set caller and caller-agent IP tags', () => { const spy = jest.spyOn(OpenTelemetryScope.prototype as any, 'setTagMaybe'); const invokeAgentDetails: InvokeAgentDetails = { From dd51e8a55a330eaf70ae1f580a7d9da4bbfed280 Mon Sep 17 00:00:00 2001 From: jsl517 Date: Fri, 5 Dec 2025 15:22:26 -0800 Subject: [PATCH 3/5] updates --- .../src/tracing/constants.ts | 3 ++- .../src/tracing/contracts.ts | 2 +- .../src/tracing/middleware/BaggageBuilder.ts | 18 +++++++++++++ .../src/tracing/processors/util.ts | 2 ++ .../src/tracing/scopes/InvokeAgentScope.ts | 2 ++ .../observability/core/BaggageBuilder.test.ts | 11 ++++++++ tests/observability/core/scopes.test.ts | 26 +++++++++++++++++++ 7 files changed, 62 insertions(+), 2 deletions(-) diff --git a/packages/agents-a365-observability/src/tracing/constants.ts b/packages/agents-a365-observability/src/tracing/constants.ts index 21dc0308..14f3e2ef 100644 --- a/packages/agents-a365-observability/src/tracing/constants.ts +++ b/packages/agents-a365-observability/src/tracing/constants.ts @@ -42,7 +42,7 @@ export class OpenTelemetryConstants { public static readonly GEN_AI_AGENT_ID_KEY = 'gen_ai.agent.id'; public static readonly GEN_AI_AGENT_NAME_KEY = 'gen_ai.agent.name'; public static readonly GEN_AI_AGENT_DESCRIPTION_KEY = 'gen_ai.agent.description'; - public static readonly GEN_AI_AGENT_PLATFORM_ID_KEY = 'gen_ai.agent.platform_id'; + public static readonly GEN_AI_AGENT_PLATFORM_ID_KEY = 'gen_ai.agent.platformid'; public static readonly GEN_AI_CONVERSATION_ID_KEY = 'gen_ai.conversation.id'; public static readonly GEN_AI_CONVERSATION_ITEM_LINK_KEY = 'gen_ai.conversation.item.link'; public static readonly GEN_AI_TOKEN_TYPE_KEY = 'gen_ai.token.type'; @@ -80,6 +80,7 @@ export class OpenTelemetryConstants { public static readonly GEN_AI_CALLER_AGENT_ID_KEY = 'gen_ai.caller.agent.id'; public static readonly GEN_AI_CALLER_AGENT_APPLICATION_ID_KEY = 'gen_ai.caller.agent.applicationid'; public static readonly GEN_AI_CALLER_AGENT_CLIENT_IP_KEY = 'gen_ai.caller.agent.user.client.ip'; + public static readonly GEN_AI_CALLER_AGENT_PLATFORM_ID_KEY = "gen_ai.caller.agent.platformid"; // Agent-specific dimensions public static readonly AGENT_ID_KEY = 'gen_ai.agent.id'; public static readonly GEN_AI_TASK_ID_KEY = 'gen_ai.task.id'; diff --git a/packages/agents-a365-observability/src/tracing/contracts.ts b/packages/agents-a365-observability/src/tracing/contracts.ts index fc776c2a..f5150434 100644 --- a/packages/agents-a365-observability/src/tracing/contracts.ts +++ b/packages/agents-a365-observability/src/tracing/contracts.ts @@ -109,7 +109,7 @@ export interface AgentDetails { /** Optional icon identifier or URL for visual representation of the agent */ iconUri?: string; - + /** Optional platform identifier for the agent */ platformId?: string; } diff --git a/packages/agents-a365-observability/src/tracing/middleware/BaggageBuilder.ts b/packages/agents-a365-observability/src/tracing/middleware/BaggageBuilder.ts index fe55289f..ab5f2af6 100644 --- a/packages/agents-a365-observability/src/tracing/middleware/BaggageBuilder.ts +++ b/packages/agents-a365-observability/src/tracing/middleware/BaggageBuilder.ts @@ -189,11 +189,29 @@ export class BaggageBuilder { return this; } + /** + * Set the caller client IP baggage value. + * Used to capture the originating client IP for the request + * so it can be propagated via OpenTelemetry baggage. + * @param value The caller client IP address + * @returns Self for method chaining + */ + callerClientIp(value: string | null | undefined): BaggageBuilder { this.set(OpenTelemetryConstants.GEN_AI_CALLER_CLIENT_IP_KEY, value); return this; } + /** + * Set the caller agent platform ID baggage value. + * @param value The caller agent platform identifier + * @returns Self for method chaining + */ + callerAgentPlatformId(value: string | null | undefined): BaggageBuilder { + this.set(OpenTelemetryConstants.GEN_AI_CALLER_AGENT_PLATFORM_ID_KEY, value); + return this; + } + /** * Set the conversation ID baggage value. * @param value The conversation ID diff --git a/packages/agents-a365-observability/src/tracing/processors/util.ts b/packages/agents-a365-observability/src/tracing/processors/util.ts index b2a41745..ed0e729f 100644 --- a/packages/agents-a365-observability/src/tracing/processors/util.ts +++ b/packages/agents-a365-observability/src/tracing/processors/util.ts @@ -24,6 +24,7 @@ export const GENERIC_ATTRIBUTES: readonly string[] = [ consts.GEN_AI_AGENT_UPN_KEY, consts.GEN_AI_AGENT_BLUEPRINT_ID_KEY, consts.GEN_AI_AGENT_AUID_KEY, + consts.GEN_AI_AGENT_PLATFORM_ID_KEY, ]; /** @@ -44,6 +45,7 @@ export const INVOKE_AGENT_ATTRIBUTES: readonly string[] = [ consts.GEN_AI_CALLER_AGENT_TENANT_ID_KEY, consts.GEN_AI_CALLER_AGENT_APPLICATION_ID_KEY, consts.GEN_AI_CALLER_AGENT_CLIENT_IP_KEY, + consts.GEN_AI_CALLER_AGENT_PLATFORM_ID_KEY, // Execution context consts.GEN_AI_EXECUTION_TYPE_KEY, consts.GEN_AI_EXECUTION_SOURCE_ID_KEY, diff --git a/packages/agents-a365-observability/src/tracing/scopes/InvokeAgentScope.ts b/packages/agents-a365-observability/src/tracing/scopes/InvokeAgentScope.ts index f540c9a2..8e2517e3 100644 --- a/packages/agents-a365-observability/src/tracing/scopes/InvokeAgentScope.ts +++ b/packages/agents-a365-observability/src/tracing/scopes/InvokeAgentScope.ts @@ -93,6 +93,8 @@ export class InvokeAgentScope extends OpenTelemetryScope { this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_AGENT_UPN_KEY, callerAgentDetails.agentUPN); this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_AGENT_TENANT_ID_KEY, callerAgentDetails.tenantId); this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_AGENT_CLIENT_IP_KEY, callerAgentDetails.agentClientIP); + this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_AGENT_PLATFORM_ID_KEY, callerAgentDetails.platformId); + } } diff --git a/tests/observability/core/BaggageBuilder.test.ts b/tests/observability/core/BaggageBuilder.test.ts index 681d1798..ac955472 100644 --- a/tests/observability/core/BaggageBuilder.test.ts +++ b/tests/observability/core/BaggageBuilder.test.ts @@ -56,6 +56,17 @@ describe('BaggageBuilder', () => { const bag = propagation.getBaggage((scope as any).contextWithBaggage); expect(bag?.getEntry(OpenTelemetryConstants.GEN_AI_AGENT_PLATFORM_ID_KEY)?.value).toBe('platform-abc-456'); }); + + it('should set caller agent platform ID via fluent API', () => { + const builder = new BaggageBuilder(); + builder.callerAgentPlatformId('caller-platform-xyz'); + + const scope = builder.build(); + expect(scope).toBeInstanceOf(BaggageScope); + + const bag = propagation.getBaggage((scope as any).contextWithBaggage); + expect(bag?.getEntry(OpenTelemetryConstants.GEN_AI_CALLER_AGENT_PLATFORM_ID_KEY)?.value).toBe('caller-platform-xyz'); + }); }); describe('setPairs', () => { diff --git a/tests/observability/core/scopes.test.ts b/tests/observability/core/scopes.test.ts index 89f426ce..50e8ff58 100644 --- a/tests/observability/core/scopes.test.ts +++ b/tests/observability/core/scopes.test.ts @@ -158,6 +158,32 @@ describe('Scopes', () => { spy.mockRestore(); }); + it('should propagate caller agent platformId in span attributes', () => { + const spy = jest.spyOn(OpenTelemetryScope.prototype as any, 'setTagMaybe'); + const invokeAgentDetails: InvokeAgentDetails = { + agentId: 'test-agent', + agentName: 'Test Agent' + }; + const callerAgentDetails: AgentDetails = { + agentId: 'caller-agent', + agentName: 'Caller Agent', + agentDescription: 'desc', + conversationId: 'conv', + platformId: 'caller-platform-xyz' + } as any; + + const scope = InvokeAgentScope.start(invokeAgentDetails, testTenantDetails, callerAgentDetails, undefined); + expect(scope).toBeInstanceOf(InvokeAgentScope); + + const calls = spy.mock.calls.map(args => ({ key: args[0], val: args[1] })); + expect(calls).toEqual(expect.arrayContaining([ + expect.objectContaining({ key: OpenTelemetryConstants.GEN_AI_CALLER_AGENT_PLATFORM_ID_KEY, val: 'caller-platform-xyz' }) + ])); + + scope?.dispose(); + spy.mockRestore(); + }); + it('should set caller and caller-agent IP tags', () => { const spy = jest.spyOn(OpenTelemetryScope.prototype as any, 'setTagMaybe'); const invokeAgentDetails: InvokeAgentDetails = { From 820d1a26163ec0c477df16c4b1f1182c55bbf808 Mon Sep 17 00:00:00 2001 From: jsl517 Date: Tue, 9 Dec 2025 12:08:26 -0800 Subject: [PATCH 4/5] fix merge error --- .../src/tracing/contracts.ts | 23 +------------------ 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/packages/agents-a365-observability/src/tracing/contracts.ts b/packages/agents-a365-observability/src/tracing/contracts.ts index f60774fa..320b0f7a 100644 --- a/packages/agents-a365-observability/src/tracing/contracts.ts +++ b/packages/agents-a365-observability/src/tracing/contracts.ts @@ -175,28 +175,7 @@ export interface CallerDetails { callerClientIp?: string; } -/** - * Enhanced agent details with additional properties - */ -export interface EnhancedAgentDetails extends AgentDetails { - /** The agent user ID (AUID) */ - agentAUID?: string; - - /** The agent user principal name (UPN) */ - agentUPN?: string; - - /** The agent blueprint/application ID */ - agentBlueprintId?: string; - - /** The tenant ID for the agent */ - tenantId?: string; - - /** The client IP address for the agent user */ - agentClientIP?: string; - - /** Optional platform identifier for the agent */ - platformId?: string; -} +/* * @deprecated Use AgentDetails. EnhancedAgentDetails is now an alias of AgentDetails. */ export type EnhancedAgentDetails = AgentDetails; From f52ffb9a7dceb0eaabf875a520b3a1d8e21555ce Mon Sep 17 00:00:00 2001 From: jsl517 Date: Tue, 9 Dec 2025 12:41:35 -0800 Subject: [PATCH 5/5] comments --- packages/agents-a365-observability/src/tracing/constants.ts | 2 +- .../src/tracing/middleware/BaggageBuilder.ts | 1 - .../src/tracing/scopes/InvokeAgentScope.ts | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/agents-a365-observability/src/tracing/constants.ts b/packages/agents-a365-observability/src/tracing/constants.ts index 14f3e2ef..bc7426a2 100644 --- a/packages/agents-a365-observability/src/tracing/constants.ts +++ b/packages/agents-a365-observability/src/tracing/constants.ts @@ -80,7 +80,7 @@ export class OpenTelemetryConstants { public static readonly GEN_AI_CALLER_AGENT_ID_KEY = 'gen_ai.caller.agent.id'; public static readonly GEN_AI_CALLER_AGENT_APPLICATION_ID_KEY = 'gen_ai.caller.agent.applicationid'; public static readonly GEN_AI_CALLER_AGENT_CLIENT_IP_KEY = 'gen_ai.caller.agent.user.client.ip'; - public static readonly GEN_AI_CALLER_AGENT_PLATFORM_ID_KEY = "gen_ai.caller.agent.platformid"; + public static readonly GEN_AI_CALLER_AGENT_PLATFORM_ID_KEY = 'gen_ai.caller.agent.platformid'; // Agent-specific dimensions public static readonly AGENT_ID_KEY = 'gen_ai.agent.id'; public static readonly GEN_AI_TASK_ID_KEY = 'gen_ai.task.id'; diff --git a/packages/agents-a365-observability/src/tracing/middleware/BaggageBuilder.ts b/packages/agents-a365-observability/src/tracing/middleware/BaggageBuilder.ts index ab5f2af6..1d49936c 100644 --- a/packages/agents-a365-observability/src/tracing/middleware/BaggageBuilder.ts +++ b/packages/agents-a365-observability/src/tracing/middleware/BaggageBuilder.ts @@ -196,7 +196,6 @@ export class BaggageBuilder { * @param value The caller client IP address * @returns Self for method chaining */ - callerClientIp(value: string | null | undefined): BaggageBuilder { this.set(OpenTelemetryConstants.GEN_AI_CALLER_CLIENT_IP_KEY, value); return this; diff --git a/packages/agents-a365-observability/src/tracing/scopes/InvokeAgentScope.ts b/packages/agents-a365-observability/src/tracing/scopes/InvokeAgentScope.ts index 307a4302..9ba96525 100644 --- a/packages/agents-a365-observability/src/tracing/scopes/InvokeAgentScope.ts +++ b/packages/agents-a365-observability/src/tracing/scopes/InvokeAgentScope.ts @@ -94,7 +94,6 @@ export class InvokeAgentScope extends OpenTelemetryScope { this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_AGENT_TENANT_ID_KEY, callerAgentDetails.tenantId); this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_AGENT_CLIENT_IP_KEY, callerAgentDetails.agentClientIP); this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_AGENT_PLATFORM_ID_KEY, callerAgentDetails.platformId); - } }