Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Breaking Changes (`@microsoft/agents-a365-observability`)

- **Caller dimension constants renamed to `user.*` namespace** — Aligns with OpenTelemetry semantic conventions and .NET SDK:
- `GEN_AI_CALLER_ID_KEY` (`microsoft.caller.id`) → `USER_ID_KEY` (`user.id`)
- `GEN_AI_CALLER_NAME_KEY` (`microsoft.caller.name`) → `USER_NAME_KEY` (`user.name`)
- `GEN_AI_CALLER_UPN_KEY` (`microsoft.caller.upn`) → `USER_EMAIL_KEY` (`user.email`)
- `GEN_AI_AGENT_UPN_KEY` (`microsoft.agent.user.upn`) → `GEN_AI_AGENT_EMAIL_KEY` (`microsoft.agent.user.email`)
- `GEN_AI_CALLER_AGENT_UPN_KEY` (`microsoft.a365.caller.agent.user.upn`) → `GEN_AI_CALLER_AGENT_EMAIL_KEY` (`microsoft.a365.caller.agent.user.email`)
- **`CallerDetails` properties renamed** — `callerId` → `userId`, `callerUpn` → `userEmail`, `callerName` → `userName`.
- **`AgentDetails.agentUPN` renamed to `AgentDetails.agentEmail`**.
- **`BaggageBuilder` methods renamed** — `callerId()` → `userId()`, `callerName()` → `userName()`, `callerUpn()` → `userEmail()`, `agentUpn()` → `agentEmail()`.
- **`SourceMetadata` renamed to `Channel`** — The exported interface representing invocation channel information is renamed from `SourceMetadata` to `Channel`.
- **`AgentRequest.sourceMetadata` renamed to `AgentRequest.channel`** — The optional property on `AgentRequest` is renamed from `sourceMetadata` to `channel` (type changed from `SourceMetadata` to `Channel`).
- **`BaggageBuilder.serviceName()` renamed to `BaggageBuilder.operationSource()`** — Fluent setter for the service name baggage value.
Expand All @@ -20,6 +29,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Breaking Changes (`@microsoft/agents-a365-observability-hosting`)

- **`ScopeUtils.deriveCallerDetails()` now returns renamed properties** — `callerId` → `userId`, `callerUpn` → `userEmail`, `callerName` → `userName` (matching `CallerDetails` rename).
- **`ScopeUtils.deriveAgentDetails()` / `deriveCallerAgent()` now return `agentEmail` instead of `agentUPN`** (matching `AgentDetails` rename).
- **`getCallerBaggagePairs()` now emits `user.id`, `user.name`, `user.email`** instead of `microsoft.caller.id`, `microsoft.caller.name`, `microsoft.caller.upn`.
- **`ScopeUtils.deriveSourceMetadataObject()` renamed to `ScopeUtils.deriveChannelObject()`**.
- **`BaggageBuilderUtils.setSourceMetadataBaggage()` renamed to `BaggageBuilderUtils.setChannelBaggage()`**.
- **`getSourceMetadataBaggagePairs()` renamed to `getChannelBaggagePairs()`** in `TurnContextUtils`.
Expand Down Expand Up @@ -47,7 +59,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **ObservabilityHostingManager**: `enableBaggage` option now defaults to `false` (was `true`). Callers must explicitly set `enableBaggage: true` to register the BaggageMiddleware.
- `ScopeUtils.deriveAgentDetails` now resolves `agentId` via `activity.getAgenticInstanceId()` for embodied (agentic) agents only. For non-embodied agents, `agentId` is `undefined` since the token's app ID cannot reliably be attributed to the agent.
- `ScopeUtils.deriveAgentDetails` now resolves `agentBlueprintId` from the JWT `xms_par_app_azp` claim via `RuntimeUtility.getAgentIdFromToken()` instead of reading `recipient.agenticAppBlueprintId`.
- `ScopeUtils.deriveAgentDetails` now resolves `agentUPN` via `activity.getAgenticUser()` instead of `recipient.agenticUserId`.
- `ScopeUtils.deriveAgentDetails` now resolves `agentEmail` via `activity.getAgenticUser()` instead of `recipient.agenticUserId`.
- `ScopeUtils.deriveTenantDetails` now uses `activity.getAgenticTenantId()` instead of `recipient.tenantId`.
- `getTargetAgentBaggagePairs` now uses `activity.getAgenticInstanceId()` instead of `recipient.agenticAppId`.
- `getTenantIdPair` now uses `activity.getAgenticTenantId()` instead of manual `channelData` parsing.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export class ScopeUtils {
agentName: recipient.name,
agentAUID: recipient.aadObjectId,
agentBlueprintId,
agentUPN: turnContext?.activity?.getAgenticUser?.(),
agentEmail: turnContext?.activity?.getAgenticUser?.(),
agentDescription: recipient.role,
tenantId: turnContext?.activity?.getAgenticTenantId?.()
} as AgentDetails;
Expand All @@ -87,23 +87,23 @@ export class ScopeUtils {
agentDescription: from.role,
tenantId: from.tenantId,
agentId: from.agenticAppId,
agentUPN: from.agenticUserId
agentEmail: from.agenticUserId
} as AgentDetails;
}


/**
* Derive caller identity details (id, upn, name, tenant, client ip) from the activity from.
* Derive caller identity details (id, email, name, tenant) from the activity from.
* @param turnContext Activity context
* @returns Caller details when available; otherwise undefined.
*/
public static deriveCallerDetails(turnContext: TurnContext): CallerDetails | undefined {
const from = turnContext?.activity?.from;
if (!from) return undefined;
return {
callerId: from.aadObjectId,
callerUpn: from.agenticUserId,
callerName: from.name,
userId: from.aadObjectId,
userEmail: from.agenticUserId,
userName: from.name,
tenantId: from.tenantId,
} as CallerDetails;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ export function getCallerBaggagePairs(turnContext: TurnContext): Array<[string,

const upn = from.agenticUserId;
const pairs: Array<[string, string | undefined]> = [
[OpenTelemetryConstants.GEN_AI_CALLER_ID_KEY, from.aadObjectId],
[OpenTelemetryConstants.GEN_AI_CALLER_NAME_KEY, from.name],
[OpenTelemetryConstants.GEN_AI_CALLER_UPN_KEY, upn],
[OpenTelemetryConstants.USER_ID_KEY, from.aadObjectId],
[OpenTelemetryConstants.USER_NAME_KEY, from.name],
[OpenTelemetryConstants.USER_EMAIL_KEY, upn],
[OpenTelemetryConstants.GEN_AI_CALLER_AGENT_APPLICATION_ID_KEY, from.agenticAppBlueprintId]
];
return normalizePairs(pairs);
Expand Down
12 changes: 6 additions & 6 deletions packages/agents-a365-observability/src/tracing/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@ export class OpenTelemetryConstants {
public static readonly GEN_AI_TOOL_CALL_RESULT_KEY = 'gen_ai.tool.call.result';
public static readonly GEN_AI_TOOL_TYPE_KEY = 'gen_ai.tool.type';

// Agent user (user tied to agent instance during creation) or caller dimensions
public static readonly GEN_AI_CALLER_ID_KEY = 'microsoft.caller.id';
public static readonly GEN_AI_CALLER_NAME_KEY = 'microsoft.caller.name';
public static readonly GEN_AI_CALLER_UPN_KEY = 'microsoft.caller.upn';
// Human caller dimensions (OTel user.* namespace)
public static readonly USER_ID_KEY = 'user.id';
public static readonly USER_NAME_KEY = 'user.name';
public static readonly USER_EMAIL_KEY = 'user.email';
public static readonly GEN_AI_CALLER_CLIENT_IP_KEY = 'client.address';

// Agent to Agent caller agent dimensions
public static readonly GEN_AI_CALLER_AGENT_USER_ID_KEY = 'microsoft.a365.caller.agent.user.id';
public static readonly GEN_AI_CALLER_AGENT_UPN_KEY = 'microsoft.a365.caller.agent.user.upn';
public static readonly GEN_AI_CALLER_AGENT_EMAIL_KEY = 'microsoft.a365.caller.agent.user.email';
public static readonly GEN_AI_CALLER_AGENT_NAME_KEY = 'microsoft.a365.caller.agent.name';
public static readonly GEN_AI_CALLER_AGENT_ID_KEY = 'microsoft.a365.caller.agent.id';
public static readonly GEN_AI_CALLER_AGENT_APPLICATION_ID_KEY = 'microsoft.a365.caller.agent.blueprint.id';
Expand All @@ -86,7 +86,7 @@ export class OpenTelemetryConstants {

// Baggage keys
public static readonly GEN_AI_AGENT_AUID_KEY = 'microsoft.agent.user.id';
public static readonly GEN_AI_AGENT_UPN_KEY = 'microsoft.agent.user.upn';
public static readonly GEN_AI_AGENT_EMAIL_KEY = 'microsoft.agent.user.email';
public static readonly GEN_AI_AGENT_BLUEPRINT_ID_KEY = 'microsoft.a365.agent.blueprint.id';

// Execution context dimensions
Expand Down
15 changes: 6 additions & 9 deletions packages/agents-a365-observability/src/tracing/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ export interface AgentDetails {
/** The agent user ID (AUID) */
agentAUID?: string;

/** The agent user principal name (UPN) */
agentUPN?: string;
/** The agent email address */
agentEmail?: string;

/** The agent blueprint/application ID */
agentBlueprintId?: string;
Expand Down Expand Up @@ -158,16 +158,13 @@ export interface ToolCallDetails {
*/
export interface CallerDetails {
/** The unique identifier for the caller */
callerId?: string;
userId?: string;

/** The user principal name (UPN) of the caller */
callerUpn?: string;
/** The email address of the caller */
userEmail?: string;

/** The display name of the caller */
callerName?: string;

/** The user ID of the caller */
callerUserId?: string;
userName?: string;

/** The tenant ID of the caller */
tenantId?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,12 @@ export class BaggageBuilder {
}

/**
* Set the agent UPN baggage value.
* @param value The agent UPN
* Set the agent email baggage value.
* @param value The agent email
* @returns Self for method chaining
*/
agentUpn(value: string | null | undefined): BaggageBuilder {
this.set(OpenTelemetryConstants.GEN_AI_AGENT_UPN_KEY, value);
agentEmail(value: string | null | undefined): BaggageBuilder {
this.set(OpenTelemetryConstants.GEN_AI_AGENT_EMAIL_KEY, value);
return this;
}

Expand All @@ -100,12 +100,12 @@ export class BaggageBuilder {
}

/**
* Set the caller ID baggage value.
* @param value The caller ID
* Set the user ID baggage value.
* @param value The user ID
* @returns Self for method chaining
*/
callerId(value: string | null | undefined): BaggageBuilder {
this.set(OpenTelemetryConstants.GEN_AI_CALLER_ID_KEY, value);
userId(value: string | null | undefined): BaggageBuilder {
this.set(OpenTelemetryConstants.USER_ID_KEY, value);
return this;
}

Expand Down Expand Up @@ -150,22 +150,22 @@ export class BaggageBuilder {
}

/**
* Set the caller name baggage value.
* @param value The caller name
* Set the user name baggage value.
* @param value The user name
* @returns Self for method chaining
*/
callerName(value: string | null | undefined): BaggageBuilder {
this.set(OpenTelemetryConstants.GEN_AI_CALLER_NAME_KEY, value);
userName(value: string | null | undefined): BaggageBuilder {
this.set(OpenTelemetryConstants.USER_NAME_KEY, value);
return this;
}

/**
* Set the caller UPN baggage value.
* @param value The caller UPN
* Set the user email baggage value.
* @param value The user email
* @returns Self for method chaining
*/
callerUpn(value: string | null | undefined): BaggageBuilder {
this.set(OpenTelemetryConstants.GEN_AI_CALLER_UPN_KEY, value);
userEmail(value: string | null | undefined): BaggageBuilder {
this.set(OpenTelemetryConstants.USER_EMAIL_KEY, value);
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ export const GENERIC_ATTRIBUTES: readonly string[] = [
consts.GEN_AI_AGENT_NAME_KEY,
consts.GEN_AI_AGENT_DESCRIPTION_KEY,
consts.SESSION_DESCRIPTION_KEY,
consts.GEN_AI_AGENT_UPN_KEY,
consts.GEN_AI_AGENT_EMAIL_KEY,
consts.GEN_AI_AGENT_AUID_KEY,
consts.GEN_AI_AGENT_PLATFORM_ID_KEY,
consts.GEN_AI_AGENT_BLUEPRINT_ID_KEY,
consts.SERVICE_NAME_KEY,
// Caller / Invoker attributes
consts.GEN_AI_CALLER_ID_KEY,
consts.GEN_AI_CALLER_NAME_KEY,
consts.GEN_AI_CALLER_UPN_KEY,
consts.USER_ID_KEY,
consts.USER_NAME_KEY,
consts.USER_EMAIL_KEY,
consts.GEN_AI_CALLER_CLIENT_IP_KEY,
// Channel attributes
consts.CHANNEL_NAME_KEY,
Expand All @@ -41,7 +41,7 @@ export const INVOKE_AGENT_ATTRIBUTES: readonly string[] = [
consts.GEN_AI_CALLER_AGENT_ID_KEY,
consts.GEN_AI_CALLER_AGENT_NAME_KEY,
consts.GEN_AI_CALLER_AGENT_USER_ID_KEY,
consts.GEN_AI_CALLER_AGENT_UPN_KEY,
consts.GEN_AI_CALLER_AGENT_EMAIL_KEY,
consts.GEN_AI_CALLER_AGENT_APPLICATION_ID_KEY,
consts.GEN_AI_CALLER_AGENT_PLATFORM_ID_KEY,
];
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export class InvokeAgentScope extends OpenTelemetryScope {
this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_AGENT_ID_KEY, callerAgentDetails.agentId);
this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_AGENT_APPLICATION_ID_KEY, callerAgentDetails.agentBlueprintId);
this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_AGENT_USER_ID_KEY, callerAgentDetails.agentAUID);
this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_AGENT_UPN_KEY, callerAgentDetails.agentUPN);
this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_AGENT_EMAIL_KEY, callerAgentDetails.agentEmail);
this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_AGENT_PLATFORM_ID_KEY, callerAgentDetails.platformId);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export abstract class OpenTelemetryScope implements Disposable {
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);
this.setTagMaybe(OpenTelemetryConstants.GEN_AI_AGENT_UPN_KEY, agentDetails.agentUPN);
this.setTagMaybe(OpenTelemetryConstants.GEN_AI_AGENT_EMAIL_KEY, agentDetails.agentEmail);
this.setTagMaybe(OpenTelemetryConstants.GEN_AI_AGENT_BLUEPRINT_ID_KEY, agentDetails.agentBlueprintId);
}

Expand All @@ -102,9 +102,9 @@ export abstract class OpenTelemetryScope implements Disposable {

// Set caller details if provided
if (callerDetails) {
this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_ID_KEY, callerDetails.callerId);
this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_UPN_KEY, callerDetails.callerUpn);
this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_NAME_KEY, callerDetails.callerName);
this.setTagMaybe(OpenTelemetryConstants.USER_ID_KEY, callerDetails.userId);
this.setTagMaybe(OpenTelemetryConstants.USER_EMAIL_KEY, callerDetails.userEmail);
this.setTagMaybe(OpenTelemetryConstants.USER_NAME_KEY, callerDetails.userName);
this.setTagMaybe(OpenTelemetryConstants.GEN_AI_CALLER_CLIENT_IP_KEY, callerDetails.callerClientIp);
}
}
Expand Down
10 changes: 6 additions & 4 deletions tests/observability/core/SpanProcessor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ describe('SpanProcessor', () => {
const baggageEntries = {
[OpenTelemetryConstants.GEN_AI_OPERATION_NAME_KEY]: OpenTelemetryConstants.INVOKE_AGENT_OPERATION_NAME,
[OpenTelemetryConstants.TENANT_ID_KEY]: 'tenant-123',
[OpenTelemetryConstants.GEN_AI_CALLER_ID_KEY]: 'caller-456'
[OpenTelemetryConstants.USER_ID_KEY]: 'caller-456'
};

let baggage = propagation.createBaggage();
Expand Down Expand Up @@ -163,17 +163,19 @@ describe('SpanProcessor', () => {
expect(GENERIC_ATTRIBUTES).toContain(OpenTelemetryConstants.TENANT_ID_KEY);
expect(GENERIC_ATTRIBUTES).toContain(OpenTelemetryConstants.GEN_AI_AGENT_ID_KEY);
expect(GENERIC_ATTRIBUTES).toContain(OpenTelemetryConstants.SESSION_ID_KEY);
expect(GENERIC_ATTRIBUTES).toContain(OpenTelemetryConstants.GEN_AI_CALLER_ID_KEY);
expect(GENERIC_ATTRIBUTES).toContain(OpenTelemetryConstants.GEN_AI_CALLER_NAME_KEY);
expect(GENERIC_ATTRIBUTES).toContain(OpenTelemetryConstants.GEN_AI_CALLER_UPN_KEY);
expect(GENERIC_ATTRIBUTES).toContain(OpenTelemetryConstants.USER_ID_KEY);
expect(GENERIC_ATTRIBUTES).toContain(OpenTelemetryConstants.USER_NAME_KEY);
expect(GENERIC_ATTRIBUTES).toContain(OpenTelemetryConstants.USER_EMAIL_KEY);
expect(GENERIC_ATTRIBUTES).toContain(OpenTelemetryConstants.GEN_AI_CALLER_CLIENT_IP_KEY);
expect(GENERIC_ATTRIBUTES).toContain(OpenTelemetryConstants.GEN_AI_AGENT_EMAIL_KEY);
expect(GENERIC_ATTRIBUTES).toContain(OpenTelemetryConstants.CHANNEL_NAME_KEY);
expect(GENERIC_ATTRIBUTES).toContain(OpenTelemetryConstants.CHANNEL_LINK_KEY);
expect(GENERIC_ATTRIBUTES).not.toContain('correlation.id');
});

it('should apply invoke agent specific attributes', () => {
expect(INVOKE_AGENT_ATTRIBUTES).toContain(OpenTelemetryConstants.GEN_AI_CALLER_AGENT_ID_KEY);
expect(INVOKE_AGENT_ATTRIBUTES).toContain(OpenTelemetryConstants.GEN_AI_CALLER_AGENT_EMAIL_KEY);
});

it('should include blueprint ID in generic attributes', () => {
Expand Down
Loading
Loading