v1.5
Major updates in this version:
- OpenTelemetry support — the SDK has been fully instrumented for oTel
- Proactive messaging improvements — new methods and helpers for sending proactive messages
- Improvements to "typing" behavior
- New format for oAuth environment variable names
New: OpenTelemetry Support (@microsoft/agents-telemetry)
Version 1.5 introduces @microsoft/agents-telemetry — a new package providing distributed tracing, structured log emission, and metric recording across the entire SDK.
All major SDK components are now instrumented out of the box: turn processing, HTTP connector calls, storage reads/writes, authentication flows, dialog execution, and Copilot Studio client operations.
Telemetry is zero-overhead by default — if you don't install the OpenTelemetry API packages, all instrumentation paths are safe no-ops.
Getting started
Install the peer dependencies in your agent project:
npm install @opentelemetry/api @opentelemetry/api-logs
Set up a provider early in your application entry point (before any SDK code runs):
import { NodeSDK } from '@opentelemetry/sdk-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
const sdk = new NodeSDK({
traceExporter: new OTLPTraceExporter({
url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
}),
serviceName: 'my-agent-service',
})
sdk.start()See the full README for the complete span/metric catalog and advanced usage (custom spans, metrics, log bridging).
New: Proactive Messaging via AgentApplication
The proactive messaging subsystem is now a first-class part of AgentApplication, accessible via app.proactive. It is initialized automatically when your app has a storage configured — no extra setup required.
Store a conversation reference during a live turn
Call storeConversation inside any activity handler. It saves the conversation reference to storage and returns its ID for later use.
app.onActivity('message', async (ctx, state) => {
const convId = await app.proactive.storeConversation(ctx)
await ctx.sendActivity(`Conversation stored. ID: ${convId}`)
})Send a proactive message by stored ID
From a webhook, scheduled job, or any out-of-turn code:
await app.proactive.sendActivity(adapter, convId, { text: 'Your report is ready.' })Continue a full conversation turn (with state)
Use continueConversation when you need access to TurnState, the full handler pipeline, or want to send more than one activity:
await app.proactive.continueConversation(adapter, convId, async (ctx, state) => {
const count = state.getValue<number>('conversation.notificationCount') ?? 0
state.setValue('conversation.notificationCount', count + 1)
await ctx.sendActivity(`Notification #${count + 1}: threshold exceeded.`)
})Create a brand-new conversation
Use createConversation to initiate a 1:1 conversation with a Teams user who has never messaged your agent. Build the options with CreateConversationOptionsBuilder:
import { CreateConversationOptionsBuilder } from '@microsoft/agents-hosting'
const opts = CreateConversationOptionsBuilder
.create(process.env.CLIENT_ID!, 'msteams')
.withUser('user-aad-object-id')
.withTenantId('tenant-id')
.storeConversation(true) // auto-store so you can retrieve it later
.build()
const conv = await app.proactive.createConversation(adapter, opts, async (ctx, state) => {
await ctx.sendActivity('Hi! I have an update for you.')
})Build a ConversationReference from scratch
When you need to construct a reference from external data (e.g. from a database record) rather than from a live turn, use ConversationReferenceBuilder:
import { ConversationReferenceBuilder, TeamsServiceEndpoints } from '@microsoft/agents-hosting'
const ref = ConversationReferenceBuilder
.create(process.env.CLIENT_ID!, 'msteams')
.withUser('user-aad-object-id')
.withConversationId('19:existing-thread-id@thread.tacv2')
.withServiceUrl(TeamsServiceEndpoints.publicGlobal)
.build()See the proactive-agent test agent for more examples, including a sample RESTful API for triggering proactive messages externally.
Fix: Typing indicator timer scoped per turn context (#1016)
The startTypingTimer in AgentApplication previously used a single shared timer variable at the class level, which could cause typing indicators to interfere across concurrent turns. The timer state is now stored on the TurnContext so each turn manages its own independent typing loop.
A new typing option is available on AgentApplicationOptions to configure timing behavior:
new AgentApplicationBuilder()
.withTyping({
initialDelayMs: 0, // delay before first typing indicator (default: 0ms)
intervalMs: 4000, // interval between subsequent indicators (default: 4s)
channelStrategies: {
'webchat': { intervalMs: 3000 } // per-channel overrides
}
}
})
.build()Change: Auth handler environment variable naming aligned with .NET/Python** (#872)
The environment variable format for configuring OAuth authorization handlers has been updated to match the conventions used in the .NET and Python SDKs.
| Format | Example | |
|---|---|---|
| Legacy | <handlerId>_<property> |
graph_connectionName |
| New | AgentApplication__UserAuthorization__Handlers__<handlerId>__Settings__<property> |
AgentApplication__UserAuthorization__Handlers__graph__Settings__azureBotOAuthConnectionName |
The legacy format is still supported for backward compatibility, and matching is case-insensitive for handler IDs. Legacy property names are also remapped to their new equivalents (e.g. connectionName → azureBotOAuthConnectionName, maxAttempts → invalidSignInRetryMax).
| Legacy name | New name |
|---|---|
connectionName |
azureBotOAuthConnectionName |
connectionTitle |
title |
connectionText |
text |
messages_invalidCode |
invalidSignInRetryMessage |
messages_invalidCodeFormat |
invalidSignInRetryMessageFormat |
messages_maxAttemptsExceeded |
invalidSignInRetryMaxExceededMessage |
maxAttempts |
invalidSignInRetryMax |
enableSso |
enableSso |
obo_connection |
oboConnectionName |
obo_scopes |
oboScopes |
If you are configuring auth handlers via environment variables, update your .env files to use the new format. The legacy format may be removed in a future release.
Full list of changes
- Post release for 1.4 by @benbrown in #1003
- Bump flatted from 3.3.3 to 3.4.2 by @dependabot[bot] in #1007
- Bump picomatch by @dependabot[bot] in #1011
- Bump yaml from 2.8.2 to 2.8.3 by @dependabot[bot] in #1012
- Detach SSO token exchange invoke activity from the route handler by @sw-joelmut in #1013
- bump fast-xml-parser version by @ceciliaavila in #1028
- Bump sinon from 21.0.2 to 21.0.3 by @dependabot[bot] in #1030
- Bump typedoc from 0.28.17 to 0.28.18 by @dependabot[bot] in #1031
- Bump @opentelemetry/resources from 2.6.0 to 2.6.1 by @dependabot[bot] in #1032
- Bump @types/node from 25.3.3 to 25.4.0 by @dependabot[bot] in #993
- Bump lodash version by @ceciliaavila in #1035
- Proactive improvements by @benbrown in #1008
- Bump axios from 1.13.6 to 1.15.0 by @dependabot[bot] in #1041
- AgentApplication startTypingTimer shares single-app level _typingTimer variable by @sw-joelmut in #1016
- Update test-agents to use new config style by @benbrown in #1048
- Bump dependencies by @sw-joelmut in #1051
- adapter.process() Causes Error: Cannot set headers after they are sent to the client by @sw-joelmut in #1043
- Add additional debugging + info in readme about using debugging by @benbrown in #1037
- OAuth env variable names should match .net/python by @sw-joelmut in #872
- [feat] Add agents-telemetry package that includes support for OpenTelemetry (OTel) by @sw-joelmut in #1056
- [#1029] Pin version of dependencies to handle upgrades deliberately by @ceciliaavila in #1042
- Send invoke response when there is no active auth flow by @sw-joelmut in #1052
- [#923] Add OTel instrumentation for proactive methods by @ceciliaavila in #1058
- Update OBO token flow to show specific span trace and add unit tests by @sw-joelmut in #1061
- [#923] Add OTel to agents-hosting-dialogs by @ceciliaavila in #1059
- Bump @xmldom/xmldom from 0.8.12 to 0.8.13 by @dependabot[bot] in #1055
- [#1062] Fix top-level await for ESM browser builds in agents-telemetry by @ceciliaavila in #1063
- [#1052] Fix browser build by @ceciliaavila in #1065
Full Changelog: v1.4...v1.5