npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cuylabs/agent-a365-observability

v7.4.0

Published

Microsoft Agent 365 observability adapter for @cuylabs/agent-core

Readme

@cuylabs/agent-a365-observability

Microsoft Agent 365 observability adapter for @cuylabs/agent-core.

This package connects agent-core's portable OpenTelemetry spans to Microsoft's Agent 365 observability SDK without putting Microsoft SDK types in agent-core.

It does four things:

  • starts Microsoft Agent 365 Observability;
  • wires the Agent 365 exporter token resolver;
  • returns a small createAgent({ tracing }) config fragment for stable agent metadata;
  • wraps each request in Agent 365 baggage so spans include tenant, agent, conversation, channel, and caller identity.

For the deeper design, read docs/README.md. For the full SDK and auth flow, read docs/sdk-and-auth-flow.md.

Install

pnpm add @cuylabs/agent-a365-observability @microsoft/agents-a365-observability @microsoft/agents-a365-runtime

Install @microsoft/agents-a365-observability-hosting as well when using the M365/OBO token-cache helpers.

Dependency roles:

| Package | Required when | Notes | | ---------------------------------------------- | ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | @microsoft/agents-a365-observability | Always, when exporting to Agent 365 | Owns Microsoft's ObservabilityManager, exporter, baggage processor, trace propagation utilities, and optional OutputScope. | | @microsoft/agents-a365-runtime | Always with Microsoft's observability SDK | Provides Microsoft runtime configuration types used by the SDK. | | @microsoft/agents-a365-observability-hosting | Only for M365/OBO token-cache helpers | Provides AgenticTokenCache. It is an optional peer and is lazy-loaded only by OBO helpers. S2S App Service agents do not need it. |

Usage

import { createAgent } from "@cuylabs/agent-core";
import {
  createA365ObservedTurnSource,
  createA365S2STokenResolverFromEnv,
  createA365TracingConfig,
  initA365S2SObservability,
  initA365Observability,
  runWithA365Context,
  runWithA365OutputMessages,
  runWithA365TurnContext,
} from "@cuylabs/agent-a365-observability";

const observability = await initA365Observability({
  serviceName: "email-agent-service",
  serviceVersion: "1.0.0",
  configuration: {
    exporterEnabled: true,
    logLevel: "info",
  },
  tokenResolver: async (agentId, tenantId) => {
    return getObservabilityToken(agentId, tenantId);
  },
});

const agent = createAgent({
  name: "email-assistant",
  model,
  tracing: createA365TracingConfig({
    agentId: "agent-456",
    agentDescription: "Organizes email and calendar work",
    agentVersion: "1.0.0",
  }),
});

await runWithA365Context(
  {
    tenantId: "tenant-123",
    agentId: "agent-456",
    conversationId: "conversation-789",
    sessionId: "session-789",
    channelName: "msteams",
  },
  async () => {
    for await (const event of agent.chat("session-789", "Find urgent email")) {
      // stream events to your channel
    }
  },
);

await agent.close();
await observability.shutdown();

When you are adapting a custom channel around AgentTurnSource, use createA365ObservedTurnSource(...) instead of hand-rolling async-generator wrapping. The helper keeps the full event stream inside the Agent 365 baggage scope while letting the channel own its own metadata mapping:

const observedSource = createA365ObservedTurnSource({
  source: agent,
  context: ({ sessionId }) => ({
    tenantId: "tenant-123",
    agentId: "agent-456",
    conversationId: sessionId,
    sessionId,
    channelName: "slack",
    userId: "slack-user-id",
  }),
});

M365 / Teams bot turns

When your bot uses @microsoft/agents-hosting TurnContext, use the TurnContext helper so tenant, agent, conversation, channel, and user identity are derived from the incoming activity:

await runWithA365TurnContext(
  context, // TurnContext from CloudAdapter
  {
    agentId: "agent-456",
    agentDescription: "Organizes email and calendar work",
    agentVersion: "1.0.0",
    sessionId,
  },
  async () => {
    for await (const event of agent.chat(sessionId, context.activity.text)) {
      // stream events to the channel
    }
  },
);

If you use @cuylabs/channel-m365-agent-core, this wrapper is built into the channel adapter:

const m365 = createM365ChannelAdapter({
  agent,
  a365Observability: {
    agentId: "agent-456",
    agentDescription: "Organizes email and calendar work",
    agentVersion: "1.0.0",
  },
});

The host process still needs to call initA365Observability(...) once during startup so the Microsoft exporter and token resolver are registered.

Shape

This package is the @cuylabs/agent-core equivalent of a framework-specific Agent 365 observability extension. Microsoft's OpenAI Agents and LangChain extension packages patch those harnesses directly so their model/framework spans flow through the Agent 365 ObservabilityManager. This package does the same kind of adapter work at the agent-core layer: it starts the Microsoft ObservabilityManager, feeds agent-core tracing metadata, and wraps each agent-core turn with Agent 365 baggage.

The integration has four separate pieces:

  1. initA365Observability(...) starts Microsoft's exporter and baggage span processor once at host startup.
  2. createA365TracingConfig(...) feeds agent-core's createAgent({ tracing }) contract.
  3. runWithA365Context(...) or runWithA365TurnContext(...) binds per-request Agent 365 baggage before agent.chat() runs.
  4. createA365ObservedTurnSource(...) wraps a channel-neutral AgentTurnSource when a host needs streaming-safe A365 baggage around every emitted event.
  5. createA365S2STokenResolver(...) provides a reusable service-to-service token resolver for Agent 365 Observability API export.

The source follows those same boundaries: auth/ owns Agent 365 S2S token resolution, context/ owns baggage and TurnContext mapping, runtime/ owns Microsoft SDK startup and lazy loading, and tracing/ owns the agent-core tracing config adapter.

Use agent-core and AI SDK v7 for portable OpenTelemetry spans:

  • invoke_agent agent spans
  • AI SDK chat model spans nested under the agent turn
  • AI SDK execute_tool tool spans when tools run

Use this package for Agent 365-specific context on those spans:

  • microsoft.tenant.id
  • gen_ai.agent.id
  • microsoft.session.id
  • gen_ai.conversation.id
  • channel and caller attributes
  • A2A caller-agent attributes such as microsoft.a365.caller.agent.id

For multi-tenant agents, prefer runWithA365Context() over static tracing attributes. Static attributes are useful for stable agent metadata; baggage is the right place for per-request tenant and conversation identity.

Use extraBaggage only for additional dimensions that are not part of the standard Agent 365 identity set. Structured fields such as tenantId, agentId, conversationId, and userId win over conflicting extraBaggage keys.

Microsoft's baggage processor does not overwrite span attributes that agent-core already set. In normal @cuylabs/channel-m365-agent-core usage this is fine because the channel's default session strategy uses the M365 conversation.id as the agent-core session ID. If you use custom M365 session mapping, gen_ai.conversation.id reflects the custom agent-core session ID. See docs/agent-core-otel.md for details.

Phoenix vs Agent 365

Phoenix is a normal OTLP destination. You pass a span processor/exporter into agent-core tracing, and agent-core owns the tracer provider lifecycle for that example.

Agent 365 is different. The Microsoft SDK owns the exporter and adds an Agent 365 span processor that copies baggage into span attributes. agent-core still emits the agent and AI SDK spans, while this package starts the Microsoft exporter and wraps each request with the tenant, agent, conversation, channel, and caller baggage that Agent 365 expects.

AI SDK Telemetry

agent-core uses AI SDK v7 telemetry and enables @ai-sdk/otel's GenAIOpenTelemetry integration by default. That gives you current GenAI-shaped model spans such as invoke_agent, agent_step, chat, and execute_tool, with attributes like gen_ai.operation.name, gen_ai.provider.name, model IDs, and token usage.

This package does not replace that model telemetry. It adds the Microsoft Agent 365 layer: exporter startup, token resolution, and baggage that flows microsoft.tenant.id, gen_ai.agent.id, conversation, channel, and caller identity onto the spans.

createA365TracingConfig() defaults emitToolSpans to false. AI SDK v7 already emits execute_tool spans with the Agent 365 GenAI tool keys, including gen_ai.tool.name, gen_ai.tool.call.id, gen_ai.tool.call.arguments, and gen_ai.tool.call.result when content recording is enabled. Disabling agent-core's extra tool-span layer avoids duplicate execute_tool spans for one tool call while preserving model and tool observability from the AI SDK integration.

If you need to plug in another AI SDK telemetry integration, pass it through createA365TracingConfig():

const agent = createAgent({
  name: "email-assistant",
  model,
  tracing: createA365TracingConfig({
    agentId: "agent-456",
    telemetryIntegrations: [myIntegration],
  }),
});

Set useGenAIOpenTelemetry: false only when you intentionally want to use global AI SDK telemetry integrations or another model-span integration.

Set emitToolSpans: true only when your agent execution path does not use AI SDK v7 tool telemetry and you need agent-core to emit tool spans itself.

TurnContext Mapping

runWithA365TurnContext(...) follows Microsoft's agents-a365-observability-hosting helper behavior for M365 Activity fields. Some mappings look surprising if read as generic Bot Framework fields:

  • activity.serviceUrl maps to microsoft.conversation.item.link.
  • activity.recipient.role maps to gen_ai.agent.description.
  • activity.from.agenticUserId maps to user.email.
  • activity.channelIdSubChannel maps to microsoft.channel.link.

Pass explicit runWithA365TurnContext options when your host has more precise values, such as a real Teams message deep link for conversationItemLink. Caller-agent attributes and host-owned fields such as agentEmail, agentPlatformId, and callerClientIp are not inferred from TurnContext; pass them explicitly through runWithA365Context() or the TurnContext options when you need those dimensions.

Exporter Modes

For batch export, provide tokenResolver in initA365Observability().

For non-OBO service-to-service export, use initA365S2SObservability() when the host should use the Agent 365 CLI generated environment values. The helper understands both A365_OBSERVABILITY_* names and generated agent365Observability__* names such as agent365Observability__tenantId, agent365Observability__agentId, agent365Observability__clientId, and agent365Observability__clientSecret.

await initA365S2SObservability({
  serviceName: "email-agent-service",
  serviceVersion: "1.0.0",
});

The S2S initializer creates the token resolver, enables the Microsoft S2S exporter endpoint, and enables the exporter unless configuration overrides it.

Use createA365S2STokenResolverFromEnv() when you want the resolver only:

const tokenResolver = createA365S2STokenResolverFromEnv();

await initA365Observability({
  serviceName: "email-agent-service",
  tokenResolver,
  exporterOptions: { useS2SEndpoint: true },
  configuration: { exporterEnabled: true },
});

Use createA365S2STokenResolver() when you want to pass values explicitly:

import {
  createA365S2STokenResolver,
  initA365Observability,
} from "@cuylabs/agent-a365-observability";

const tokenResolver = createA365S2STokenResolver({
  tenantId: process.env.A365_OBSERVABILITY_TENANT_ID!,
  agentId: process.env.A365_OBSERVABILITY_AGENT_ID!,
  blueprintClientId: process.env.A365_OBSERVABILITY_CLIENT_ID!,
  blueprintClientSecret: process.env.A365_OBSERVABILITY_CLIENT_SECRET,
  useManagedIdentity:
    process.env.A365_OBSERVABILITY_USE_MANAGED_IDENTITY === "true",
});

await initA365Observability({
  serviceName: "email-agent-service",
  tokenResolver,
  exporterOptions: { useS2SEndpoint: true },
  configuration: { exporterEnabled: true },
});

Hosts that want Azure managed identity without taking an Azure dependency in this package can pass managedIdentityAssertionProvider; the provider should return an assertion token for api://AzureADTokenExchange.

For per-request export, pass exportToken to runWithA365Context() and enable per-request export in startup configuration:

await initA365Observability({
  serviceName: "email-agent-service",
  tokenResolver,
  configuration: {
    exporterEnabled: true,
    perRequest: {
      enabled: true,
      maxTraces: 1000,
      maxSpansPerTrace: 5000,
      maxConcurrentExports: 20,
      flushGraceMs: 250,
      maxTraceAgeMs: 30 * 60 * 1000,
    },
  },
});

Microsoft's current per-request processor reads its tuning options from environment-backed configuration. This package applies these explicit startup values to the Microsoft environment variables before the SDK starts.

Trace Propagation

Use the trace propagation helpers when one agent calls another over HTTP or when async work needs to keep a parent span:

const headers = await injectA365TraceContextToHeaders({});
await fetch(agentUrl, { method: "POST", headers });

await runWithA365ExtractedTraceContext(req.headers, () =>
  runWithA365Context(context, () => agent.chat(sessionId, text)),
);

For queue or callback flows where you persisted the parent span ids, use runWithA365ParentSpanRef(...).

Optional Output Spans

agent-core records output messages on the agent turn span. If an Agent 365 deployment needs Microsoft's separate output_messages span shape, wrap the outgoing delivery path:

await runWithA365OutputMessages(
  {
    tenantId,
    agentId,
    conversationId,
    channelName: "msteams",
    messages: ["Done."],
  },
  () => sendMessage("Done."),
);

This is opt-in because outgoing message content is recorded as telemetry.

OBO Token Cache

For M365/OBO hosts, use the OBO helpers around Microsoft's AgenticTokenCache. This is separate from the S2S setup flow.

This path requires the optional @microsoft/agents-a365-observability-hosting peer because AgenticTokenCache lives in Microsoft's hosting package. The adapter does not load that package for S2S startup, S2S token resolution, baggage wrapping, trace propagation, or output spans. It is loaded only when an OBO helper is called.

await refreshA365OboObservabilityToken({
  agentId,
  tenantId,
  turnContext,
  authorization,
  scopes: ["api://9b975845-388f-4429-889e-eab1ef63949c/.default"],
});

await initA365Observability({
  serviceName: "email-agent-service",
  tokenResolver: createA365OboTokenResolver(),
  configuration: { exporterEnabled: true },
});

Optional Features

The adapter now exposes the official additive pieces that do not duplicate agent-core telemetry:

  • S2S Observability token resolution, including Agent 365 CLI env aliases;
  • Microsoft trace context propagation helpers;
  • per-request export token and processor configuration;
  • optional Microsoft OutputScope output-message spans;
  • OBO token cache helpers for M365 per-user authorization.

Real-time threat protection and chat-history submission are outside observability and should live in separate Agent 365 tooling or security packages.

Examples

Examples live in the repository under packages/agent-a365-observability/examples. They are monorepo-local reference files and are not shipped in the npm tarball.