@cuylabs/agent-microsoft-opentelemetry 4.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +173 -0
- package/dist/chunk-S7XVXBY3.js +966 -0
- package/dist/index.d.ts +440 -0
- package/dist/index.js +363 -0
- package/dist/loader.d.ts +2 -0
- package/dist/loader.js +3 -0
- package/dist/register.d.ts +2 -0
- package/dist/register.js +17 -0
- package/docs/architecture.md +110 -0
- package/docs/destinations-and-instrumentation.md +116 -0
- package/docs/distro-alignment.md +123 -0
- package/docs/env.md +76 -0
- package/examples/01-agent365-s2s.ts +40 -0
- package/package.json +77 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MICROSOFT_A365_ATTRIBUTES,
|
|
3
|
+
MICROSOFT_A365_FMI_SCOPE,
|
|
4
|
+
MICROSOFT_A365_OBSERVABILITY_SCOPE,
|
|
5
|
+
MicrosoftOpenTelemetryModuleLoadError,
|
|
6
|
+
MicrosoftOpenTelemetryResourceModuleLoadError,
|
|
7
|
+
buildMicrosoftA365BaggagePairs,
|
|
8
|
+
callOptionalBooleanMethod,
|
|
9
|
+
callOptionalStringMethod,
|
|
10
|
+
createEnvReader,
|
|
11
|
+
createMicrosoftA365S2STokenResolver,
|
|
12
|
+
createMicrosoftA365S2STokenResolverFromEnv,
|
|
13
|
+
createMicrosoftOpenTelemetryInstrumentationOptions,
|
|
14
|
+
createMicrosoftOpenTelemetryTracingConfig,
|
|
15
|
+
firstNonEmpty,
|
|
16
|
+
initMicrosoftOpenTelemetry,
|
|
17
|
+
initMicrosoftOpenTelemetryFromEnv,
|
|
18
|
+
loadMicrosoftOpenTelemetryModule,
|
|
19
|
+
loadOpenTelemetryResourceModule,
|
|
20
|
+
mergeInstrumentationOptions,
|
|
21
|
+
normalizeInstrumentationProfile,
|
|
22
|
+
parseServiceEndpoint,
|
|
23
|
+
readPath,
|
|
24
|
+
resolveMicrosoftOpenTelemetryBooleanEnv,
|
|
25
|
+
resolveMicrosoftOpenTelemetryEnvironment,
|
|
26
|
+
resolveMicrosoftOpenTelemetryNumberEnv,
|
|
27
|
+
runWithMicrosoftA365Context,
|
|
28
|
+
updateMicrosoftA365ExportToken
|
|
29
|
+
} from "./chunk-S7XVXBY3.js";
|
|
30
|
+
|
|
31
|
+
// src/a365/hosting.ts
|
|
32
|
+
async function configureMicrosoftA365Hosting(adapter, options = {}) {
|
|
33
|
+
const module = await loadMicrosoftOpenTelemetryModule(options.runtime);
|
|
34
|
+
const hostingOptions = resolveHostingOptions(options);
|
|
35
|
+
if (module.configureA365Hosting) {
|
|
36
|
+
return module.configureA365Hosting(adapter, hostingOptions);
|
|
37
|
+
}
|
|
38
|
+
if (module.ObservabilityHostingManager) {
|
|
39
|
+
const manager = new module.ObservabilityHostingManager();
|
|
40
|
+
manager.configure(adapter, hostingOptions);
|
|
41
|
+
return manager;
|
|
42
|
+
}
|
|
43
|
+
throw new MicrosoftOpenTelemetryModuleLoadError(
|
|
44
|
+
"The installed @microsoft/opentelemetry package does not expose A365 hosting middleware helpers."
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
function resolveHostingOptions(options) {
|
|
48
|
+
return {
|
|
49
|
+
enableBaggage: options.enableBaggage ?? true,
|
|
50
|
+
enableOutputLogging: options.enableOutputLogging ?? true
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/a365/turn-context.ts
|
|
55
|
+
function createMicrosoftA365ContextFromTurnContext(turnContext, options = {}) {
|
|
56
|
+
const { serviceUrl, useRecipientIdAsAgentId, ...requestOptions } = options;
|
|
57
|
+
const activity = turnContext.activity ?? {};
|
|
58
|
+
const channelData = activity.channelData;
|
|
59
|
+
const endpoint = parseServiceEndpoint(serviceUrl ?? activity.serviceUrl);
|
|
60
|
+
const isAgenticRequest = callOptionalBooleanMethod(activity.isAgenticRequest);
|
|
61
|
+
const agentId = firstNonEmpty(
|
|
62
|
+
requestOptions.agentId,
|
|
63
|
+
isAgenticRequest ? callOptionalStringMethod(activity.getAgenticInstanceId) : void 0,
|
|
64
|
+
activity.recipient?.agenticAppId,
|
|
65
|
+
useRecipientIdAsAgentId ? activity.recipient?.id : void 0
|
|
66
|
+
);
|
|
67
|
+
const tenantId = firstNonEmpty(
|
|
68
|
+
requestOptions.tenantId,
|
|
69
|
+
callOptionalStringMethod(activity.getAgenticTenantId),
|
|
70
|
+
activity.recipient?.tenantId,
|
|
71
|
+
activity.conversation?.tenantId,
|
|
72
|
+
readPath(channelData, ["tenant", "id"]),
|
|
73
|
+
readPath(channelData, ["tenantId"])
|
|
74
|
+
);
|
|
75
|
+
const context = { ...requestOptions };
|
|
76
|
+
setIfDefined(context, "tenantId", tenantId);
|
|
77
|
+
setIfDefined(context, "agentId", agentId);
|
|
78
|
+
setIfDefined(
|
|
79
|
+
context,
|
|
80
|
+
"agentName",
|
|
81
|
+
firstNonEmpty(requestOptions.agentName, activity.recipient?.name)
|
|
82
|
+
);
|
|
83
|
+
setIfDefined(
|
|
84
|
+
context,
|
|
85
|
+
"agentDescription",
|
|
86
|
+
firstNonEmpty(requestOptions.agentDescription, activity.recipient?.role)
|
|
87
|
+
);
|
|
88
|
+
setIfDefined(
|
|
89
|
+
context,
|
|
90
|
+
"agentAuid",
|
|
91
|
+
firstNonEmpty(requestOptions.agentAuid, activity.recipient?.aadObjectId)
|
|
92
|
+
);
|
|
93
|
+
setIfDefined(
|
|
94
|
+
context,
|
|
95
|
+
"agentBlueprintId",
|
|
96
|
+
firstNonEmpty(
|
|
97
|
+
requestOptions.agentBlueprintId,
|
|
98
|
+
activity.recipient?.agenticAppBlueprintId
|
|
99
|
+
)
|
|
100
|
+
);
|
|
101
|
+
setIfDefined(
|
|
102
|
+
context,
|
|
103
|
+
"conversationId",
|
|
104
|
+
firstNonEmpty(requestOptions.conversationId, activity.conversation?.id)
|
|
105
|
+
);
|
|
106
|
+
setIfDefined(
|
|
107
|
+
context,
|
|
108
|
+
"conversationItemLink",
|
|
109
|
+
firstNonEmpty(requestOptions.conversationItemLink, activity.serviceUrl)
|
|
110
|
+
);
|
|
111
|
+
setIfDefined(
|
|
112
|
+
context,
|
|
113
|
+
"sessionId",
|
|
114
|
+
firstNonEmpty(
|
|
115
|
+
requestOptions.sessionId,
|
|
116
|
+
requestOptions.conversationId,
|
|
117
|
+
activity.conversation?.id
|
|
118
|
+
)
|
|
119
|
+
);
|
|
120
|
+
setIfDefined(
|
|
121
|
+
context,
|
|
122
|
+
"channelName",
|
|
123
|
+
firstNonEmpty(requestOptions.channelName, activity.channelId)
|
|
124
|
+
);
|
|
125
|
+
setIfDefined(
|
|
126
|
+
context,
|
|
127
|
+
"channelLink",
|
|
128
|
+
firstNonEmpty(requestOptions.channelLink, activity.channelIdSubChannel)
|
|
129
|
+
);
|
|
130
|
+
setIfDefined(
|
|
131
|
+
context,
|
|
132
|
+
"userId",
|
|
133
|
+
firstNonEmpty(
|
|
134
|
+
requestOptions.userId,
|
|
135
|
+
activity.from?.aadObjectId,
|
|
136
|
+
activity.from?.id,
|
|
137
|
+
readPath(turnContext.identity, ["oid"]),
|
|
138
|
+
readPath(turnContext.identity, ["sub"])
|
|
139
|
+
)
|
|
140
|
+
);
|
|
141
|
+
setIfDefined(
|
|
142
|
+
context,
|
|
143
|
+
"userName",
|
|
144
|
+
firstNonEmpty(requestOptions.userName, activity.from?.name)
|
|
145
|
+
);
|
|
146
|
+
setIfDefined(
|
|
147
|
+
context,
|
|
148
|
+
"userEmail",
|
|
149
|
+
firstNonEmpty(
|
|
150
|
+
requestOptions.userEmail,
|
|
151
|
+
activity.from?.email,
|
|
152
|
+
activity.from?.userPrincipalName,
|
|
153
|
+
activity.from?.agenticUserId,
|
|
154
|
+
readPath(channelData, ["from", "userPrincipalName"]),
|
|
155
|
+
readPath(turnContext.identity, ["preferred_username"]),
|
|
156
|
+
readPath(turnContext.identity, ["upn"])
|
|
157
|
+
)
|
|
158
|
+
);
|
|
159
|
+
setIfDefined(
|
|
160
|
+
context,
|
|
161
|
+
"callerAgentBlueprintId",
|
|
162
|
+
firstNonEmpty(
|
|
163
|
+
requestOptions.callerAgentBlueprintId,
|
|
164
|
+
activity.from?.agenticAppBlueprintId
|
|
165
|
+
)
|
|
166
|
+
);
|
|
167
|
+
setIfDefined(
|
|
168
|
+
context,
|
|
169
|
+
"serverAddress",
|
|
170
|
+
firstNonEmpty(requestOptions.serverAddress, endpoint?.serverAddress)
|
|
171
|
+
);
|
|
172
|
+
setIfDefined(
|
|
173
|
+
context,
|
|
174
|
+
"serverPort",
|
|
175
|
+
requestOptions.serverPort ?? endpoint?.serverPort
|
|
176
|
+
);
|
|
177
|
+
return context;
|
|
178
|
+
}
|
|
179
|
+
function setIfDefined(context, key, value) {
|
|
180
|
+
if (value !== void 0) {
|
|
181
|
+
context[key] = value;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// src/internal/async-queue.ts
|
|
186
|
+
var AsyncQueue = class {
|
|
187
|
+
values = [];
|
|
188
|
+
waiters = [];
|
|
189
|
+
closed = false;
|
|
190
|
+
push(value) {
|
|
191
|
+
if (this.closed) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const waiter = this.waiters.shift();
|
|
195
|
+
if (waiter) {
|
|
196
|
+
waiter({ value, done: false });
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
this.values.push(value);
|
|
200
|
+
}
|
|
201
|
+
close() {
|
|
202
|
+
if (this.closed) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
this.closed = true;
|
|
206
|
+
for (const waiter of this.waiters.splice(0)) {
|
|
207
|
+
waiter({ value: void 0, done: true });
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
[Symbol.asyncIterator]() {
|
|
211
|
+
return {
|
|
212
|
+
next: () => {
|
|
213
|
+
const value = this.values.shift();
|
|
214
|
+
if (value !== void 0) {
|
|
215
|
+
return Promise.resolve({ value, done: false });
|
|
216
|
+
}
|
|
217
|
+
if (this.closed) {
|
|
218
|
+
return Promise.resolve({ value: void 0, done: true });
|
|
219
|
+
}
|
|
220
|
+
return new Promise((resolve) => {
|
|
221
|
+
this.waiters.push(resolve);
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
// src/a365/turn-source.ts
|
|
229
|
+
function createMicrosoftA365ObservedTurnSource({
|
|
230
|
+
context,
|
|
231
|
+
runtimeOptions,
|
|
232
|
+
source
|
|
233
|
+
}) {
|
|
234
|
+
const resolveContext = typeof context === "function" ? context : () => context;
|
|
235
|
+
return {
|
|
236
|
+
async *chat(sessionId, message, options) {
|
|
237
|
+
const requestContext = await resolveContext({
|
|
238
|
+
sessionId,
|
|
239
|
+
message,
|
|
240
|
+
options
|
|
241
|
+
});
|
|
242
|
+
const queue = new AsyncQueue();
|
|
243
|
+
let failure;
|
|
244
|
+
void runWithMicrosoftA365Context(
|
|
245
|
+
requestContext,
|
|
246
|
+
async () => {
|
|
247
|
+
try {
|
|
248
|
+
for await (const event of source.chat(
|
|
249
|
+
sessionId,
|
|
250
|
+
message,
|
|
251
|
+
options
|
|
252
|
+
)) {
|
|
253
|
+
queue.push(event);
|
|
254
|
+
}
|
|
255
|
+
} catch (error) {
|
|
256
|
+
failure = error;
|
|
257
|
+
} finally {
|
|
258
|
+
queue.close();
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
runtimeOptions
|
|
262
|
+
).catch((error) => {
|
|
263
|
+
failure = error;
|
|
264
|
+
queue.close();
|
|
265
|
+
});
|
|
266
|
+
for await (const event of queue) {
|
|
267
|
+
yield event;
|
|
268
|
+
}
|
|
269
|
+
if (failure) {
|
|
270
|
+
throw failure;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// src/runtime/destinations.ts
|
|
277
|
+
var TRUE_VALUES = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
|
|
278
|
+
function isMicrosoftOtlpEnvironmentEnabled(env = process.env) {
|
|
279
|
+
return Boolean(
|
|
280
|
+
readFirst(
|
|
281
|
+
env,
|
|
282
|
+
"OTEL_EXPORTER_OTLP_ENDPOINT",
|
|
283
|
+
"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT",
|
|
284
|
+
"OTEL_EXPORTER_OTLP_METRICS_ENDPOINT",
|
|
285
|
+
"OTEL_EXPORTER_OTLP_LOGS_ENDPOINT"
|
|
286
|
+
)
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
function isAzureMonitorEnvironmentEnabled(env = process.env) {
|
|
290
|
+
if (isTruthy(readFirst(env, "MICROSOFT_OTEL_AZURE_MONITOR_ENABLED"))) {
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
return Boolean(readFirst(env, "APPLICATIONINSIGHTS_CONNECTION_STRING"));
|
|
294
|
+
}
|
|
295
|
+
function createAzureMonitorOptionsFromEnv(env = process.env) {
|
|
296
|
+
const enabled = isAzureMonitorEnvironmentEnabled(env);
|
|
297
|
+
const connectionString = readFirst(
|
|
298
|
+
env,
|
|
299
|
+
"APPLICATIONINSIGHTS_CONNECTION_STRING"
|
|
300
|
+
);
|
|
301
|
+
if (!enabled && !connectionString) {
|
|
302
|
+
return void 0;
|
|
303
|
+
}
|
|
304
|
+
return {
|
|
305
|
+
enabled,
|
|
306
|
+
...connectionString ? { azureMonitorExporterOptions: { connectionString } } : {}
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
function summarizeMicrosoftOpenTelemetryDestinations(options = {}) {
|
|
310
|
+
const env = options.env ?? process.env;
|
|
311
|
+
const azureMonitor = options.azureMonitor === false ? false : options.azureMonitor?.enabled ?? isAzureMonitorEnvironmentEnabled(env);
|
|
312
|
+
const agent365 = options.a365?.enabled === true && options.a365.enableObservabilityExporter === true;
|
|
313
|
+
const otlp = isMicrosoftOtlpEnvironmentEnabled(env);
|
|
314
|
+
return {
|
|
315
|
+
agent365,
|
|
316
|
+
azureMonitor,
|
|
317
|
+
otlp,
|
|
318
|
+
console: options.enableConsoleExporters ?? (!agent365 && !azureMonitor && !otlp)
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
function readFirst(env, ...names) {
|
|
322
|
+
for (const name of names) {
|
|
323
|
+
const value = env[name]?.trim();
|
|
324
|
+
if (value) {
|
|
325
|
+
return value;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return void 0;
|
|
329
|
+
}
|
|
330
|
+
function isTruthy(value) {
|
|
331
|
+
return TRUE_VALUES.has((value ?? "").trim().toLowerCase());
|
|
332
|
+
}
|
|
333
|
+
export {
|
|
334
|
+
MICROSOFT_A365_ATTRIBUTES,
|
|
335
|
+
MICROSOFT_A365_FMI_SCOPE,
|
|
336
|
+
MICROSOFT_A365_OBSERVABILITY_SCOPE,
|
|
337
|
+
MicrosoftOpenTelemetryModuleLoadError,
|
|
338
|
+
MicrosoftOpenTelemetryResourceModuleLoadError,
|
|
339
|
+
buildMicrosoftA365BaggagePairs,
|
|
340
|
+
configureMicrosoftA365Hosting,
|
|
341
|
+
createAzureMonitorOptionsFromEnv,
|
|
342
|
+
createEnvReader,
|
|
343
|
+
createMicrosoftA365ContextFromTurnContext,
|
|
344
|
+
createMicrosoftA365ObservedTurnSource,
|
|
345
|
+
createMicrosoftA365S2STokenResolver,
|
|
346
|
+
createMicrosoftA365S2STokenResolverFromEnv,
|
|
347
|
+
createMicrosoftOpenTelemetryInstrumentationOptions,
|
|
348
|
+
createMicrosoftOpenTelemetryTracingConfig,
|
|
349
|
+
initMicrosoftOpenTelemetry,
|
|
350
|
+
initMicrosoftOpenTelemetryFromEnv,
|
|
351
|
+
isAzureMonitorEnvironmentEnabled,
|
|
352
|
+
isMicrosoftOtlpEnvironmentEnabled,
|
|
353
|
+
loadMicrosoftOpenTelemetryModule,
|
|
354
|
+
loadOpenTelemetryResourceModule,
|
|
355
|
+
mergeInstrumentationOptions,
|
|
356
|
+
normalizeInstrumentationProfile,
|
|
357
|
+
resolveMicrosoftOpenTelemetryBooleanEnv,
|
|
358
|
+
resolveMicrosoftOpenTelemetryEnvironment,
|
|
359
|
+
resolveMicrosoftOpenTelemetryNumberEnv,
|
|
360
|
+
runWithMicrosoftA365Context,
|
|
361
|
+
summarizeMicrosoftOpenTelemetryDestinations,
|
|
362
|
+
updateMicrosoftA365ExportToken
|
|
363
|
+
};
|
package/dist/loader.d.ts
ADDED
package/dist/loader.js
ADDED
package/dist/register.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
initMicrosoftOpenTelemetryFromEnv
|
|
3
|
+
} from "./chunk-S7XVXBY3.js";
|
|
4
|
+
|
|
5
|
+
// src/register.ts
|
|
6
|
+
var DISABLED_VALUES = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
|
|
7
|
+
var GLOBAL_HANDLE_KEY = /* @__PURE__ */ Symbol.for(
|
|
8
|
+
"@cuylabs/agent-microsoft-opentelemetry/register"
|
|
9
|
+
);
|
|
10
|
+
var disabled = DISABLED_VALUES.has(
|
|
11
|
+
(process.env.CUYLABS_MICROSOFT_OPENTELEMETRY_DISABLED ?? "").toLowerCase()
|
|
12
|
+
);
|
|
13
|
+
if (!disabled) {
|
|
14
|
+
const globalState = globalThis;
|
|
15
|
+
globalState[GLOBAL_HANDLE_KEY] ??= initMicrosoftOpenTelemetryFromEnv();
|
|
16
|
+
await globalState[GLOBAL_HANDLE_KEY];
|
|
17
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
`@cuylabs/agent-microsoft-opentelemetry` is the Microsoft OpenTelemetry distro
|
|
4
|
+
adapter for `agents-ts`.
|
|
5
|
+
|
|
6
|
+
## Layers
|
|
7
|
+
|
|
8
|
+
The package keeps three boundaries separate.
|
|
9
|
+
|
|
10
|
+
First, `@cuylabs/agent-core` remains the owner of agent execution telemetry. It
|
|
11
|
+
configures AI SDK v7 GenAI telemetry for model calls and tool execution and
|
|
12
|
+
provides `createAgent({ tracing })` as the app-facing integration point.
|
|
13
|
+
|
|
14
|
+
Second, `@microsoft/opentelemetry` owns the Microsoft OpenTelemetry provider,
|
|
15
|
+
span processors, exporters, Azure Monitor integration, Agent 365 exporter, OTLP
|
|
16
|
+
export, and Microsoft-supported framework instrumentation.
|
|
17
|
+
|
|
18
|
+
Third, this package bridges the two. It starts the Microsoft distro, creates
|
|
19
|
+
Agent 365 S2S token resolvers, applies Agent 365 baggage around turns, and
|
|
20
|
+
returns `agent-core` tracing defaults that align with Microsoft Agent 365
|
|
21
|
+
schemas.
|
|
22
|
+
|
|
23
|
+
The adapter default instrumentation profile is `agent-core`. It disables
|
|
24
|
+
Microsoft's optional framework auto-instrumentations for OpenAI Agents SDK and
|
|
25
|
+
LangChain because those are not part of the normal `agents-ts` execution path.
|
|
26
|
+
Applications can opt into those official Microsoft framework integrations with
|
|
27
|
+
`microsoft-genai` or explicit `instrumentationOptions`.
|
|
28
|
+
|
|
29
|
+
## Source Layout
|
|
30
|
+
|
|
31
|
+
The Microsoft distro source is organized by owned implementation subsystems:
|
|
32
|
+
A365 processors/exporters/scopes, Azure Monitor, OTLP, GenAI instrumentation,
|
|
33
|
+
and shared distro setup. This package does not copy those internals. It keeps
|
|
34
|
+
only the adapter responsibilities:
|
|
35
|
+
|
|
36
|
+
| Folder | Responsibility |
|
|
37
|
+
| --- | --- |
|
|
38
|
+
| `src/a365` | Normalize channel/runtime data into Microsoft A365 baggage. |
|
|
39
|
+
| `src/auth` | Resolve Agent 365 S2S tokens from CLI-generated configuration or host-provided managed identity assertions. |
|
|
40
|
+
| `src/runtime` | Call `useMicrosoftOpenTelemetry()`, map adapter options to distro options, expose destination helpers, expose instrumentation profiles, and load Microsoft modules lazily. |
|
|
41
|
+
| `src/tracing` | Produce `agent-core` tracing config that avoids duplicate tool spans and carries Microsoft attributes. |
|
|
42
|
+
| `src/common` | Public cross-cutting type aliases. |
|
|
43
|
+
| `src/internal` | Small helpers that are not part of the package API. |
|
|
44
|
+
|
|
45
|
+
## Agent 365 Flow
|
|
46
|
+
|
|
47
|
+
An application starts the Microsoft distro with `initMicrosoftOpenTelemetry()`.
|
|
48
|
+
For ESM services that rely on automatic instrumentation, start the process with
|
|
49
|
+
`--import @cuylabs/agent-microsoft-opentelemetry/loader`. That loader imports
|
|
50
|
+
Microsoft's OpenTelemetry hook first and then runs this package's env-driven
|
|
51
|
+
registration. This follows the distro's requirement that instrumentation hooks
|
|
52
|
+
are registered before app modules load.
|
|
53
|
+
|
|
54
|
+
When `a365.enabled` is true, the distro registers the Agent 365 span processor.
|
|
55
|
+
When `a365.enableObservabilityExporter` is true, it also registers the Agent
|
|
56
|
+
365 HTTP exporter. The adapter still accepts `exporterEnabled` as a
|
|
57
|
+
compatibility alias, but new code should use Microsoft's option name.
|
|
58
|
+
|
|
59
|
+
Each channel turn should run inside `runWithMicrosoftA365Context()` or an
|
|
60
|
+
observed turn source. That scope creates Microsoft baggage values such as
|
|
61
|
+
`microsoft.tenant.id`, `gen_ai.agent.id`, `user.email`,
|
|
62
|
+
`microsoft.channel.name`, and `gen_ai.conversation.id`. The Microsoft distro
|
|
63
|
+
copies those baggage values onto spans before export.
|
|
64
|
+
|
|
65
|
+
For Microsoft Agent Hosting compatible adapters, the package can also delegate
|
|
66
|
+
to the official distro's hosting middleware with
|
|
67
|
+
`configureMicrosoftA365Hosting()`. This keeps Microsoft TurnContext middleware
|
|
68
|
+
on Microsoft's implementation while still exposing one `agents-ts` integration
|
|
69
|
+
surface.
|
|
70
|
+
|
|
71
|
+
The Agent 365 exporter groups spans by tenant and agent identity, then calls the
|
|
72
|
+
token resolver. For S2S agents, `createMicrosoftA365S2STokenResolver()` performs
|
|
73
|
+
the Agent 365 FMI flow and caches the resulting Observability API token.
|
|
74
|
+
|
|
75
|
+
## Tool Spans
|
|
76
|
+
|
|
77
|
+
The default tracing helper returns `emitToolSpans: false`. This is intentional.
|
|
78
|
+
AI SDK v7 GenAI telemetry already emits standard tool spans for normal
|
|
79
|
+
`streamText()` tool execution. Enabling both AI SDK tool spans and agent-core
|
|
80
|
+
custom tool spans creates duplicate `execute_tool` telemetry for one tool call.
|
|
81
|
+
|
|
82
|
+
If an application has a custom execution path that does not emit AI SDK tool
|
|
83
|
+
spans, it can opt in with:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
createMicrosoftOpenTelemetryTracingConfig({
|
|
87
|
+
emitToolSpans: true,
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Content Capture
|
|
92
|
+
|
|
93
|
+
The package does not force a privacy policy. `recordInputs` and `recordOutputs`
|
|
94
|
+
are explicit `agent-core` tracing settings. Set them to `true` when Agent 365
|
|
95
|
+
validation or audit scenarios require prompt, response, tool argument, and tool
|
|
96
|
+
result attributes. Set them to `false` when an application needs metadata-only
|
|
97
|
+
telemetry.
|
|
98
|
+
|
|
99
|
+
## Distro Migration
|
|
100
|
+
|
|
101
|
+
The older `@cuylabs/agent-a365-observability` package wraps
|
|
102
|
+
`@microsoft/agents-a365-observability`. This package wraps the newer
|
|
103
|
+
`@microsoft/opentelemetry` distro. New Microsoft integrations should prefer this
|
|
104
|
+
package unless they need to remain on the older Microsoft package family for
|
|
105
|
+
compatibility.
|
|
106
|
+
|
|
107
|
+
For an option-by-option mapping to the Microsoft distro, see
|
|
108
|
+
[distro-alignment.md](./distro-alignment.md).
|
|
109
|
+
For destination and instrumentation strategy, see
|
|
110
|
+
[destinations-and-instrumentation.md](./destinations-and-instrumentation.md).
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Destinations and Instrumentation
|
|
2
|
+
|
|
3
|
+
The Microsoft OpenTelemetry distro separates where telemetry is sent from how
|
|
4
|
+
telemetry is created.
|
|
5
|
+
|
|
6
|
+
## Destinations
|
|
7
|
+
|
|
8
|
+
Destinations are exporters attached to the same OpenTelemetry pipeline.
|
|
9
|
+
|
|
10
|
+
| Destination | Purpose | How to enable |
|
|
11
|
+
| --- | --- | --- |
|
|
12
|
+
| Agent 365 | Microsoft 365 admin, Defender, and Purview agent activity. | `a365.enabled: true` plus `a365.enableObservabilityExporter: true`. |
|
|
13
|
+
| Azure Monitor | Application operations, traces, dependencies, logs, metrics, dashboards, and alerts. | `azureMonitor: { enabled: true }` or `APPLICATIONINSIGHTS_CONNECTION_STRING`. |
|
|
14
|
+
| OTLP | Vendor-neutral export to collectors or backends such as Grafana, Datadog, or New Relic. | Standard `OTEL_EXPORTER_OTLP_*` environment variables. |
|
|
15
|
+
| Console | Local validation when no remote exporter is active, or explicit debugging. | `enableConsoleExporters: true`. |
|
|
16
|
+
|
|
17
|
+
The same spans can flow to multiple destinations. Agent 365 enrichment still
|
|
18
|
+
helps Azure Monitor and OTLP when `a365.enabled` is true because the Microsoft
|
|
19
|
+
distro's `A365SpanProcessor` copies baggage onto spans before export.
|
|
20
|
+
|
|
21
|
+
## Instrumentation
|
|
22
|
+
|
|
23
|
+
Instrumentation controls which libraries create spans.
|
|
24
|
+
|
|
25
|
+
| Instrumentation | Purpose |
|
|
26
|
+
| --- | --- |
|
|
27
|
+
| `http`, `azureSdk`, databases, Redis, Bunyan, Winston | Infrastructure and application telemetry. |
|
|
28
|
+
| `openaiAgents` | Optional Microsoft auto-instrumentation for the OpenAI Agents SDK when `@openai/agents` is installed. |
|
|
29
|
+
| `langchain` | Optional Microsoft auto-instrumentation for LangChain when `@langchain/core` is installed. |
|
|
30
|
+
| `agent-core` tracing | Emits `agents-ts` agent invocation spans and passes AI SDK v7 GenAI telemetry into `streamText()`. |
|
|
31
|
+
|
|
32
|
+
`@cuylabs/agent-core` does not use OpenAI Agents SDK or LangChain internally.
|
|
33
|
+
Those Microsoft framework instrumentations are useful only when an application
|
|
34
|
+
also runs those frameworks in the same process.
|
|
35
|
+
|
|
36
|
+
## Profiles
|
|
37
|
+
|
|
38
|
+
Use `instrumentationProfile` to make the intent explicit.
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
await initMicrosoftOpenTelemetry({
|
|
42
|
+
instrumentationProfile: "agent-core",
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
| Profile | Behavior |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| `agent-core` | Disables Microsoft infra and framework auto-instrumentation. Best default for `agents-ts`, where `agent-core` emits the agent/model/tool telemetry. |
|
|
49
|
+
| `microsoft-genai` | Disables infrastructure auto-instrumentation and enables Microsoft's OpenAI Agents SDK and LangChain auto-instrumentations. |
|
|
50
|
+
| `full-stack` | Enables infrastructure and Microsoft framework auto-instrumentation. Best when sending to Azure Monitor or a general OTLP backend too. |
|
|
51
|
+
| `manual` | Disables built-in auto-instrumentation. Best when the app supplies its own processors/instrumentation. |
|
|
52
|
+
| `microsoft-default` | Leaves `instrumentationOptions` unset so the official distro applies its native defaults. |
|
|
53
|
+
|
|
54
|
+
Explicit `instrumentationOptions` always override the selected profile.
|
|
55
|
+
The old pre-release name `agent365-genai` is still accepted as an alias for
|
|
56
|
+
`microsoft-genai`, but new code should not use it.
|
|
57
|
+
|
|
58
|
+
## Content Recording
|
|
59
|
+
|
|
60
|
+
There are two content-recording surfaces only if the application uses both
|
|
61
|
+
`agent-core` and Microsoft's optional framework auto-instrumentations.
|
|
62
|
+
|
|
63
|
+
`agent-core` uses `agent.recordInputs` and `agent.recordOutputs` in this
|
|
64
|
+
adapter's tracing config. Those settings control AI SDK v7 GenAI telemetry
|
|
65
|
+
created by `@cuylabs/agent-core`.
|
|
66
|
+
|
|
67
|
+
Microsoft's optional OpenAI Agents and LangChain auto-instrumentations use their
|
|
68
|
+
own official flags:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
await initMicrosoftOpenTelemetry({
|
|
72
|
+
instrumentationOptions: {
|
|
73
|
+
openaiAgents: {
|
|
74
|
+
enabled: true,
|
|
75
|
+
isContentRecordingEnabled: true,
|
|
76
|
+
},
|
|
77
|
+
langchain: {
|
|
78
|
+
enabled: true,
|
|
79
|
+
isContentRecordingEnabled: true,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Set these only when the process actually uses those frameworks. They do not
|
|
86
|
+
control `agent-core`'s AI SDK spans.
|
|
87
|
+
|
|
88
|
+
## Example: Agent 365 and Azure Monitor
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
const observability = await initMicrosoftOpenTelemetry({
|
|
92
|
+
resource: {
|
|
93
|
+
serviceName: "cdo-ai-companion",
|
|
94
|
+
serviceVersion: "1.0.0",
|
|
95
|
+
},
|
|
96
|
+
a365: {
|
|
97
|
+
enabled: true,
|
|
98
|
+
enableObservabilityExporter: true,
|
|
99
|
+
useS2SEndpoint: true,
|
|
100
|
+
tokenResolver: createMicrosoftA365S2STokenResolverFromEnv(),
|
|
101
|
+
},
|
|
102
|
+
azureMonitor: {
|
|
103
|
+
enabled: Boolean(process.env.APPLICATIONINSIGHTS_CONNECTION_STRING),
|
|
104
|
+
},
|
|
105
|
+
instrumentationProfile: "full-stack",
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
This sends the same enriched spans to Agent 365 and Azure Monitor. Set standard
|
|
110
|
+
`OTEL_EXPORTER_OTLP_ENDPOINT` variables to add OTLP export without changing code.
|
|
111
|
+
|
|
112
|
+
Azure Monitor options are passed through to the Microsoft distro. Use official
|
|
113
|
+
properties such as `enableLiveMetrics`, `enableStandardMetrics`,
|
|
114
|
+
`enableTraceBasedSamplingForLogs`, `enablePerformanceCounters`, and
|
|
115
|
+
`browserSdkLoaderOptions` when the application needs those Azure Monitor
|
|
116
|
+
features.
|