@cuylabs/agent-foundry-agentserver-core 4.8.1 → 4.9.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/README.md CHANGED
@@ -4,8 +4,10 @@ Shared host utilities for TypeScript Foundry Agent Server protocol packages.
4
4
 
5
5
  This package mirrors the protocol-agnostic parts of Python
6
6
  `azure-ai-agentserver-core`: configuration resolution, request/session ID
7
- sanitization, request correlation IDs, server-version header formatting, and
8
- standard error envelopes. Protocol packages such as
7
+ sanitization, request correlation IDs, server-version header formatting,
8
+ standard error envelopes, common Express host middleware, readiness/liveness
9
+ handlers, request logging, graceful shutdown helpers, and startup configuration
10
+ logging. Protocol packages such as
9
11
  `@cuylabs/agent-foundry-agentserver-invocations` build their HTTP surface on top
10
12
  of these helpers.
11
13
 
@@ -18,6 +20,10 @@ sets the Foundry baggage keys used by the Python host:
18
20
  ```text
19
21
  azure.ai.agentserver.invocation_id
20
22
  azure.ai.agentserver.session_id
23
+ azure.ai.agentserver.response_id
24
+ azure.ai.agentserver.conversation_id
25
+ azure.ai.agentserver.streaming
26
+ azure.ai.agentserver.x-request-id
21
27
  ```
22
28
 
23
29
  Exporter setup is optional and is driven by the same environment variables used
@@ -31,3 +37,17 @@ by hosted agents:
31
37
  Protocol packages call `configureAgentServerObservability()` by default. Apps
32
38
  that already own OpenTelemetry setup can disable package-managed exporter setup
33
39
  at the protocol layer and keep the span/baggage helpers.
40
+
41
+ ## Python Comparison
42
+
43
+ Compare this package to Python `azure-ai-agentserver-core`, not to a protocol
44
+ package. Python exposes an `AgentServerHost` built on ASGI, Starlette, and
45
+ Hypercorn. This TypeScript package exposes the equivalent protocol-agnostic
46
+ host pieces for Express and Node HTTP, then protocol packages compose those
47
+ pieces into their own server entry points.
48
+
49
+ `@cuylabs/agent-foundry-agentserver-responses` depends on this package in the
50
+ same way Python `azure-ai-agentserver-responses` depends on
51
+ `azure-ai-agentserver-core`. The local `@cuylabs/agent-foundry-hosting`
52
+ package is different: it is agents-ts runtime glue, not a direct Python SDK
53
+ peer.
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { Server } from 'node:http';
2
+ import { Request, Response, NextFunction, Express } from 'express';
1
3
  import { Span, Context } from '@opentelemetry/api';
2
4
 
3
5
  interface AgentServerConfig {
@@ -36,11 +38,14 @@ interface AgentServerErrorBody {
36
38
  message: string;
37
39
  type?: string;
38
40
  details?: Array<Record<string, unknown>>;
41
+ additionalInfo?: Record<string, unknown>;
39
42
  };
40
43
  }
41
44
  declare function createErrorBody(code: string, message: string, options?: {
42
45
  type?: string;
43
46
  details?: Array<Record<string, unknown>>;
47
+ additionalInfo?: Record<string, unknown>;
48
+ requestId?: string;
44
49
  }): AgentServerErrorBody;
45
50
 
46
51
  declare const REQUEST_ID_HEADER: "x-request-id";
@@ -48,6 +53,7 @@ declare const INVOCATION_ID_HEADER: "x-agent-invocation-id";
48
53
  declare const SESSION_ID_HEADER: "x-agent-session-id";
49
54
  declare const USER_ISOLATION_HEADER: "x-agent-user-isolation-key";
50
55
  declare const CHAT_ISOLATION_HEADER: "x-agent-chat-isolation-key";
56
+ declare const CLIENT_HEADER_PREFIX: "x-client-";
51
57
  declare const SESSION_ID_HEADERS: readonly ["x-agent-session-id", "x-foundry-agent-session-id", "x-ms-agent-session-id"];
52
58
  declare const INVOCATION_ID_HEADERS: readonly ["x-agent-invocation-id", "x-foundry-invocation-id", "x-ms-invocation-id", "x-invocation-id"];
53
59
  type HeaderGetter = (name: string) => string | undefined;
@@ -66,10 +72,47 @@ interface InvocationIdentity {
66
72
  sessionId: string;
67
73
  invocationId: string;
68
74
  }
75
+ interface AgentIsolationContext {
76
+ userKey?: string;
77
+ chatKey?: string;
78
+ }
69
79
  declare function resolveInvocationIdentity(input: InvocationIdentityInput): InvocationIdentity;
70
80
  declare function resolveRequestId(header: HeaderGetter): string;
81
+ declare function resolveAgentIsolation(header: HeaderGetter): AgentIsolationContext;
82
+ declare function collectClientHeaders(headers: Record<string, string | string[] | undefined>): Record<string, string>;
71
83
  declare function sanitizeProtocolId(value: string | undefined, fallback: string): string;
72
84
 
85
+ interface AgentServerLogger {
86
+ info(message: string, meta?: Record<string, unknown>): void;
87
+ warn(message: string, meta?: Record<string, unknown>): void;
88
+ error(message: string, meta?: Record<string, unknown>): void;
89
+ debug?(message: string, meta?: Record<string, unknown>): void;
90
+ }
91
+ interface AgentServerExpressRequest extends Request {
92
+ agentServerRequestId?: string;
93
+ }
94
+ interface AgentServerRequestHeadersMiddlewareOptions {
95
+ platformServer: string;
96
+ }
97
+ declare function agentServerRequestHeadersMiddleware(options: AgentServerRequestHeadersMiddlewareOptions): (req: Request, res: Response, next: NextFunction) => void;
98
+ declare function addProtectedHeaders(res: Response, headers: Record<string, string>, options?: {
99
+ sanitizeHeaderNames?: readonly string[];
100
+ }): void;
101
+ declare function agentServerLoggingMiddleware(logger: AgentServerLogger): (req: Request, res: Response, next: NextFunction) => void;
102
+ declare function readinessHandler(_req: Request, res: Response): void;
103
+ declare function healthzHandler(_req: Request, res: Response): void;
104
+ declare function methodNotAllowed(allowedMethods: readonly string[]): (_req: Request, res: Response) => void;
105
+ declare function notFoundMiddleware(): (_req: Request, res: Response, _next: NextFunction) => void;
106
+ declare function errorMiddleware(logger: AgentServerLogger): (err: unknown, req: Request, res: Response, next: NextFunction) => void;
107
+ declare function closeServerWithTimeout(server: Server, timeoutSeconds: number): Promise<void>;
108
+ declare function configureExpressAgentServerApp(app: Express, options?: {
109
+ trustProxy?: boolean | number | string;
110
+ }): void;
111
+ declare function logAgentServerStartupConfiguration(logger: AgentServerLogger, config: AgentServerConfig, platformServer: string): void;
112
+ declare function maskUri(uri: string): string;
113
+ declare function formatError(error: unknown): string;
114
+ declare function defaultAgentServerLogger(scope: string): AgentServerLogger;
115
+
73
116
  interface AgentServerObservabilityLogger {
74
117
  info(message: string, meta?: Record<string, unknown>): void;
75
118
  warn(message: string, meta?: Record<string, unknown>): void;
@@ -98,6 +141,9 @@ declare const AGENTSERVER_CORE_PACKAGE_VERSION: string;
98
141
  declare const AGENTSERVER_INVOCATION_ID_BAGGAGE = "azure.ai.agentserver.invocation_id";
99
142
  declare const AGENTSERVER_SESSION_ID_BAGGAGE = "azure.ai.agentserver.session_id";
100
143
  declare const AGENTSERVER_CONVERSATION_ID_BAGGAGE = "azure.ai.agentserver.conversation_id";
144
+ declare const AGENTSERVER_RESPONSE_ID_BAGGAGE = "azure.ai.agentserver.response_id";
145
+ declare const AGENTSERVER_STREAMING_BAGGAGE = "azure.ai.agentserver.streaming";
146
+ declare const AGENTSERVER_REQUEST_ID_BAGGAGE = "azure.ai.agentserver.x-request-id";
101
147
  declare const ATTR_SERVICE_NAME = "service.name";
102
148
  declare const ATTR_GEN_AI_SYSTEM = "gen_ai.system";
103
149
  declare const ATTR_GEN_AI_PROVIDER_NAME = "gen_ai.provider.name";
@@ -112,6 +158,9 @@ declare const ATTR_SESSION_ID = "microsoft.session.id";
112
158
  declare const ATTR_INVOCATION_ID = "azure.ai.agentserver.invocations.id";
113
159
  declare const ATTR_INVOCATIONS_ERROR_CODE = "azure.ai.agentserver.invocations.error.code";
114
160
  declare const ATTR_INVOCATIONS_ERROR_MESSAGE = "azure.ai.agentserver.invocations.error.message";
161
+ declare const ATTR_RESPONSES_STREAMING = "azure.ai.agentserver.responses.streaming";
162
+ declare const ATTR_RESPONSES_ERROR_CODE = "azure.ai.agentserver.responses.error.code";
163
+ declare const ATTR_RESPONSES_ERROR_MESSAGE = "azure.ai.agentserver.responses.error.message";
115
164
  interface AgentServerRequestSpanOptions {
116
165
  config: Pick<AgentServerConfig, "agentId" | "agentName" | "agentVersion" | "projectArmId">;
117
166
  headers: HeaderGetter;
@@ -121,6 +170,9 @@ interface AgentServerRequestSpanOptions {
121
170
  instrumentationScope?: string;
122
171
  invocationId?: string;
123
172
  sessionId?: string;
173
+ responseId?: string;
174
+ conversationId?: string;
175
+ streaming?: boolean | string;
124
176
  correlationRequestId?: string;
125
177
  }
126
178
  interface AgentServerRequestSpan {
@@ -140,10 +192,13 @@ declare function startAgentServerRequestSpan(options: AgentServerRequestSpanOpti
140
192
  declare function withAgentServerBaggage(sourceContext: Context, values: {
141
193
  invocationId?: string;
142
194
  sessionId?: string;
195
+ responseId?: string;
196
+ conversationId?: string;
197
+ streaming?: boolean | string;
143
198
  requestId?: string;
144
199
  }): Context;
145
200
  declare function recordAgentServerSpanError(span: Span, error: unknown, code?: string): void;
146
201
  declare function createFoundryEnrichmentSpanProcessor(config: Pick<AgentServerConfig, "agentId" | "agentName" | "agentVersion" | "projectArmId">): AgentServerSpanProcessor;
147
202
  declare function flushSpans(timeoutMillis?: number): void;
148
203
 
149
- export { AGENTSERVER_CONVERSATION_ID_BAGGAGE, AGENTSERVER_CORE_PACKAGE_VERSION, AGENTSERVER_INVOCATION_ID_BAGGAGE, AGENTSERVER_SESSION_ID_BAGGAGE, ATTR_FOUNDRY_PROJECT_ID, ATTR_GEN_AI_AGENT_ID, ATTR_GEN_AI_AGENT_NAME, ATTR_GEN_AI_AGENT_VERSION, ATTR_GEN_AI_CONVERSATION_ID, ATTR_GEN_AI_OPERATION_NAME, ATTR_GEN_AI_PROVIDER_NAME, ATTR_GEN_AI_RESPONSE_ID, ATTR_GEN_AI_SYSTEM, ATTR_INVOCATIONS_ERROR_CODE, ATTR_INVOCATIONS_ERROR_MESSAGE, ATTR_INVOCATION_ID, ATTR_SERVICE_NAME, ATTR_SESSION_ID, type AgentServerConfig, type AgentServerConfigOptions, type AgentServerErrorBody, type AgentServerLogLevel, type AgentServerObservabilityHandle, type AgentServerObservabilityLogger, type AgentServerRequestSpan, type AgentServerRequestSpanOptions, type AgentServerSpanProcessor, CHAT_ISOLATION_HEADER, type ConfigureAgentServerObservabilityOptions, type HeaderGetter, INVOCATION_ID_HEADER, INVOCATION_ID_HEADERS, type InvocationIdentity, type InvocationIdentityInput, type QueryGetter, REQUEST_ID_HEADER, SESSION_ID_HEADER, SESSION_ID_HEADERS, USER_ISOLATION_HEADER, buildPlatformServerHeader, buildServerVersionSegment, configureAgentServerObservability, createErrorBody, createFoundryEnrichmentSpanProcessor, flushSpans, recordAgentServerSpanError, resolveAgentServerConfig, resolveGracefulShutdownTimeout, resolveInvocationIdentity, resolveLogLevel, resolvePort, resolveRequestId, resolveSseKeepAliveInterval, sanitizeProtocolId, startAgentServerRequestSpan, withAgentServerBaggage };
204
+ export { AGENTSERVER_CONVERSATION_ID_BAGGAGE, AGENTSERVER_CORE_PACKAGE_VERSION, AGENTSERVER_INVOCATION_ID_BAGGAGE, AGENTSERVER_REQUEST_ID_BAGGAGE, AGENTSERVER_RESPONSE_ID_BAGGAGE, AGENTSERVER_SESSION_ID_BAGGAGE, AGENTSERVER_STREAMING_BAGGAGE, ATTR_FOUNDRY_PROJECT_ID, ATTR_GEN_AI_AGENT_ID, ATTR_GEN_AI_AGENT_NAME, ATTR_GEN_AI_AGENT_VERSION, ATTR_GEN_AI_CONVERSATION_ID, ATTR_GEN_AI_OPERATION_NAME, ATTR_GEN_AI_PROVIDER_NAME, ATTR_GEN_AI_RESPONSE_ID, ATTR_GEN_AI_SYSTEM, ATTR_INVOCATIONS_ERROR_CODE, ATTR_INVOCATIONS_ERROR_MESSAGE, ATTR_INVOCATION_ID, ATTR_RESPONSES_ERROR_CODE, ATTR_RESPONSES_ERROR_MESSAGE, ATTR_RESPONSES_STREAMING, ATTR_SERVICE_NAME, ATTR_SESSION_ID, type AgentIsolationContext, type AgentServerConfig, type AgentServerConfigOptions, type AgentServerErrorBody, type AgentServerExpressRequest, type AgentServerLogLevel, type AgentServerLogger, type AgentServerObservabilityHandle, type AgentServerObservabilityLogger, type AgentServerRequestSpan, type AgentServerRequestSpanOptions, type AgentServerSpanProcessor, CHAT_ISOLATION_HEADER, CLIENT_HEADER_PREFIX, type ConfigureAgentServerObservabilityOptions, type HeaderGetter, INVOCATION_ID_HEADER, INVOCATION_ID_HEADERS, type InvocationIdentity, type InvocationIdentityInput, type QueryGetter, REQUEST_ID_HEADER, SESSION_ID_HEADER, SESSION_ID_HEADERS, USER_ISOLATION_HEADER, addProtectedHeaders, agentServerLoggingMiddleware, agentServerRequestHeadersMiddleware, buildPlatformServerHeader, buildServerVersionSegment, closeServerWithTimeout, collectClientHeaders, configureAgentServerObservability, configureExpressAgentServerApp, createErrorBody, createFoundryEnrichmentSpanProcessor, defaultAgentServerLogger, errorMiddleware, flushSpans, formatError, healthzHandler, logAgentServerStartupConfiguration, maskUri, methodNotAllowed, notFoundMiddleware, readinessHandler, recordAgentServerSpanError, resolveAgentIsolation, resolveAgentServerConfig, resolveGracefulShutdownTimeout, resolveInvocationIdentity, resolveLogLevel, resolvePort, resolveRequestId, resolveSseKeepAliveInterval, sanitizeProtocolId, startAgentServerRequestSpan, withAgentServerBaggage };
package/dist/index.js CHANGED
@@ -105,12 +105,17 @@ function validateInteger(value, source) {
105
105
 
106
106
  // src/errors.ts
107
107
  function createErrorBody(code, message, options = {}) {
108
+ const additionalInfo = {
109
+ ...options.additionalInfo ?? {},
110
+ ...options.requestId ? { request_id: options.requestId } : {}
111
+ };
108
112
  return {
109
113
  error: {
110
114
  code,
111
115
  message,
112
116
  ...options.type ? { type: options.type } : {},
113
- ...options.details ? { details: options.details } : {}
117
+ ...options.details ? { details: options.details } : {},
118
+ ...Object.keys(additionalInfo).length > 0 ? { additionalInfo } : {}
114
119
  }
115
120
  };
116
121
  }
@@ -122,6 +127,7 @@ var INVOCATION_ID_HEADER = "x-agent-invocation-id";
122
127
  var SESSION_ID_HEADER = "x-agent-session-id";
123
128
  var USER_ISOLATION_HEADER = "x-agent-user-isolation-key";
124
129
  var CHAT_ISOLATION_HEADER = "x-agent-chat-isolation-key";
130
+ var CLIENT_HEADER_PREFIX = "x-client-";
125
131
  var SESSION_ID_HEADERS = [
126
132
  SESSION_ID_HEADER,
127
133
  "x-foundry-agent-session-id",
@@ -155,6 +161,28 @@ function resolveRequestId(header) {
155
161
  const incoming = header(REQUEST_ID_HEADER)?.trim();
156
162
  return incoming || randomUUID().replaceAll("-", "");
157
163
  }
164
+ function resolveAgentIsolation(header) {
165
+ const userKey = header(USER_ISOLATION_HEADER);
166
+ const chatKey = header(CHAT_ISOLATION_HEADER);
167
+ return {
168
+ ...userKey !== void 0 ? { userKey } : {},
169
+ ...chatKey !== void 0 ? { chatKey } : {}
170
+ };
171
+ }
172
+ function collectClientHeaders(headers) {
173
+ const clientHeaders = {};
174
+ for (const [name, value] of Object.entries(headers)) {
175
+ const lowerName = name.toLowerCase();
176
+ if (!lowerName.startsWith(CLIENT_HEADER_PREFIX)) {
177
+ continue;
178
+ }
179
+ const headerValue = Array.isArray(value) ? value.join(",") : value;
180
+ if (headerValue !== void 0) {
181
+ clientHeaders[lowerName] = headerValue;
182
+ }
183
+ }
184
+ return clientHeaders;
185
+ }
158
186
  function sanitizeProtocolId(value, fallback) {
159
187
  const normalized = value?.trim();
160
188
  if (!normalized || normalized.length > MAX_ID_LENGTH || !VALID_ID_RE.test(normalized)) {
@@ -192,6 +220,190 @@ function traceIdFromTraceparent(traceparent) {
192
220
  return void 0;
193
221
  }
194
222
 
223
+ // src/host.ts
224
+ function agentServerRequestHeadersMiddleware(options) {
225
+ return (req, res, next) => {
226
+ const requestId = resolveRequestId((name) => req.header(name));
227
+ req.agentServerRequestId = requestId;
228
+ addProtectedHeaders(res, {
229
+ [REQUEST_ID_HEADER]: requestId,
230
+ "x-platform-server": options.platformServer
231
+ });
232
+ next();
233
+ };
234
+ }
235
+ function addProtectedHeaders(res, headers, options = {}) {
236
+ const protectedRes = res;
237
+ if (!protectedRes.__agentServerProtectedHeaders) {
238
+ const protectedHeaders = /* @__PURE__ */ new Map();
239
+ const originalSetHeader2 = res.setHeader.bind(res);
240
+ protectedRes.__agentServerProtectedHeaders = protectedHeaders;
241
+ protectedRes.__agentServerOriginalSetHeader = originalSetHeader2;
242
+ res.setHeader = ((name, value) => {
243
+ const protectedValue = protectedHeaders.get(name.toLowerCase());
244
+ if (protectedValue !== void 0) {
245
+ return originalSetHeader2(name, protectedValue);
246
+ }
247
+ return originalSetHeader2(name, value);
248
+ });
249
+ }
250
+ const originalSetHeader = protectedRes.__agentServerOriginalSetHeader ?? res.setHeader.bind(res);
251
+ const sanitizeHeaderNames = new Set(
252
+ (options.sanitizeHeaderNames ?? [INVOCATION_ID_HEADER, SESSION_ID_HEADER]).map((name) => name.toLowerCase())
253
+ );
254
+ for (const [name, value] of Object.entries(headers)) {
255
+ const lowerName = name.toLowerCase();
256
+ const safeValue = sanitizeHeaderNames.has(lowerName) ? sanitizeProtocolId(value, value) : value;
257
+ protectedRes.__agentServerProtectedHeaders.set(lowerName, safeValue);
258
+ originalSetHeader(name, safeValue);
259
+ }
260
+ }
261
+ function agentServerLoggingMiddleware(logger) {
262
+ return (req, res, next) => {
263
+ const start = Date.now();
264
+ res.on("finish", () => {
265
+ logger.info("http request", {
266
+ method: req.method,
267
+ path: req.path,
268
+ statusCode: res.statusCode,
269
+ durationMs: Date.now() - start,
270
+ requestId: req.agentServerRequestId,
271
+ clientRequestId: req.header("x-ms-client-request-id")
272
+ });
273
+ });
274
+ next();
275
+ };
276
+ }
277
+ function readinessHandler(_req, res) {
278
+ res.json({ status: "healthy" });
279
+ }
280
+ function healthzHandler(_req, res) {
281
+ res.json({ ok: true });
282
+ }
283
+ function methodNotAllowed(allowedMethods) {
284
+ return (_req, res) => {
285
+ res.setHeader("Allow", allowedMethods.join(", "));
286
+ res.status(405).json(
287
+ createErrorBody(
288
+ "method_not_allowed",
289
+ `Allowed methods: ${allowedMethods.join(", ")}`
290
+ )
291
+ );
292
+ };
293
+ }
294
+ function notFoundMiddleware() {
295
+ return (_req, res, _next) => {
296
+ res.status(404).json(createErrorBody("not_found", "No route matched this path."));
297
+ };
298
+ }
299
+ function errorMiddleware(logger) {
300
+ return (err, req, res, _next) => {
301
+ logger.error("Unhandled middleware error", { error: formatError(err) });
302
+ if (res.headersSent) {
303
+ res.end();
304
+ return;
305
+ }
306
+ res.status(500).json(
307
+ createErrorBody(
308
+ "internal_error",
309
+ err instanceof Error ? err.message : String(err),
310
+ {
311
+ requestId: req.agentServerRequestId
312
+ }
313
+ )
314
+ );
315
+ };
316
+ }
317
+ async function closeServerWithTimeout(server, timeoutSeconds) {
318
+ const closePromise = new Promise((resolve, reject) => {
319
+ server.close((error) => {
320
+ if (!error || error.code === "ERR_SERVER_NOT_RUNNING") {
321
+ resolve();
322
+ return;
323
+ }
324
+ reject(error);
325
+ });
326
+ });
327
+ if (timeoutSeconds <= 0) {
328
+ await closePromise;
329
+ return;
330
+ }
331
+ let timeout;
332
+ const timeoutPromise = new Promise((resolve) => {
333
+ timeout = setTimeout(() => {
334
+ server.closeAllConnections?.();
335
+ resolve();
336
+ }, timeoutSeconds * 1e3);
337
+ });
338
+ try {
339
+ await Promise.race([closePromise, timeoutPromise]);
340
+ } finally {
341
+ if (timeout) {
342
+ clearTimeout(timeout);
343
+ }
344
+ }
345
+ }
346
+ function configureExpressAgentServerApp(app, options = {}) {
347
+ app.disable("x-powered-by");
348
+ app.set("trust proxy", options.trustProxy ?? 1);
349
+ }
350
+ function logAgentServerStartupConfiguration(logger, config, platformServer) {
351
+ logger.info("Foundry Agent Server platform environment", {
352
+ isHosted: config.isHosted,
353
+ agentName: config.agentName || "(not set)",
354
+ agentVersion: config.agentVersion || "(not set)",
355
+ port: config.port,
356
+ sessionId: config.sessionId || "(not set)",
357
+ sseKeepAliveIntervalSeconds: config.sseKeepAliveIntervalSeconds > 0 ? config.sseKeepAliveIntervalSeconds : "disabled"
358
+ });
359
+ logger.info("Foundry Agent Server connectivity", {
360
+ projectEndpoint: maskUri(config.projectEndpoint),
361
+ otlpEndpoint: maskUri(config.otlpEndpoint),
362
+ applicationInsightsConfigured: Boolean(
363
+ config.applicationInsightsConnectionString.trim()
364
+ )
365
+ });
366
+ logger.info("Foundry Agent Server host options", {
367
+ gracefulShutdownTimeoutSeconds: config.gracefulShutdownTimeoutSeconds,
368
+ platformServer
369
+ });
370
+ }
371
+ function maskUri(uri) {
372
+ const normalized = uri.trim();
373
+ if (!normalized) {
374
+ return "(not set)";
375
+ }
376
+ try {
377
+ const parsed = new URL(normalized);
378
+ return `${parsed.protocol}//${parsed.host}`;
379
+ } catch {
380
+ return "(redacted)";
381
+ }
382
+ }
383
+ function formatError(error) {
384
+ if (error instanceof Error) {
385
+ return error.stack ?? error.message;
386
+ }
387
+ return String(error);
388
+ }
389
+ function defaultAgentServerLogger(scope) {
390
+ return {
391
+ info: (message, meta) => {
392
+ if (meta) {
393
+ console.log(`[${scope}] ${message}`, meta);
394
+ } else {
395
+ console.log(`[${scope}] ${message}`);
396
+ }
397
+ },
398
+ warn: (message, meta) => {
399
+ console.warn(`[${scope}] ${message}`, meta ?? "");
400
+ },
401
+ error: (message, meta) => {
402
+ console.error(`[${scope}] ${message}`, meta ?? "");
403
+ }
404
+ };
405
+ }
406
+
195
407
  // src/tracing.ts
196
408
  import {
197
409
  SpanKind,
@@ -204,6 +416,9 @@ import { AsyncLocalStorageContextManager } from "@opentelemetry/context-async-ho
204
416
  var AGENTSERVER_INVOCATION_ID_BAGGAGE = "azure.ai.agentserver.invocation_id";
205
417
  var AGENTSERVER_SESSION_ID_BAGGAGE = "azure.ai.agentserver.session_id";
206
418
  var AGENTSERVER_CONVERSATION_ID_BAGGAGE = "azure.ai.agentserver.conversation_id";
419
+ var AGENTSERVER_RESPONSE_ID_BAGGAGE = "azure.ai.agentserver.response_id";
420
+ var AGENTSERVER_STREAMING_BAGGAGE = "azure.ai.agentserver.streaming";
421
+ var AGENTSERVER_REQUEST_ID_BAGGAGE = "azure.ai.agentserver.x-request-id";
207
422
  var ATTR_SERVICE_NAME = "service.name";
208
423
  var ATTR_GEN_AI_SYSTEM = "gen_ai.system";
209
424
  var ATTR_GEN_AI_PROVIDER_NAME = "gen_ai.provider.name";
@@ -218,6 +433,9 @@ var ATTR_SESSION_ID = "microsoft.session.id";
218
433
  var ATTR_INVOCATION_ID = "azure.ai.agentserver.invocations.id";
219
434
  var ATTR_INVOCATIONS_ERROR_CODE = "azure.ai.agentserver.invocations.error.code";
220
435
  var ATTR_INVOCATIONS_ERROR_MESSAGE = "azure.ai.agentserver.invocations.error.message";
436
+ var ATTR_RESPONSES_STREAMING = "azure.ai.agentserver.responses.streaming";
437
+ var ATTR_RESPONSES_ERROR_CODE = "azure.ai.agentserver.responses.error.code";
438
+ var ATTR_RESPONSES_ERROR_MESSAGE = "azure.ai.agentserver.responses.error.message";
221
439
  var SERVICE_NAME_VALUE = "azure.ai.agentserver";
222
440
  var GEN_AI_SYSTEM_VALUE = "azure.ai.agentserver";
223
441
  var GEN_AI_PROVIDER_NAME_VALUE = "AzureAI Hosted Agents";
@@ -250,6 +468,11 @@ function startAgentServerRequestSpan(options) {
250
468
  setAttribute(attributes, ATTR_FOUNDRY_PROJECT_ID, config.projectArmId);
251
469
  setAttribute(attributes, ATTR_SESSION_ID, options.sessionId);
252
470
  setAttribute(attributes, ATTR_INVOCATION_ID, options.invocationId);
471
+ setAttribute(attributes, ATTR_GEN_AI_RESPONSE_ID, options.responseId);
472
+ setAttribute(attributes, ATTR_GEN_AI_CONVERSATION_ID, options.conversationId);
473
+ if (options.streaming !== void 0) {
474
+ attributes[ATTR_RESPONSES_STREAMING] = typeof options.streaming === "boolean" ? options.streaming : options.streaming === "true";
475
+ }
253
476
  const requestId = options.headers("x-request-id") ?? options.correlationRequestId;
254
477
  setAttribute(attributes, "x_request_id", requestId);
255
478
  const span = trace.getTracer(instrumentationScope).startSpan(
@@ -265,6 +488,9 @@ function startAgentServerRequestSpan(options) {
265
488
  {
266
489
  invocationId: options.invocationId,
267
490
  sessionId: options.sessionId,
491
+ responseId: options.responseId,
492
+ conversationId: options.conversationId,
493
+ streaming: options.streaming,
268
494
  requestId
269
495
  }
270
496
  );
@@ -314,10 +540,28 @@ function withAgentServerBaggage(sourceContext, values) {
314
540
  });
315
541
  }
316
542
  if (values.requestId) {
543
+ baggage = baggage.setEntry(AGENTSERVER_REQUEST_ID_BAGGAGE, {
544
+ value: values.requestId
545
+ });
317
546
  baggage = baggage.setEntry("x_request_id", {
318
547
  value: values.requestId
319
548
  });
320
549
  }
550
+ if (values.responseId !== void 0) {
551
+ baggage = baggage.setEntry(AGENTSERVER_RESPONSE_ID_BAGGAGE, {
552
+ value: values.responseId
553
+ });
554
+ }
555
+ if (values.conversationId !== void 0) {
556
+ baggage = baggage.setEntry(AGENTSERVER_CONVERSATION_ID_BAGGAGE, {
557
+ value: values.conversationId
558
+ });
559
+ }
560
+ if (values.streaming !== void 0) {
561
+ baggage = baggage.setEntry(AGENTSERVER_STREAMING_BAGGAGE, {
562
+ value: typeof values.streaming === "boolean" ? String(values.streaming) : values.streaming
563
+ });
564
+ }
321
565
  return propagation.setBaggage(sourceContext, baggage);
322
566
  }
323
567
  function recordAgentServerSpanError(span, error, code = "internal_error") {
@@ -450,7 +694,7 @@ async function configureAgentServerObservabilityOnce(options) {
450
694
  return makeHandle(exporters, shutdownCallbacks);
451
695
  } catch (error) {
452
696
  logger.warn("Failed to initialize Azure Monitor observability", {
453
- error: formatError(error)
697
+ error: formatError2(error)
454
698
  });
455
699
  }
456
700
  }
@@ -469,7 +713,7 @@ async function configureAgentServerObservabilityOnce(options) {
469
713
  return makeHandle(exporters, shutdownCallbacks, setup2.configured);
470
714
  } catch (error) {
471
715
  logger.warn("Failed to initialize OTLP observability", {
472
- error: formatError(error)
716
+ error: formatError2(error)
473
717
  });
474
718
  }
475
719
  }
@@ -498,7 +742,7 @@ async function createOtlpSpanProcessor(config, logger) {
498
742
  return new BatchSpanProcessor(new OTLPTraceExporter({ url: endpoint }));
499
743
  } catch (error) {
500
744
  logger.warn("Failed to initialize OTLP span processor", {
501
- error: formatError(error)
745
+ error: formatError2(error)
502
746
  });
503
747
  return void 0;
504
748
  }
@@ -520,7 +764,7 @@ async function createResource(config, logger) {
520
764
  return resources.resourceFromAttributes(attributes);
521
765
  } catch (error) {
522
766
  logger.debug?.("OpenTelemetry resource package unavailable", {
523
- error: formatError(error)
767
+ error: formatError2(error)
524
768
  });
525
769
  return void 0;
526
770
  }
@@ -546,7 +790,7 @@ async function ensureTraceProvider(config, logger, spanProcessors) {
546
790
  }
547
791
  } catch (error) {
548
792
  logger.debug?.("OpenTelemetry API provider inspection failed", {
549
- error: formatError(error)
793
+ error: formatError2(error)
550
794
  });
551
795
  }
552
796
  try {
@@ -564,19 +808,15 @@ async function ensureTraceProvider(config, logger, spanProcessors) {
564
808
  };
565
809
  } catch (error) {
566
810
  logger.warn("Failed to initialize local OpenTelemetry tracer provider", {
567
- error: formatError(error)
811
+ error: formatError2(error)
568
812
  });
569
813
  return { configured: false };
570
814
  }
571
815
  }
572
816
  async function importOptional(specifier) {
573
- const dynamicImport = new Function(
574
- "specifier",
575
- "return import(specifier)"
576
- );
577
- return await dynamicImport(specifier);
817
+ return await import(specifier);
578
818
  }
579
- function formatError(error) {
819
+ function formatError2(error) {
580
820
  if (error instanceof Error) {
581
821
  return error.stack ?? error.message;
582
822
  }
@@ -627,7 +867,10 @@ export {
627
867
  AGENTSERVER_CONVERSATION_ID_BAGGAGE,
628
868
  AGENTSERVER_CORE_PACKAGE_VERSION,
629
869
  AGENTSERVER_INVOCATION_ID_BAGGAGE,
870
+ AGENTSERVER_REQUEST_ID_BAGGAGE,
871
+ AGENTSERVER_RESPONSE_ID_BAGGAGE,
630
872
  AGENTSERVER_SESSION_ID_BAGGAGE,
873
+ AGENTSERVER_STREAMING_BAGGAGE,
631
874
  ATTR_FOUNDRY_PROJECT_ID,
632
875
  ATTR_GEN_AI_AGENT_ID,
633
876
  ATTR_GEN_AI_AGENT_NAME,
@@ -640,22 +883,42 @@ export {
640
883
  ATTR_INVOCATIONS_ERROR_CODE,
641
884
  ATTR_INVOCATIONS_ERROR_MESSAGE,
642
885
  ATTR_INVOCATION_ID,
886
+ ATTR_RESPONSES_ERROR_CODE,
887
+ ATTR_RESPONSES_ERROR_MESSAGE,
888
+ ATTR_RESPONSES_STREAMING,
643
889
  ATTR_SERVICE_NAME,
644
890
  ATTR_SESSION_ID,
645
891
  CHAT_ISOLATION_HEADER,
892
+ CLIENT_HEADER_PREFIX,
646
893
  INVOCATION_ID_HEADER,
647
894
  INVOCATION_ID_HEADERS,
648
895
  REQUEST_ID_HEADER,
649
896
  SESSION_ID_HEADER,
650
897
  SESSION_ID_HEADERS,
651
898
  USER_ISOLATION_HEADER,
899
+ addProtectedHeaders,
900
+ agentServerLoggingMiddleware,
901
+ agentServerRequestHeadersMiddleware,
652
902
  buildPlatformServerHeader,
653
903
  buildServerVersionSegment,
904
+ closeServerWithTimeout,
905
+ collectClientHeaders,
654
906
  configureAgentServerObservability,
907
+ configureExpressAgentServerApp,
655
908
  createErrorBody,
656
909
  createFoundryEnrichmentSpanProcessor,
910
+ defaultAgentServerLogger,
911
+ errorMiddleware,
657
912
  flushSpans,
913
+ formatError,
914
+ healthzHandler,
915
+ logAgentServerStartupConfiguration,
916
+ maskUri,
917
+ methodNotAllowed,
918
+ notFoundMiddleware,
919
+ readinessHandler,
658
920
  recordAgentServerSpanError,
921
+ resolveAgentIsolation,
659
922
  resolveAgentServerConfig,
660
923
  resolveGracefulShutdownTimeout,
661
924
  resolveInvocationIdentity,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cuylabs/agent-foundry-agentserver-core",
3
- "version": "4.8.1",
3
+ "version": "4.9.0",
4
4
  "description": "Shared TypeScript host utilities for Foundry Agent Server protocol packages",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -18,7 +18,8 @@
18
18
  ],
19
19
  "dependencies": {
20
20
  "@opentelemetry/api": "^1.9.0",
21
- "@opentelemetry/context-async-hooks": "^2.6.0"
21
+ "@opentelemetry/context-async-hooks": "^2.6.0",
22
+ "express": "^5.0.0"
22
23
  },
23
24
  "optionalDependencies": {
24
25
  "@azure/monitor-opentelemetry": "^1.16.0",
@@ -28,6 +29,7 @@
28
29
  "@opentelemetry/sdk-trace-node": "^2.6.0"
29
30
  },
30
31
  "devDependencies": {
32
+ "@types/express": "^5.0.0",
31
33
  "@types/node": "^22.0.0",
32
34
  "tsup": "^8.0.0",
33
35
  "typescript": "^5.7.0",