Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
ff63646
feat: align InvokeAgentDetails with .NET/Python SDKs
jsl517 Mar 19, 2026
c3eab6b
fix: address Copilot PR review comments and fix all test failures
jsl517 Mar 20, 2026
b0d099d
feat: add source files for scope API alignment
jsl517 Mar 20, 2026
b14fe0a
refactor: address PR review — rename types and unify Request interface
jsl517 Mar 20, 2026
fe81431
docs: update CHANGELOG and design doc for type renames
jsl517 Mar 20, 2026
3b9bb82
refactor: rename callerInfo → callerDetails on InvokeAgentScope
jsl517 Mar 20, 2026
8a03a7b
refactor: make request required on all scopes, rename callerInfo
jsl517 Mar 20, 2026
c9b838e
refactor: make request optional on Inference/ExecuteTool/OutputScope
jsl517 Mar 20, 2026
0e04886
fix: add tenantId check in OutputLoggingMiddleware, remove try/catch
jsl517 Mar 20, 2026
0bf75ea
fix: add defensive null check for InvokeAgentScope.details, add servi…
jsl517 Mar 21, 2026
0e4308f
docs: add CallerDetails migration JSDoc, fix baggage key table in des…
jsl517 Mar 24, 2026
ae8d7dd
refactor: make request first required param on OutputScope.start(), a…
jsl517 Mar 24, 2026
20e8508
test: populate request object in all scope tests for realistic coverage
jsl517 Mar 24, 2026
d91fe81
refactor: split InvokeAgentDetails into InvokeAgentScopeDetails + Age…
jsl517 Mar 24, 2026
476311d
refactor: flatten TenantDetails into AgentDetails, rename InvokeAgent…
jsl517 Mar 24, 2026
3a7bdb6
Merge remote-tracking branch 'origin/main' into users/pefan/syncsdk
jsl517 Mar 25, 2026
55a29bc
refactor: address PR review — remove tenantDetails from base scope, f…
jsl517 Mar 25, 2026
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
29 changes: 22 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

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

- **`InvokeAgentDetails` renamed to `InvokeAgentScopeDetails`** — Now contains only scope-level config (`endpoint`). Agent identity (`AgentDetails`) is a separate parameter. `sessionId` moved to `Request`.
- **`InvokeAgentScope.start()` — new signature.** `start(request, invokeScopeDetails, agentDetails, callerDetails?, spanDetails?)`. Tenant ID is derived from `agentDetails.tenantId` (required). `userDetails` and `callerAgentDetails` are wrapped in `CallerDetails`. Span options grouped in `SpanDetails`.
- **`InferenceScope.start()` — new signature.** `start(request, details, agentDetails, userDetails?, spanDetails?)`. Tenant ID derived from `agentDetails.tenantId` (required).
- **`ExecuteToolScope.start()` — new signature.** `start(request, details, agentDetails, userDetails?, spanDetails?)`. Tenant ID derived from `agentDetails.tenantId` (required).
- **`OutputScope.start()` — new signature.** `start(request, response, agentDetails, userDetails?, spanDetails?)`. Tenant ID derived from `agentDetails.tenantId` (required).
- **`tenantDetails` parameter removed** from all scope `start()` methods. Tenant ID is now required on `AgentDetails.tenantId`; scopes throw if missing.
- **`AgentRequest` renamed to `Request`** — Unified request interface used across all scopes. Removed `executionType` field. Removed separate `InferenceRequest`, `ToolRequest`, `OutputRequest`.
- **`CallerDetails` renamed to `UserDetails`** — Represents the human caller identity.
- **`injectTraceContext()` renamed to `injectContextToHeaders()`**.
- **`extractTraceContext()` renamed to `extractContextFromHeaders()`**.
- **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`.
- **`UserDetails` 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`).
- **`SourceMetadata` renamed to `Channel`** — The exported interface representing invocation channel information is renamed from `SourceMetadata` to `Channel`. The `AgentRequest.sourceMetadata` property is renamed to `channel`.
- **`BaggageBuilder.serviceName()` renamed to `BaggageBuilder.operationSource()`** — Fluent setter for the service name baggage value.
- **`BaggageBuilder.sourceMetadataName()` renamed to `BaggageBuilder.channelName()`** — Fluent setter for the channel name baggage value.
- **`BaggageBuilder.sourceMetadataDescription()` renamed to `BaggageBuilder.channelLink()`** — Fluent setter for the channel link baggage value.
- **`InferenceScope.start()` parameter `sourceMetadata` renamed to `channel`** — Type changed from `Pick<SourceMetadata, "name" | "description">` to `Pick<Channel, "name" | "description">`.
- **`ExecuteToolScope.start()` parameter `sourceMetadata` renamed to `channel`** — Type changed from `Pick<SourceMetadata, "name" | "description">` to `Pick<Channel, "name" | "description">`.
- **`InvokeAgentScope`** now reads `request.channel` instead of `request.sourceMetadata` for channel name/link tags.

### Added (`@microsoft/agents-a365-observability`)

- **`SpanDetails`** — New interface grouping `parentContext`, `startTime`, `endTime`, `spanKind` for scope construction.
- **`CallerDetails`** — New interface wrapping `userDetails` and `callerAgentDetails` for `InvokeAgentScope`.
- **`Request`** — Unified request context interface (`conversationId`, `channel`, `content`, `sessionId`) used across all scopes.
- **`OpenTelemetryScope.recordCancellation()`** — Records a cancellation event on the span with `error.type = 'TaskCanceledException'`.
- **`OpenTelemetryConstants.ERROR_TYPE_CANCELLED`** — Constant for the cancellation error type value.
- **`ObservabilityBuilder.withServiceNamespace()`** — Configures the `service.namespace` resource attribute.

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

Expand All @@ -39,7 +54,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **`ScopeUtils.populateInferenceScopeFromTurnContext(details, turnContext, authToken, ...)`** — New required `authToken: string` parameter.
- **`ScopeUtils.populateInvokeAgentScopeFromTurnContext(details, turnContext, authToken, ...)`** — New required `authToken: string` parameter.
- **`ScopeUtils.populateExecuteToolScopeFromTurnContext(details, turnContext, authToken, ...)`** — New required `authToken: string` parameter.
- **`ScopeUtils.buildInvokeAgentDetails(details, turnContext, authToken)`** — New required `authToken: string` parameter.
- **`ScopeUtils.buildInvokeAgentDetails()`** — Now accepts `AgentDetails` (was `InvokeAgentDetails`) and returns flat `AgentDetails` instead of the old `InvokeAgentDetails` wrapper.

### Added

Expand Down
20 changes: 11 additions & 9 deletions docs/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,14 @@ The foundation for distributed tracing in agent applications. Built on OpenTelem

| Interface | Purpose |
|-----------|---------|
| `InvokeAgentDetails` | Agent endpoint, session ID, and invocation metadata |
| `AgentDetails` | Agent identification and metadata |
| `TenantDetails` | Tenant identification for multi-tenant scenarios |
| `Request` | Request payload (channel, conversationId, content, sessionId) |
| `AgentDetails` | Agent identification and metadata (includes tenantId) |
| `InvokeAgentScopeDetails` | Details for invoking agent scope |
| `InferenceDetails` | Model name, tokens, provider information |
| `ToolCallDetails` | Tool name, arguments, endpoint |
| `CallerDetails` | Caller identification and context |
| `CallerDetails` | Wrapper for caller identity: `userDetails` (human) and/or `callerAgentDetails` (A2A) |
| `UserDetails` | Human caller identification (userId, userEmail, userName, callerClientIp) |
| `SpanDetails` | Optional span configuration (parentContext, startTime, endTime, spanKind) |

**Usage Example:**

Expand Down Expand Up @@ -159,10 +161,10 @@ const scope = new BaggageBuilder()
scope.run(() => {
// Trace agent invocation
using agentScope = InvokeAgentScope.start(
invokeAgentDetails,
tenantDetails,
callerAgentDetails,
callerDetails
{ conversationId, channel: { name: 'Teams' } }, // Request
{ endpoint: { host: 'api.example.com' } }, // InvokeAgentScopeDetails
{ agentId, agentName, tenantId }, // AgentDetails
{ userDetails: { userId, userName } } // CallerDetails (optional)
);

// Agent logic here
Expand Down Expand Up @@ -315,7 +317,7 @@ export class ObservabilityManager {
All scope classes implement the `Disposable` interface for automatic span lifecycle management:

```typescript
using scope = InvokeAgentScope.start(details, tenantDetails);
using scope = InvokeAgentScope.start(request, scopeDetails, agentDetails);
// Span is active
scope.recordResponse('result');
// Span automatically ends when scope is disposed
Expand Down
7 changes: 4 additions & 3 deletions packages/agents-a365-observability-hosting/docs/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,13 @@ async function onMessage(turnContext: TurnContext, turnState: TurnState) {
return baggageScope.run(async () => {
// Create agent invocation scope
using scope = InvokeAgentScope.start(
{ conversationId: turnContext.activity.conversation?.id, sessionId: turnContext.activity.conversation?.id },
{}, // InvokeAgentScopeDetails
{
agentId: turnContext.activity.recipient?.agenticAppId,
agentName: turnContext.activity.recipient?.name,
sessionId: turnContext.activity.conversation?.id
},
{ tenantId: turnContext.activity.recipient?.tenantId }
tenantId: turnContext.activity.recipient?.tenantId
}
);

// Agent processing...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { TurnContext, Middleware, SendActivitiesHandler } from '@microsoft/agent
import {
OutputScope,
AgentDetails,
TenantDetails,
CallerDetails,
UserDetails,
ParentSpanRef,
Request,
SpanDetails,
logger,
isPerRequestExportEnabled,
} from '@microsoft/agents-a365-observability';
Expand Down Expand Up @@ -39,19 +40,23 @@ export class OutputLoggingMiddleware implements Middleware {
async onTurn(context: TurnContext, next: () => Promise<void>): Promise<void> {
const authToken = this.resolveAuthToken(context);
const agentDetails = ScopeUtils.deriveAgentDetails(context, authToken);
const tenantDetails = ScopeUtils.deriveTenantDetails(context);

if (!agentDetails || !tenantDetails) {
if (!agentDetails || !agentDetails.tenantId) {
await next();
return;
}

const callerDetails = ScopeUtils.deriveCallerDetails(context);
const userDetails = ScopeUtils.deriveCallerDetails(context);
const conversationId = ScopeUtils.deriveConversationId(context);
const channel = ScopeUtils.deriveChannelObject(context);

const request: Request = {
conversationId,
channel,
};

context.onSendActivities(
this._createSendHandler(context, agentDetails, tenantDetails, callerDetails, conversationId, channel)
this._createSendHandler(context, agentDetails, userDetails, request)
);

await next();
Expand Down Expand Up @@ -81,10 +86,8 @@ export class OutputLoggingMiddleware implements Middleware {
private _createSendHandler(
turnContext: TurnContext,
agentDetails: AgentDetails,
tenantDetails: TenantDetails,
callerDetails?: CallerDetails,
conversationId?: string,
channel?: { name?: string; description?: string },
userDetails?: UserDetails,
request?: Request,
): SendActivitiesHandler {
return async (_ctx, activities, sendNext) => {
const messages = activities
Expand All @@ -102,14 +105,16 @@ export class OutputLoggingMiddleware implements Middleware {
);
}

const spanDetails: SpanDetails | undefined = parentSpanRef
? { parentContext: parentSpanRef }
: undefined;

const outputScope = OutputScope.start(
request ?? {},
{ messages },
agentDetails,
tenantDetails,
callerDetails,
conversationId,
channel,
parentSpanRef,
userDetails,
spanDetails,
);
try {
return await sendNext();
Expand Down
Loading
Loading