diff --git a/packages/agents-a365-observability-extensions-langchain/src/tracer.ts b/packages/agents-a365-observability-extensions-langchain/src/tracer.ts index 3708b04c..2fdb64d8 100644 --- a/packages/agents-a365-observability-extensions-langchain/src/tracer.ts +++ b/packages/agents-a365-observability-extensions-langchain/src/tracer.ts @@ -59,9 +59,10 @@ export class LangChainTracer extends BaseTracer { spanName = `${operation} ${Utils.getModel(run) || run.name}`.trim(); } - const startTime = Date.now(); + const startTime = run.start_time ?? Date.now(); const span = this.tracer.startSpan(spanName, { kind: SpanKind.INTERNAL, + startTime, attributes: { [OpenTelemetryConstants.GEN_AI_SYSTEM_KEY]: "langchain" }, }, activeContext); @@ -84,8 +85,8 @@ export class LangChainTracer extends BaseTracer { return; } + const { span } = entry; try { - const { span } = entry; entry.lastAccessTime = Date.now(); if (run.error) { @@ -108,8 +109,11 @@ export class LangChainTracer extends BaseTracer { Utils.setSessionIdAttribute(run, span); Utils.setTokenAttributes(run, span); - span.end(); + } catch (error) { + logger.error(`[LangChainTracer] Error setting span attributes for run ${run.name}: ${error instanceof Error ? error.message : String(error)}`); + span.setStatus({ code: SpanStatusCode.ERROR }); } finally { + span.end(run.end_time ?? undefined); delete this.runs[run.id]; delete this.parentByRunId[run.id]; await super._endTrace(run); diff --git a/packages/agents-a365-observability-hosting/package.json b/packages/agents-a365-observability-hosting/package.json index 61bcd883..238e2dc7 100644 --- a/packages/agents-a365-observability-hosting/package.json +++ b/packages/agents-a365-observability-hosting/package.json @@ -44,7 +44,8 @@ "dependencies": { "@microsoft/agents-a365-observability": "workspace:*", "@microsoft/agents-a365-runtime": "workspace:*", - "@microsoft/agents-hosting": "catalog:" + "@microsoft/agents-hosting": "catalog:", + "@opentelemetry/api": "catalog:" }, "devDependencies": { "@eslint/js": "catalog:", diff --git a/packages/agents-a365-observability-hosting/src/utils/ScopeUtils.ts b/packages/agents-a365-observability-hosting/src/utils/ScopeUtils.ts index 95b955a7..1f8531c3 100644 --- a/packages/agents-a365-observability-hosting/src/utils/ScopeUtils.ts +++ b/packages/agents-a365-observability-hosting/src/utils/ScopeUtils.ts @@ -4,6 +4,7 @@ // ------------------------------------------------------------------------------ import { TurnContext } from '@microsoft/agents-hosting'; +import { TimeInput } from '@opentelemetry/api'; import { InvokeAgentScope, InferenceScope, @@ -59,6 +60,8 @@ export class ScopeUtils { agentId: recipient.agenticAppId, agentName: recipient.name, agentAUID: recipient.aadObjectId, + agentBlueprintId: recipient.agenticAppBlueprintId, + agentUPN: recipient.agenticUserId, agentDescription: recipient.role, tenantId: recipient.tenantId } as AgentDetails; @@ -79,7 +82,8 @@ export class ScopeUtils { agentAUID: from.aadObjectId, agentDescription: from.role, tenantId: from.tenantId, - agentId: from.agenticAppId + agentId: from.agenticAppId, + agentUPN: from.agenticUserId } as AgentDetails; } @@ -127,11 +131,15 @@ export class ScopeUtils { * Also records input messages from the context if present. * @param details The inference call details (model, provider, tokens, etc.). * @param turnContext The current activity context to derive scope parameters from. + * @param startTime Optional explicit start time (ms epoch, Date, or HrTime). + * @param endTime Optional explicit end time (ms epoch, Date, or HrTime). * @returns A started `InferenceScope` enriched with context-derived parameters. */ static populateInferenceScopeFromTurnContext( details: InferenceDetails, - turnContext: TurnContext + turnContext: TurnContext, + startTime?: TimeInput, + endTime?: TimeInput ): InferenceScope { const agent = ScopeUtils.deriveAgentDetails(turnContext); const tenant = ScopeUtils.deriveTenantDetails(turnContext); @@ -145,7 +153,7 @@ export class ScopeUtils { throw new Error('populateInferenceScopeFromTurnContext: Missing tenant details on TurnContext (recipient)'); } - const scope = InferenceScope.start(details, agent, tenant, conversationId, sourceMetadata); + const scope = InferenceScope.start(details, agent, tenant, conversationId, sourceMetadata, undefined, startTime, endTime); this.setInputMessageTags(scope, turnContext); return scope; } @@ -157,11 +165,15 @@ export class ScopeUtils { * Also sets execution type and input messages from the context if present. * @param details The invoke-agent call details to be augmented and used for the scope. * @param turnContext The current activity context to derive scope parameters from. + * @param startTime Optional explicit start time (ms epoch, Date, or HrTime). + * @param endTime Optional explicit end time (ms epoch, Date, or HrTime). * @returns A started `InvokeAgentScope` enriched with context-derived parameters. */ static populateInvokeAgentScopeFromTurnContext( details: InvokeAgentDetails, - turnContext: TurnContext + turnContext: TurnContext, + startTime?: TimeInput, + endTime?: TimeInput ): InvokeAgentScope { const tenant = ScopeUtils.deriveTenantDetails(turnContext); const callerAgent = ScopeUtils.deriveCallerAgent(turnContext); @@ -172,7 +184,7 @@ export class ScopeUtils { throw new Error('populateInvokeAgentScopeFromTurnContext: Missing tenant details on TurnContext (recipient)'); } - const scope = InvokeAgentScope.start(invokeAgentDetails, tenant, callerAgent, caller); + const scope = InvokeAgentScope.start(invokeAgentDetails, tenant, callerAgent, caller, undefined, startTime, endTime); this.setInputMessageTags(scope, turnContext); return scope; } @@ -211,11 +223,16 @@ export class ScopeUtils { * Derives `agentDetails`, `tenantDetails`, `conversationId`, and `sourceMetadata` (channel name/link) from context. * @param details The tool call details (name, type, args, call id, etc.). * @param turnContext The current activity context to derive scope parameters from. + * @param startTime Optional explicit start time (ms epoch, Date, or HrTime). Useful when recording a + * tool call after execution has already completed. + * @param endTime Optional explicit end time (ms epoch, Date, or HrTime). * @returns A started `ExecuteToolScope` enriched with context-derived parameters. */ static populateExecuteToolScopeFromTurnContext( details: ToolCallDetails, - turnContext: TurnContext + turnContext: TurnContext, + startTime?: TimeInput, + endTime?: TimeInput ): ExecuteToolScope { const agent = ScopeUtils.deriveAgentDetails(turnContext); const tenant = ScopeUtils.deriveTenantDetails(turnContext); @@ -227,7 +244,7 @@ export class ScopeUtils { if (!tenant) { throw new Error('populateExecuteToolScopeFromTurnContext: Missing tenant details on TurnContext (recipient)'); } - const scope = ExecuteToolScope.start(details, agent, tenant, conversationId, sourceMetadata); + const scope = ExecuteToolScope.start(details, agent, tenant, conversationId, sourceMetadata, undefined, startTime, endTime); return scope; } } diff --git a/packages/agents-a365-observability/LOGGER_SETUP.md b/packages/agents-a365-observability/LOGGER_SETUP.md deleted file mode 100644 index 6a18c385..00000000 --- a/packages/agents-a365-observability/LOGGER_SETUP.md +++ /dev/null @@ -1,291 +0,0 @@ -# Logger Configuration Guide - -Complete guide for understanding and configuring logging in Agent 365 Observability SDK, including the default logger and custom logger options. - - - -## Default Logger: - -The SDK includes a **default logger** that is automatically active. Here's what it does: - -### 1. Reads Environment Variable -On startup, it reads `A365_OBSERVABILITY_LOG_LEVEL` to determine which message levels to log: -- `none` - No logging (default) -- `info` - Log info messages only -- `warn` - Log warning messages only -- `error` - Log error messages only -- `info|warn|error` - Log all levels (use pipe `|` to combine) - -### 2. Filters Messages -Based on the environment variable, it decides which messages to output: - -| Env Var | Info | Warn | Error | -|---------|------|------|-------| -| `none` | ❌ | ❌ | ❌ | -| `info` | ✅ | ❌ | ❌ | -| `warn\|error` | ❌ | ✅ | ✅ | -| `info\|warn\|error` | ✅ | ✅ | ✅ | - -### 3. Outputs to Console -Filtered messages are sent to the appropriate console method: -- Info → `console.log('[INFO]', message)` -- Warn → `console.warn('[WARN]', message)` -- Error → `console.error('[ERROR]', message)` -- Event → `console.log('[EVENT]: eventType status in durationMs ms [message] [correlationId]')` - -### 4. Works Automatically -```typescript -import { logger } from '@microsoft/agents-a365-observability'; - -// Uses the default logger automatically - no setup needed -logger.info('message'); // Logged if A365_OBSERVABILITY_LOG_LEVEL includes 'info' -logger.warn('warning'); // Logged if A365_OBSERVABILITY_LOG_LEVEL includes 'warn' -logger.error('error'); // Logged if A365_OBSERVABILITY_LOG_LEVEL includes 'error' -logger.event('my-event', true, 150, 'Operation completed'); // Log event with type, success, duration, and optional message -``` - -## Default Logger Configuration - -To control what the default logger outputs: - -```bash -# No logging (default) -export A365_OBSERVABILITY_LOG_LEVEL=none - -# Log all messages -export A365_OBSERVABILITY_LOG_LEVEL=info|warn|error - -# Log only warnings and errors -export A365_OBSERVABILITY_LOG_LEVEL=warn|error - -# Log only errors -export A365_OBSERVABILITY_LOG_LEVEL=error -``` - -**No code changes needed** - the default logger automatically reads the environment variable on startup. - -## Quick Start: Use Custom Logger - -### Your Custom Logger Implementation - -```typescript -import { Builder } from '@microsoft/agents-a365-observability'; -import type { ILogger } from '@microsoft/agents-a365-observability'; - -// Create your custom logger -const myLogger: ILogger = { - info: (msg, ...args) => console.log(`[INFO] ${msg}`, ...args), - warn: (msg, ...args) => console.warn(`[WARN] ${msg}`, ...args), - error: (msg, ...args) => console.error(`[ERROR] ${msg}`, ...args), - event: (name, success, duration) => { - const status = success ? 'succeeded' : 'failed'; - console.log(`[EVENT] ${name} ${status} in ${duration}ms`); - } -}; - -new Builder() - .withService('my-agent', '1.0.0') - .withCustomLogger(myLogger) - .build(); -``` - -### Event Logging - -The logger also supports event logging with success status and duration: - -```typescript -// Log event with success and duration -logger.event('export-operation', true, 1250); // Outputs: "[Event]: export-operation succeeded in 1250ms" -logger.event('export-operation', false, 1250); // Outputs: "[Event]: export-operation failed in 1250ms" -``` - -### Option 2: Your Existing Logger - -```typescript -import { Builder } from '@microsoft/agents-a365-observability'; -import { myExistingLogger } from './logging'; - -new Builder() - .withService('my-agent', '1.0.0') - .withCustomLogger({ - info: (msg, ...args) => myExistingLogger.info(msg, ...args), - warn: (msg, ...args) => myExistingLogger.warn(msg, ...args), - error: (msg, ...args) => myExistingLogger.error(msg, ...args), - event: (name, success, duration) => myExistingLogger.event(name, success, duration) - }) - .build(); -``` - -### Custom Logic (Filter by Content) - -```typescript -new Builder() - .withService('my-agent', '1.0.0') - .withCustomLogger({ - info: () => {}, - warn: (msg, ...args) => { - // Only log specific warnings - if (msg.includes('dropped') || msg.includes('overflow')) { - console.warn(msg, ...args); - } - }, - error: (msg, ...args) => console.error(msg, ...args), - event: (name, success, duration) => { - if (!success) { - console.error(`[Event] ${name} failed in ${duration}ms`); - } - } - }) - .build(); -``` - -## Production Setup: Route to Logging Service - -### Application Insights - -```typescript -import { Builder } from '@microsoft/agents-a365-observability'; -import { appInsights } from './monitoring'; - -new Builder() - .withService('my-agent', '1.0.0') - .withCustomLogger({ - info: (msg, ...args) => appInsights.trackTrace(msg, 1, { args }), - warn: (msg, ...args) => appInsights.trackTrace(msg, 2, { args }), - error: (msg, ...args) => appInsights.trackTrace(msg, 3, { args }), - event: (name, success, duration) => { - appInsights.trackEvent('agent-event', { name, success: String(success), duration }); - } - }) - .build(); -``` - -### Winston Logger - -```typescript -import * as winston from 'winston'; -import { Builder } from '@microsoft/agents-a365-observability'; - -const logger = winston.createLogger({ - level: 'info', - format: winston.format.json(), - transports: [ - new winston.transports.File({ filename: 'error.log', level: 'error' }), - new winston.transports.File({ filename: 'combined.log' }) - ] -}); - -new Builder() - .withService('my-agent', '1.0.0') - .withCustomLogger({ - info: (msg, ...args) => logger.info(msg, ...args), - warn: (msg, ...args) => logger.warn(msg, ...args), - error: (msg, ...args) => logger.error(msg, ...args), - event: (name, success, duration) => { - const status = success ? 'succeeded' : 'failed'; - logger.info(`Event: ${name} ${status} in ${duration}ms`); - } - }) - .build(); -``` - -### Custom Service - -```typescript -new Builder() - .withService('my-agent', '1.0.0') - .withCustomLogger({ - info: (msg, ...args) => myLoggingService.send('info', msg, args), - warn: (msg, ...args) => myLoggingService.send('warn', msg, args), - error: (msg, ...args) => { - myLoggingService.send('error', msg, args); - alertingService.notify(msg); // Alert on errors - }, - event: (name, success, duration) => { - myLoggingService.send(success ? 'event-success' : 'event-failure', `${name} in ${duration}ms`, []); - } - }) - .build(); -``` - -## Runtime Configuration - -Change logger at any point in your application: - -```typescript -import { setLogger } from '@microsoft/agents-a365-observability'; - -// Production: verbose logging -if (process.env.NODE_ENV === 'production') { - setLogger({ - info: (msg, ...args) => console.log(`[Prod-INFO] ${msg}`, ...args), - warn: (msg, ...args) => console.warn(`[Prod-WARN] ${msg}`, ...args), - error: (msg, ...args) => console.error(`[Prod-ERROR] ${msg}`, ...args), - event: (name, success, duration) => { - const status = success ? 'succeeded' : 'failed'; - console.log(`[Prod-EVENT] ${name} ${status} in ${duration}ms`); - } - }); -} - -// Development: selective logging (warn and error only) -if (process.env.NODE_ENV === 'development') { - setLogger({ - info: () => {}, - warn: (msg, ...args) => console.warn(`[Dev-WARN] ${msg}`, ...args), - error: (msg, ...args) => console.error(`[Dev-ERROR] ${msg}`, ...args), - event: (name, success, duration) => { - if (!success) { - console.warn(`[Dev-EVENT] ${name} failed in ${duration}ms`); - } - } - }); -} -``` - -You can also reset to the default logger: - -```typescript -import { resetLogger } from '@microsoft/agents-a365-observability'; - -// Reset to default logger (respects A365_OBSERVABILITY_LOG_LEVEL env var) -resetLogger(); -``` - -## What Gets Logged - -**Info:** Token resolution, export success, trace lifecycle -**Warn:** Dropped spans, max spans exceeded, buffer issues -**Error:** Export failures, auth failures, configuration errors -**Event:** Export operations (with success/failure status and duration) - -### Event Logging in Agent365Exporter - -The `Agent365Exporter` logs events at multiple levels using standardized event names from `ExporterEventNames`. - -## API Reference - -```typescript -// Interface -export interface ILogger { - info(message: string, ...args: unknown[]): void; - warn(message: string, ...args: unknown[]): void; - error(message: string, ...args: unknown[]): void; - event( - eventType: string, - isSuccess: boolean, - durationMs: number, - message?: string, - correlationId?: string - ): void; -} - -// Functions (internal use) -export function setLogger(customLogger: ILogger): void; -export function getLogger(): ILogger; -export function resetLogger(): void; - -// Builder method -builder.withCustomLogger(customLogger: ILogger): Builder; -``` - diff --git a/packages/agents-a365-observability/PER_REQUEST_EXPORT.md b/packages/agents-a365-observability/PER_REQUEST_EXPORT.md deleted file mode 100644 index 81c4786d..00000000 --- a/packages/agents-a365-observability/PER_REQUEST_EXPORT.md +++ /dev/null @@ -1,62 +0,0 @@ -# Per-request export (token via OpenTelemetry Context) - -This document explains how to enable **per-request export** for `@microsoft/agents-a365-observability` and how to provide an export token *per incoming request* using OpenTelemetry Context. - -## What it does - -When per-request export is enabled: - -- The SDK uses `PerRequestSpanProcessor` instead of OpenTelemetry’s `BatchSpanProcessor`. -- Spans are **buffered per trace** and exported when the request/trace completes. -- The Agent 365 exporter reads the auth token from the **active OpenTelemetry Context** at export time (set via `runWithExportToken`). - -This is useful when tokens are request-scoped (multi-tenant, delegated auth, per-user tokens, etc.). - -## Enable per-request export - -Set these environment variables: - -```bash -ENABLE_OBSERVABILITY=true -ENABLE_A365_OBSERVABILITY_EXPORTER=true -ENABLE_A365_OBSERVABILITY_PER_REQUEST_EXPORT=true -``` - -Then build/start observability as usual (the builder will pick the correct span processor automatically based on the env var). - -## Provide the token per request - -At your **request boundary** (e.g., Express route handler, bot adapter handler), acquire a token using your auth flow, then wrap the request handling code with `runWithExportToken(token, fn)`. - -Example (Express-style handler): - -```ts -import { runWithExportToken } from '@microsoft/agents-a365-observability'; - -app.post('/api/messages', async (req, res) => { - const token = await acquireTokenSomehow(req); // your auth - - await runWithExportToken(token, async () => { - // Any spans created in here (and async work chained from here) - // will export using this token. - await handleRequest(req, res); - }); -}); -``` - -Notes: - -- `runWithExportToken` uses OpenTelemetry Context (AsyncLocalStorage) so it must wrap the async call chain that creates the spans. -- In per-request export mode, the exporter will log an error if no token is available in context. - -## Guardrails (recommended) - -Per-request buffering can increase memory usage and cause export bursts during traffic spikes. `PerRequestSpanProcessor` includes guardrails that you can tune via env vars or via `PerRequestSpanProcessorConfiguration` overrides: - -- `A365_PER_REQUEST_MAX_CONCURRENT_EXPORTS` (default `20`): caps concurrent exports across requests/traces. -- `A365_PER_REQUEST_MAX_TRACES` (default `1000`): caps concurrently buffered traces. -- `A365_PER_REQUEST_MAX_SPANS_PER_TRACE` (default `5000`): caps buffered ended spans per trace. - -Set to `0` (or negative) to disable a specific guardrail. - -These settings live on `PerRequestSpanProcessorConfiguration` (which extends `ObservabilityConfiguration`) and are not exposed on the common `ObservabilityConfiguration` class. Use `defaultPerRequestSpanProcessorConfigurationProvider` to access these settings programmatically. \ No newline at end of file diff --git a/packages/agents-a365-observability/src/ObservabilityBuilder.ts b/packages/agents-a365-observability/src/ObservabilityBuilder.ts index 177bcd49..6ce6b9f0 100644 --- a/packages/agents-a365-observability/src/ObservabilityBuilder.ts +++ b/packages/agents-a365-observability/src/ObservabilityBuilder.ts @@ -13,8 +13,9 @@ import { PerRequestSpanProcessor } from './tracing/PerRequestSpanProcessor'; import { resourceFromAttributes } from '@opentelemetry/resources'; import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions'; import { trace } from '@opentelemetry/api'; -import { ClusterCategory } from '@microsoft/agents-a365-runtime'; -import logger, { setLogger, type ILogger } from './utils/logging'; +import { ClusterCategory, IConfigurationProvider } from '@microsoft/agents-a365-runtime'; +import logger, { setLogger, DefaultLogger, type ILogger } from './utils/logging'; +import type { ObservabilityConfiguration } from './configuration'; /** * Configuration options for Agent 365 Observability Builder */ @@ -42,6 +43,13 @@ export interface BuilderOptions { * Implement ILogger to integrate with other logging services */ customLogger?: ILogger; + + /** + * Optional configuration provider for ObservabilityConfiguration. + * When provided, this is used by the builder and its internal components + * (exporter, span processors, logger) + */ + configProvider?: IConfigurationProvider; } /** @@ -98,6 +106,18 @@ export class ObservabilityBuilder { return this; } + /** + * Configures the configuration provider for ObservabilityConfiguration. + * When set, this provider is used by the builder and its internal components + * instead of the default provider that reads from environment variables. + * @param configProvider The configuration provider + * @returns The builder instance for method chaining + */ + public withConfigurationProvider(configProvider: IConfigurationProvider): ObservabilityBuilder { + this.options.configProvider = configProvider; + return this; + } + /** * Sets a custom logger implementation for the observability SDK * @param customLogger The custom logger implementation (must implement ILogger interface) @@ -119,7 +139,7 @@ export class ObservabilityBuilder { } private createBatchProcessor(): BatchSpanProcessor { - if (!isAgent365ExporterEnabled()) { + if (!isAgent365ExporterEnabled(this.options.configProvider)) { logger.info('[ObservabilityBuilder] Agent 365 exporter not enabled. Using ConsoleSpanExporter for BatchSpanProcessor.'); return new BatchSpanProcessor(new ConsoleSpanExporter()); } @@ -132,7 +152,7 @@ export class ObservabilityBuilder { if (this.options.tokenResolver) { opts.tokenResolver = this.options.tokenResolver; } - return new BatchSpanProcessor(new Agent365Exporter(opts), { + return new BatchSpanProcessor(new Agent365Exporter(opts, this.options.configProvider), { maxQueueSize: opts.maxQueueSize, scheduledDelayMillis: opts.scheduledDelayMilliseconds, exportTimeoutMillis: opts.exporterTimeoutMilliseconds, @@ -141,7 +161,7 @@ export class ObservabilityBuilder { } private createPerRequestProcessor(): PerRequestSpanProcessor { - if (!isAgent365ExporterEnabled()) { + if (!isAgent365ExporterEnabled(this.options.configProvider)) { logger.info('[Agent365Exporter] Per-request export enabled but Agent 365 exporter is disabled. Using ConsoleSpanExporter.'); return new PerRequestSpanProcessor(new ConsoleSpanExporter()); } @@ -154,7 +174,7 @@ export class ObservabilityBuilder { // For per-request export, token is retrieved from OTel Context by Agent365Exporter // using getExportToken(), so no tokenResolver is needed here - return new PerRequestSpanProcessor(new Agent365Exporter(opts)); + return new PerRequestSpanProcessor(new Agent365Exporter(opts, this.options.configProvider)); } private createExportProcessor(): BatchSpanProcessor | PerRequestSpanProcessor { @@ -184,9 +204,11 @@ export class ObservabilityBuilder { return this.isBuilt; } - // Apply custom logger if provided + // Apply custom logger if provided, or configure logger with custom config provider if (this.options.customLogger) { setLogger(this.options.customLogger); + } else if (this.options.configProvider) { + setLogger(new DefaultLogger(this.options.configProvider)); } // Create processors in the desired order: diff --git a/packages/agents-a365-observability/src/ObservabilityManager.ts b/packages/agents-a365-observability/src/ObservabilityManager.ts index 8bc09dea..7b96c44d 100644 --- a/packages/agents-a365-observability/src/ObservabilityManager.ts +++ b/packages/agents-a365-observability/src/ObservabilityManager.ts @@ -47,6 +47,10 @@ export class ObservabilityManager { builder.withClusterCategory(options.clusterCategory); } + if (options?.configProvider) { + builder.withConfigurationProvider(options.configProvider); + } + builder.start(); ObservabilityManager.instance = builder; diff --git a/packages/agents-a365-observability/src/configuration/PerRequestSpanProcessorConfiguration.ts b/packages/agents-a365-observability/src/configuration/PerRequestSpanProcessorConfiguration.ts index 669b7fb8..6c843b7f 100644 --- a/packages/agents-a365-observability/src/configuration/PerRequestSpanProcessorConfiguration.ts +++ b/packages/agents-a365-observability/src/configuration/PerRequestSpanProcessorConfiguration.ts @@ -2,35 +2,44 @@ // Licensed under the MIT License. import { RuntimeConfiguration } from '@microsoft/agents-a365-runtime'; -import { ObservabilityConfiguration } from './ObservabilityConfiguration'; import { PerRequestSpanProcessorConfigurationOptions } from './PerRequestSpanProcessorConfigurationOptions'; +import { getPerRequestProcessorInternalOverrides } from '../internal/PerRequestProcessorInternalOverrides'; /** Guardrails to prevent unbounded memory growth / export bursts. Used for PerRequestSpanProcessor only. */ const DEFAULT_MAX_BUFFERED_TRACES = 1000; const DEFAULT_MAX_SPANS_PER_TRACE = 5000; const DEFAULT_MAX_CONCURRENT_EXPORTS = 20; +const DEFAULT_FLUSH_GRACE_MS = 250; +const DEFAULT_MAX_TRACE_AGE_MS = 30 * 60 * 1000; // 30 minutes /** * Configuration for PerRequestSpanProcessor. - * Inherits all observability and runtime settings, and adds per-request processor guardrails. + * Inherits runtime settings (clusterCategory, isNodeEnvDevelopment) and adds + * per-request processor guardrails. * * This is separated from ObservabilityConfiguration because PerRequestSpanProcessor * is used only in specific scenarios and these settings should not be exposed * in the common ObservabilityConfiguration. */ -export class PerRequestSpanProcessorConfiguration extends ObservabilityConfiguration { +export class PerRequestSpanProcessorConfiguration extends RuntimeConfiguration { protected get perRequestOverrides(): PerRequestSpanProcessorConfigurationOptions { - return this.overrides as PerRequestSpanProcessorConfigurationOptions; + const internal = getPerRequestProcessorInternalOverrides(); + const instanceOverrides = this.overrides as PerRequestSpanProcessorConfigurationOptions | undefined; + return { + ...(instanceOverrides ?? {}), + ...(internal?.isPerRequestExportEnabled !== undefined && { isPerRequestExportEnabled: internal.isPerRequestExportEnabled }), + ...(internal?.perRequestMaxTraces !== undefined && { perRequestMaxTraces: internal.perRequestMaxTraces }), + ...(internal?.perRequestMaxSpansPerTrace !== undefined && { perRequestMaxSpansPerTrace: internal.perRequestMaxSpansPerTrace }), + ...(internal?.perRequestMaxConcurrentExports !== undefined && { perRequestMaxConcurrentExports: internal.perRequestMaxConcurrentExports }), + ...(internal?.perRequestFlushGraceMs !== undefined && { perRequestFlushGraceMs: internal.perRequestFlushGraceMs }), + ...(internal?.perRequestMaxTraceAgeMs !== undefined && { perRequestMaxTraceAgeMs: internal.perRequestMaxTraceAgeMs }), + }; } constructor(overrides?: PerRequestSpanProcessorConfigurationOptions) { super(overrides); } - // Inherited: clusterCategory, isDevelopmentEnvironment, isNodeEnvDevelopment, - // observabilityAuthenticationScopes, isObservabilityExporterEnabled, - // useCustomDomainForObservability, observabilityDomainOverride, observabilityLogLevel - get isPerRequestExportEnabled(): boolean { const result = this.perRequestOverrides.isPerRequestExportEnabled?.(); if (result !== undefined) return result; @@ -54,4 +63,16 @@ export class PerRequestSpanProcessorConfiguration extends ObservabilityConfigura ?? RuntimeConfiguration.parseEnvInt(process.env.A365_PER_REQUEST_MAX_CONCURRENT_EXPORTS, DEFAULT_MAX_CONCURRENT_EXPORTS); return value > 0 ? value : DEFAULT_MAX_CONCURRENT_EXPORTS; } + + get perRequestFlushGraceMs(): number { + const value = this.perRequestOverrides.perRequestFlushGraceMs?.() + ?? RuntimeConfiguration.parseEnvInt(process.env.A365_PER_REQUEST_FLUSH_GRACE_MS, DEFAULT_FLUSH_GRACE_MS); + return value > 0 ? value : DEFAULT_FLUSH_GRACE_MS; + } + + get perRequestMaxTraceAgeMs(): number { + const value = this.perRequestOverrides.perRequestMaxTraceAgeMs?.() + ?? RuntimeConfiguration.parseEnvInt(process.env.A365_PER_REQUEST_MAX_TRACE_AGE_MS, DEFAULT_MAX_TRACE_AGE_MS); + return value > 0 ? value : DEFAULT_MAX_TRACE_AGE_MS; + } } diff --git a/packages/agents-a365-observability/src/configuration/PerRequestSpanProcessorConfigurationOptions.ts b/packages/agents-a365-observability/src/configuration/PerRequestSpanProcessorConfigurationOptions.ts index 4dd399e2..fc379e4a 100644 --- a/packages/agents-a365-observability/src/configuration/PerRequestSpanProcessorConfigurationOptions.ts +++ b/packages/agents-a365-observability/src/configuration/PerRequestSpanProcessorConfigurationOptions.ts @@ -1,20 +1,16 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { ObservabilityConfigurationOptions } from './ObservabilityConfigurationOptions'; +import { RuntimeConfigurationOptions } from '@microsoft/agents-a365-runtime'; /** - * Configuration options for PerRequestSpanProcessor - extends observability options. + * Configuration options for PerRequestSpanProcessor - extends runtime options. * All overrides are functions called on each property access. * - * Inherited from ObservabilityConfigurationOptions: - * - observabilityAuthenticationScopes, isObservabilityExporterEnabled, - * useCustomDomainForObservability, observabilityDomainOverride, observabilityLogLevel - * * Inherited from RuntimeConfigurationOptions: - * - clusterCategory, isDevelopmentEnvironment, isNodeEnvDevelopment + * - clusterCategory, isNodeEnvDevelopment */ -export type PerRequestSpanProcessorConfigurationOptions = ObservabilityConfigurationOptions & { +export type PerRequestSpanProcessorConfigurationOptions = RuntimeConfigurationOptions & { /** * Override to enable/disable per-request export mode. * When enabled, spans are buffered per-request and exported when the request completes, @@ -58,4 +54,26 @@ export type PerRequestSpanProcessorConfigurationOptions = ObservabilityConfigura * @default 20 */ perRequestMaxConcurrentExports?: () => number; + + /** + * Override for grace period (ms) to wait for child spans after root span ends. + * After the root span ends, the processor waits this long for remaining children + * before flushing the trace. Values <= 0 are ignored and the default is used. + * + * @returns Flush grace period in milliseconds. + * @envvar A365_PER_REQUEST_FLUSH_GRACE_MS + * @default 250 + */ + perRequestFlushGraceMs?: () => number; + + /** + * Override for maximum age (ms) for a trace before forcing flush. + * Traces older than this are flushed regardless of whether all spans have ended. + * Values <= 0 are ignored and the default is used. + * + * @returns Maximum trace age in milliseconds. + * @envvar A365_PER_REQUEST_MAX_TRACE_AGE_MS + * @default 1800000 (30 minutes) + */ + perRequestMaxTraceAgeMs?: () => number; }; diff --git a/packages/agents-a365-observability/src/index.ts b/packages/agents-a365-observability/src/index.ts index ea3a8d00..b86f9338 100644 --- a/packages/agents-a365-observability/src/index.ts +++ b/packages/agents-a365-observability/src/index.ts @@ -14,11 +14,20 @@ export { ExporterEventNames } from './tracing/exporter/ExporterEventNames'; export { BaggageBuilder, BaggageScope } from './tracing/middleware/BaggageBuilder'; // Per-request export utilities -export { runWithExportToken, getExportToken } from './tracing/context/token-context'; +export { runWithExportToken, updateExportToken, getExportToken } from './tracing/context/token-context'; // Parent span context utilities export { ParentSpanRef, runWithParentSpanRef, createContextWithParentSpanRef } from './tracing/context/parent-span-context'; +// Trace context propagation utilities (W3C traceparent/tracestate) +export { + HeadersCarrier, + ParentContext, + injectTraceContext, + extractTraceContext, + runWithExtractedTraceContext +} from './tracing/context/trace-context-propagation'; + // Contracts and interfaces export { ExecutionType, @@ -34,14 +43,16 @@ export { ServiceEndpoint, InferenceDetails, InferenceOperationType, - InferenceResponse + InferenceResponse, + OutputResponse } from './tracing/contracts'; // Scopes export { OpenTelemetryScope } from './tracing/scopes/OpenTelemetryScope'; export { ExecuteToolScope } from './tracing/scopes/ExecuteToolScope'; export { InvokeAgentScope } from './tracing/scopes/InvokeAgentScope'; -export { InferenceScope} from './tracing/scopes/InferenceScope'; +export { InferenceScope } from './tracing/scopes/InferenceScope'; +export { OutputScope } from './tracing/scopes/OutputScope'; export { logger, setLogger, getLogger, resetLogger, formatError } from './utils/logging'; export type { ILogger } from './utils/logging'; diff --git a/packages/agents-a365-observability/src/internal/PerRequestProcessorInternalOverrides.ts b/packages/agents-a365-observability/src/internal/PerRequestProcessorInternalOverrides.ts new file mode 100644 index 00000000..7af19089 --- /dev/null +++ b/packages/agents-a365-observability/src/internal/PerRequestProcessorInternalOverrides.ts @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import type { PerRequestSpanProcessorConfigurationOptions } from '../configuration/PerRequestSpanProcessorConfigurationOptions'; + +let overrides: PerRequestSpanProcessorConfigurationOptions | undefined; + +// Only for tests / internal usage +export function setPerRequestProcessorInternalOverrides(value?: PerRequestSpanProcessorConfigurationOptions) { + overrides = value; +} + +export function getPerRequestProcessorInternalOverrides(): PerRequestSpanProcessorConfigurationOptions | undefined { + return overrides; +} diff --git a/packages/agents-a365-observability/src/tracing/PerRequestSpanProcessor.ts b/packages/agents-a365-observability/src/tracing/PerRequestSpanProcessor.ts index da170fa1..6e5d005f 100644 --- a/packages/agents-a365-observability/src/tracing/PerRequestSpanProcessor.ts +++ b/packages/agents-a365-observability/src/tracing/PerRequestSpanProcessor.ts @@ -9,11 +9,7 @@ import { IConfigurationProvider } from '@microsoft/agents-a365-runtime'; import logger from '../utils/logging'; import { PerRequestSpanProcessorConfiguration, defaultPerRequestSpanProcessorConfigurationProvider } from '../configuration'; -/** Default grace period (ms) to wait for child spans after root span ends */ -const DEFAULT_FLUSH_GRACE_MS = 250; -/** Default maximum age (ms) for a trace before forcing flush */ -const DEFAULT_MAX_TRACE_AGE_MS = 30 * 60 * 1000; // 30 minutes function isRootSpan(span: ReadableSpan): boolean { return !span.parentSpanContext; @@ -44,6 +40,8 @@ export class PerRequestSpanProcessor implements SpanProcessor { private readonly maxBufferedTraces: number; private readonly maxSpansPerTrace: number; private readonly maxConcurrentExports: number; + private readonly flushGraceMs: number; + private readonly maxTraceAgeMs: number; private inFlightExports = 0; private exportWaiters: Array<() => void> = []; @@ -51,23 +49,19 @@ export class PerRequestSpanProcessor implements SpanProcessor { /** * Construct a PerRequestSpanProcessor. * @param exporter The span exporter to use. - * @param flushGraceMs Grace period (ms) to wait for child spans after root span ends. - * @param maxTraceAgeMs Maximum age (ms) for a trace before forcing flush. * @param configProvider Optional configuration provider. Defaults to defaultPerRequestSpanProcessorConfigurationProvider if not specified. */ constructor( private readonly exporter: SpanExporter, - private readonly flushGraceMs: number = DEFAULT_FLUSH_GRACE_MS, - private readonly maxTraceAgeMs: number = DEFAULT_MAX_TRACE_AGE_MS, configProvider?: IConfigurationProvider ) { - // Defaults are intentionally high but bounded; override via configuration if needed. - // Set to 0 (or negative) to disable a guardrail. const effectiveConfigProvider = configProvider ?? defaultPerRequestSpanProcessorConfigurationProvider; const config = effectiveConfigProvider.getConfiguration(); this.maxBufferedTraces = config.perRequestMaxTraces; this.maxSpansPerTrace = config.perRequestMaxSpansPerTrace; this.maxConcurrentExports = config.perRequestMaxConcurrentExports; + this.flushGraceMs = config.perRequestFlushGraceMs; + this.maxTraceAgeMs = config.perRequestMaxTraceAgeMs; } onStart(span: ReadableSpan, ctx: Context): void { diff --git a/packages/agents-a365-observability/src/tracing/constants.ts b/packages/agents-a365-observability/src/tracing/constants.ts index 02edfb85..c7e88b19 100644 --- a/packages/agents-a365-observability/src/tracing/constants.ts +++ b/packages/agents-a365-observability/src/tracing/constants.ts @@ -9,6 +9,7 @@ export class OpenTelemetryConstants { // Span operation names public static readonly INVOKE_AGENT_OPERATION_NAME = 'invoke_agent'; public static readonly EXECUTE_TOOL_OPERATION_NAME = 'execute_tool'; + public static readonly OUTPUT_MESSAGES_OPERATION_NAME = 'output_messages'; public static readonly CHAT_OPERATION_NAME = 'chat'; // OpenTelemetry semantic conventions diff --git a/packages/agents-a365-observability/src/tracing/context/parent-span-context.ts b/packages/agents-a365-observability/src/tracing/context/parent-span-context.ts index 19d5b9f3..d59ebfdf 100644 --- a/packages/agents-a365-observability/src/tracing/context/parent-span-context.ts +++ b/packages/agents-a365-observability/src/tracing/context/parent-span-context.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { context, trace, Context, SpanContext, TraceFlags } from '@opentelemetry/api'; +import { context, trace, Context, SpanContext, TraceFlags, TraceState } from '@opentelemetry/api'; import logger from '../../utils/logging'; /** @@ -19,9 +19,20 @@ export interface ParentSpanRef { */ spanId: string; - /** * Optional trace flags + /** + * Optional trace flags + */ + traceFlags?: TraceFlags; + + /** + * Optional W3C trace state for vendor-specific trace context data + */ + traceState?: TraceState; + + /** + * Whether the span originated from a remote service (default: true) */ - traceFlags?: TraceFlags; + isRemote?: boolean; } function isValidTraceId(traceId: string): boolean { @@ -50,17 +61,23 @@ export function createContextWithParentSpanRef(base: Context, parent: ParentSpan return base; } - // If the base context already has an active span for the same trace, reuse its traceFlags. - // Otherwise, default to NONE to avoid up-sampling. - const active = trace.getSpan(base)?.spanContext(); - const derivedFlags = active?.traceId === parent.traceId ? active.traceFlags : undefined; - const traceFlags = derivedFlags ?? TraceFlags.NONE; + // Determine traceFlags: + // 1. Use parent.traceFlags if explicitly provided + // 2. Inherit from active span if its traceId matches + // 3. Default to SAMPLED — manually instrumented spans should always be captured + const activeCtx = trace.getSpan(base)?.spanContext(); + const traceFlags = + parent.traceFlags + ?? (activeCtx?.traceId === parent.traceId ? activeCtx.traceFlags : undefined) + ?? TraceFlags.SAMPLED; // Create a SpanContext from the parent reference const parentSpanContext: SpanContext = { traceId: parent.traceId, spanId: parent.spanId, traceFlags, + traceState: parent.traceState, + isRemote: parent.isRemote ?? true, }; // Create a non-recording span with the parent context diff --git a/packages/agents-a365-observability/src/tracing/context/token-context.ts b/packages/agents-a365-observability/src/tracing/context/token-context.ts index 3fc034a9..f8521f6e 100644 --- a/packages/agents-a365-observability/src/tracing/context/token-context.ts +++ b/packages/agents-a365-observability/src/tracing/context/token-context.ts @@ -7,19 +7,61 @@ import logger from '../../utils/logging'; const EXPORT_TOKEN_KEY = createContextKey('a365_export_token'); +/** + * Mutable holder stored in Context so the token can be refreshed + * after the context is created (OTel contexts are immutable, but + * the object reference stays the same). + */ +interface TokenHolder { + token: string; +} + /** * Run a function within a Context that carries the per-request export token. * This keeps the token only in OTel Context (ALS), never in any registry. + * + * The token can be updated later via `updateExportToken()` before the trace + * is flushed — useful when the callback is long-running and the original + * token may expire before export. */ export function runWithExportToken(token: string, fn: () => T): T { - const ctxWithToken = context.active().setValue(EXPORT_TOKEN_KEY, token); + const holder: TokenHolder = { token }; + const ctxWithToken = context.active().setValue(EXPORT_TOKEN_KEY, holder); logger.info('[TokenContext] Running function with export token in context.'); return context.with(ctxWithToken, fn); } +/** + * Update the export token in the active OTel Context. + * Call this to refresh the token before ending the root span when the + * original token may have expired during a long-running request. + * + * Must be called within the same async context created by `runWithExportToken`. + * @param token The fresh token to use for export. + * @returns true if the token was updated successfully, false if no token holder was found. + */ +export function updateExportToken(token: string): boolean { + const value = context.active().getValue(EXPORT_TOKEN_KEY); + if (value && typeof value === 'object' && 'token' in value) { + (value as TokenHolder).token = token; + logger.info('[TokenContext] Export token updated in context.'); + return true; + } + logger.warn('[TokenContext] updateExportToken called but no token holder found in active context. Was runWithExportToken called?'); + return false; +} + /** * Retrieve the per-request export token from a given OTel Context (or the active one). */ export function getExportToken(ctx: Context = context.active()): string | undefined { - return ctx.getValue(EXPORT_TOKEN_KEY) as string | undefined; + const value = ctx.getValue(EXPORT_TOKEN_KEY); + if (value && typeof value === 'object' && 'token' in value) { + return (value as TokenHolder).token; + } + // Backward compat: support raw string values from older callers + if (typeof value === 'string') { + return value; + } + return undefined; } diff --git a/packages/agents-a365-observability/src/tracing/context/trace-context-propagation.ts b/packages/agents-a365-observability/src/tracing/context/trace-context-propagation.ts new file mode 100644 index 00000000..392e48d7 --- /dev/null +++ b/packages/agents-a365-observability/src/tracing/context/trace-context-propagation.ts @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { context, propagation, Context } from '@opentelemetry/api'; +import { ParentSpanRef } from './parent-span-context'; + +/** + * Carrier type for HTTP headers used in trace context propagation. + * Compatible with Node.js IncomingHttpHeaders and plain string maps. + */ +export type HeadersCarrier = Record; + +/** + * A parent context for span creation. Accepts either: + * - {@link ParentSpanRef}: explicit traceId/spanId pair (manual approach) + * - {@link Context}: an OTel Context, typically from {@link extractTraceContext} or `propagation.extract()` + */ +export type ParentContext = ParentSpanRef | Context; + +/** + * Type guard to distinguish a {@link ParentSpanRef} from an OTel {@link Context}. + */ +export function isParentSpanRef(value: ParentContext): value is ParentSpanRef { + if (typeof value !== 'object' || value === null) return false; + + const maybeCtx = value as Context; + if ( + typeof maybeCtx.getValue === 'function' && + typeof maybeCtx.setValue === 'function' && + typeof maybeCtx.deleteValue === 'function' + ) { + return false; + } + + const maybeRef = value as ParentSpanRef; + return ( + 'traceId' in maybeRef && + typeof maybeRef.traceId === 'string' && + 'spanId' in maybeRef && + typeof maybeRef.spanId === 'string' + ); +} + +/** + * Injects the current trace context (`traceparent`/`tracestate` headers) into + * the provided headers object using the globally registered W3C propagator. + * + * @param headers Mutable object where trace context headers will be written. + * @param ctx Optional OTel Context to inject from. Defaults to the active context. + * @returns The same `headers` object, for chaining convenience. + * + * @example + * ```typescript + * const headers: Record = {}; + * injectTraceContext(headers); + * await fetch('http://service-b/process', { headers }); + * ``` + */ +export function injectTraceContext( + headers: Record, + ctx?: Context +): Record { + propagation.inject(ctx ?? context.active(), headers); + return headers; +} + +/** + * Extracts trace context from incoming HTTP headers using the globally + * registered W3C propagator. Returns an OTel {@link Context} that can be + * passed to scope classes as a {@link ParentContext}. + * + * @param headers The incoming HTTP request headers containing `traceparent`/`tracestate`. + * @param baseCtx Optional base context to extend. Defaults to the active context. + * @returns An OTel Context containing the extracted trace information. + * + * @example + * ```typescript + * const parentCtx = extractTraceContext(req.headers); + * const scope = InvokeAgentScope.start(details, tenantDetails, undefined, undefined, parentCtx); + * ``` + */ +export function extractTraceContext( + headers: HeadersCarrier, + baseCtx?: Context +): Context { + return propagation.extract(baseCtx ?? context.active(), headers); +} + +/** + * Extracts trace context from incoming HTTP headers and runs the callback + * within that context. Any spans created inside the callback will be + * parented to the extracted trace. + * + * @param headers The incoming HTTP request headers containing `traceparent`/`tracestate`. + * @param callback The function to execute within the extracted context. + * @returns The result of the callback. + * + * @example + * ```typescript + * runWithExtractedTraceContext(req.headers, () => { + * const scope = InvokeAgentScope.start(details, tenantDetails); + * scope.dispose(); + * }); + * ``` + */ +export function runWithExtractedTraceContext(headers: HeadersCarrier, callback: () => T): T { + return context.with(extractTraceContext(headers), callback); +} diff --git a/packages/agents-a365-observability/src/tracing/contracts.ts b/packages/agents-a365-observability/src/tracing/contracts.ts index d67521ed..bef4b494 100644 --- a/packages/agents-a365-observability/src/tracing/contracts.ts +++ b/packages/agents-a365-observability/src/tracing/contracts.ts @@ -258,3 +258,12 @@ export interface InferenceResponse { outputTokens?: number; } + +/** + * Represents a response containing output messages from an agent. + * Used with OutputScope for output message tracing. + */ +export interface OutputResponse { + /** The output messages from the agent */ + messages: string[]; +} diff --git a/packages/agents-a365-observability/src/tracing/exporter/Agent365Exporter.ts b/packages/agents-a365-observability/src/tracing/exporter/Agent365Exporter.ts index a7e22e90..1083f83c 100644 --- a/packages/agents-a365-observability/src/tracing/exporter/Agent365Exporter.ts +++ b/packages/agents-a365-observability/src/tracing/exporter/Agent365Exporter.ts @@ -6,7 +6,8 @@ import { ExportResult, ExportResultCode } from '@opentelemetry/core'; import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'; -import { PowerPlatformApiDiscovery, ClusterCategory } from '@microsoft/agents-a365-runtime'; +import { PowerPlatformApiDiscovery, ClusterCategory, IConfigurationProvider } from '@microsoft/agents-a365-runtime'; +import type { ObservabilityConfiguration } from '../../configuration'; import { partitionByIdentity, parseIdentityKey, @@ -88,12 +89,15 @@ interface OTLPStatus { export class Agent365Exporter implements SpanExporter { private closed = false; private readonly options: Agent365ExporterOptions; + private readonly configProvider?: IConfigurationProvider; /** * Initialize exporter with a fully constructed options instance. - * If tokenResolver is missing, installs cache-backed resolver. + * @param options Exporter options controlling batching, timeouts, token acquisition and endpoint shape. + * @param configProvider Optional configuration provider. When supplied, the exporter uses it for + * configuration lookups (custom domain, domain override) instead of the default env-based provider. */ - constructor(options: Agent365ExporterOptions) { + constructor(options: Agent365ExporterOptions, configProvider?: IConfigurationProvider) { if (!options) { throw new Error('Agent365ExporterOptions must be provided (was null/undefined)'); } @@ -102,6 +106,7 @@ export class Agent365Exporter implements SpanExporter { throw new Error('Agent365Exporter tokenResolver must be provided for batch export'); } this.options = options; + this.configProvider = configProvider; } /** @@ -165,7 +170,7 @@ export class Agent365Exporter implements SpanExporter { const payload = this.buildExportRequest(spans); const body = JSON.stringify(payload); - const usingCustomServiceEndpoint = useCustomDomainForObservability(); + const usingCustomServiceEndpoint = useCustomDomainForObservability(this.configProvider); // Select endpoint path based on S2S flag const endpointRelativePath = this.options.useS2SEndpoint @@ -173,7 +178,7 @@ export class Agent365Exporter implements SpanExporter { : `/maven/agent365/agents/${agentId}/traces`; let url: string; - const domainOverride = getAgent365ObservabilityDomainOverride(); + const domainOverride = getAgent365ObservabilityDomainOverride(this.configProvider); if (domainOverride) { url = `${domainOverride}${endpointRelativePath}?api-version=1`; } else if (usingCustomServiceEndpoint) { @@ -220,7 +225,7 @@ export class Agent365Exporter implements SpanExporter { } else { const skipReason = tokenNotResolvedReason || 'Token not resolved for export request'; - logger.event(`${ExporterEventNames.EXPORT_GROUP}-${tenantId}-${agentId}`, false, 0, `skip exporting: ${skipReason}`); + logger.event(ExporterEventNames.EXPORT_GROUP, false, 0, `skip exporting: ${skipReason}`, { tenantId, agentId }); return; } @@ -233,10 +238,10 @@ export class Agent365Exporter implements SpanExporter { const { ok, correlationId } = await this.postWithRetries(url, body, headers); const duration = Date.now() - startTime; if (!ok) { - logger.event(`${ExporterEventNames.EXPORT_GROUP}-${tenantId}-${agentId}`, false, duration, undefined, correlationId); + logger.event(ExporterEventNames.EXPORT_GROUP, false, duration, undefined, { tenantId, agentId, correlationId }); throw new Error('Failed to export spans'); } - logger.event(`${ExporterEventNames.EXPORT_GROUP}-${tenantId}-${agentId}`, true, duration, 'Spans exported successfully', correlationId); + logger.event(ExporterEventNames.EXPORT_GROUP, true, duration, 'Spans exported successfully', { tenantId, agentId, correlationId }); } /** diff --git a/packages/agents-a365-observability/src/tracing/exporter/ExporterEventNames.ts b/packages/agents-a365-observability/src/tracing/exporter/ExporterEventNames.ts index 530e6394..35cc202d 100644 --- a/packages/agents-a365-observability/src/tracing/exporter/ExporterEventNames.ts +++ b/packages/agents-a365-observability/src/tracing/exporter/ExporterEventNames.ts @@ -2,22 +2,23 @@ // Licensed under the MIT License. /** - * Event names used by Agent365Exporter for logging and monitoring + * Event names used by Agent365Exporter for logging and monitoring. + * These are low-cardinality event types to ensure efficient monitoring and aggregation. */ -export const ExporterEventNames = { +export enum ExporterEventNames { /** * Overall export operation event - logs the entire batch export success/failure and duration */ - EXPORT: 'agent365-export', + EXPORT = 'agent365-export', /** - * Group export operation event - logs individual tenant/agent group export success/failure and duration - * Use with template: `export-group-${tenantId}-${agentId}` + * Group export operation event - logs individual tenant/agent group export success/failure and duration. + * Contextual information (tenantId, agentId, correlationId) should be passed in the details parameter. */ - EXPORT_GROUP: 'export-group', + EXPORT_GROUP = 'export-group', /** - * Tracked spans being skipped due to missing tenant or agent ID. Before export event, spans are partitioned by identity (tenant or agent ID) first. + * Tracked spans being skipped due to missing tenant or agent ID. Before export event, spans are partitioned by identity (tenant or agent ID) first. */ - EXPORT_PARTITION_SPAN_MISSING_IDENTITY: 'export-partition-span-missing-identity' -} as const; + EXPORT_PARTITION_SPAN_MISSING_IDENTITY = 'export-partition-span-missing-identity' +} diff --git a/packages/agents-a365-observability/src/tracing/exporter/utils.ts b/packages/agents-a365-observability/src/tracing/exporter/utils.ts index 19694241..5ba74953 100644 --- a/packages/agents-a365-observability/src/tracing/exporter/utils.ts +++ b/packages/agents-a365-observability/src/tracing/exporter/utils.ts @@ -14,6 +14,7 @@ import { PerRequestSpanProcessorConfiguration, defaultPerRequestSpanProcessorConfigurationProvider } from '../../configuration'; +import { getPerRequestProcessorInternalOverrides } from '../../internal/PerRequestProcessorInternalOverrides'; /** * Convert trace ID to hex string format @@ -133,7 +134,8 @@ export function isAgent365ExporterEnabled( } /** - * Check if per-request export is enabled via environment variable. + * Check if per-request export is enabled. + * Precedence: internal overrides > configuration provider > environment variable. * When enabled, the PerRequestSpanProcessor is used instead of BatchSpanProcessor. * The token is passed via OTel Context (async local storage) at export time. * @param configProvider Optional configuration provider. Defaults to defaultPerRequestSpanProcessorConfigurationProvider if not specified. @@ -141,6 +143,13 @@ export function isAgent365ExporterEnabled( export function isPerRequestExportEnabled( configProvider?: IConfigurationProvider ): boolean { + const overrides = getPerRequestProcessorInternalOverrides(); + const overrideValue = overrides?.isPerRequestExportEnabled?.(); + if (typeof overrideValue === 'boolean') { + logger.info(`[Agent365Exporter] Per-request export enabled (internal override): ${overrideValue}`); + return overrideValue; + } + const provider = configProvider ?? defaultPerRequestSpanProcessorConfigurationProvider; const enabled = provider.getConfiguration().isPerRequestExportEnabled; logger.info(`[Agent365Exporter] Per-request export enabled: ${enabled}`); diff --git a/packages/agents-a365-observability/src/tracing/scopes/ExecuteToolScope.ts b/packages/agents-a365-observability/src/tracing/scopes/ExecuteToolScope.ts index 1281d07e..ed2c3a48 100644 --- a/packages/agents-a365-observability/src/tracing/scopes/ExecuteToolScope.ts +++ b/packages/agents-a365-observability/src/tracing/scopes/ExecuteToolScope.ts @@ -1,11 +1,10 @@ -// ------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// ------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -import { SpanKind } from '@opentelemetry/api'; +import { SpanKind, TimeInput } from '@opentelemetry/api'; import { OpenTelemetryScope } from './OpenTelemetryScope'; import { ToolCallDetails, AgentDetails, TenantDetails, SourceMetadata } from '../contracts'; -import { ParentSpanRef } from '../context/parent-span-context'; +import { ParentContext } from '../context/trace-context-propagation'; import { OpenTelemetryConstants } from '../constants'; /** @@ -19,7 +18,12 @@ export class ExecuteToolScope extends OpenTelemetryScope { * @param tenantDetails The tenant details * @param conversationId Optional conversation id to tag on the span (`gen_ai.conversation.id`). * @param sourceMetadata Optional source metadata; only `name` (channel name) and `description` (channel link/URL) are used for tagging. - * @param parentSpanRef Optional explicit parent span reference for cross-async-boundary tracing. + * @param parentContext Optional parent context for cross-async-boundary tracing. + * Accepts a ParentSpanRef (manual traceId/spanId) or an OTel Context (e.g. from extractTraceContext). + * @param startTime Optional explicit start time (ms epoch, Date, or HrTime). Useful when recording a + * tool call after execution has already completed. + * @param endTime Optional explicit end time (ms epoch, Date, or HrTime). When provided, the span will + * use this timestamp when disposed instead of the current wall-clock time. * @returns A new ExecuteToolScope instance. */ public static start( @@ -28,9 +32,11 @@ export class ExecuteToolScope extends OpenTelemetryScope { tenantDetails: TenantDetails, conversationId?: string, sourceMetadata?: Pick, - parentSpanRef?: ParentSpanRef + parentContext?: ParentContext, + startTime?: TimeInput, + endTime?: TimeInput ): ExecuteToolScope { - return new ExecuteToolScope(details, agentDetails, tenantDetails, conversationId, sourceMetadata, parentSpanRef); + return new ExecuteToolScope(details, agentDetails, tenantDetails, conversationId, sourceMetadata, parentContext, startTime, endTime); } private constructor( @@ -39,7 +45,9 @@ export class ExecuteToolScope extends OpenTelemetryScope { tenantDetails: TenantDetails, conversationId?: string, sourceMetadata?: Pick, - parentSpanRef?: ParentSpanRef + parentContext?: ParentContext, + startTime?: TimeInput, + endTime?: TimeInput ) { super( SpanKind.INTERNAL, @@ -47,7 +55,9 @@ export class ExecuteToolScope extends OpenTelemetryScope { `${OpenTelemetryConstants.EXECUTE_TOOL_OPERATION_NAME} ${details.toolName}`, agentDetails, tenantDetails, - parentSpanRef + parentContext, + startTime, + endTime ); // Destructure the details object to match C# pattern diff --git a/packages/agents-a365-observability/src/tracing/scopes/InferenceScope.ts b/packages/agents-a365-observability/src/tracing/scopes/InferenceScope.ts index 0f1827b2..3973cc91 100644 --- a/packages/agents-a365-observability/src/tracing/scopes/InferenceScope.ts +++ b/packages/agents-a365-observability/src/tracing/scopes/InferenceScope.ts @@ -1,8 +1,7 @@ -// ------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// ------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -import { SpanKind } from '@opentelemetry/api'; +import { SpanKind, TimeInput } from '@opentelemetry/api'; import { OpenTelemetryScope } from './OpenTelemetryScope'; import { OpenTelemetryConstants } from '../constants'; import { @@ -11,7 +10,7 @@ import { TenantDetails, SourceMetadata } from '../contracts'; -import { ParentSpanRef } from '../context/parent-span-context'; +import { ParentContext } from '../context/trace-context-propagation'; /** * Provides OpenTelemetry tracing scope for generative AI inference operations. @@ -24,7 +23,10 @@ export class InferenceScope extends OpenTelemetryScope { * @param tenantDetails The tenant details * @param conversationId Optional conversation id to tag on the span (`gen_ai.conversation.id`). * @param sourceMetadata Optional source metadata; only `name` (channel name) and `description` (channel link/URL) are used for tagging. - * @param parentSpanRef Optional explicit parent span reference for cross-async-boundary tracing. + * @param parentContext Optional parent context for cross-async-boundary tracing. + * Accepts a ParentSpanRef (manual traceId/spanId) or an OTel Context (e.g. from extractTraceContext). + * @param startTime Optional explicit start time (ms epoch, Date, or HrTime). + * @param endTime Optional explicit end time (ms epoch, Date, or HrTime). * @returns A new InferenceScope instance */ public static start( @@ -33,9 +35,11 @@ export class InferenceScope extends OpenTelemetryScope { tenantDetails: TenantDetails, conversationId?: string, sourceMetadata?: Pick, - parentSpanRef?: ParentSpanRef + parentContext?: ParentContext, + startTime?: TimeInput, + endTime?: TimeInput ): InferenceScope { - return new InferenceScope(details, agentDetails, tenantDetails, conversationId, sourceMetadata, parentSpanRef); + return new InferenceScope(details, agentDetails, tenantDetails, conversationId, sourceMetadata, parentContext, startTime, endTime); } private constructor( @@ -44,7 +48,9 @@ export class InferenceScope extends OpenTelemetryScope { tenantDetails: TenantDetails, conversationId?: string, sourceMetadata?: Pick, - parentSpanRef?: ParentSpanRef + parentContext?: ParentContext, + startTime?: TimeInput, + endTime?: TimeInput ) { super( SpanKind.CLIENT, @@ -52,7 +58,9 @@ export class InferenceScope extends OpenTelemetryScope { `${details.operationName} ${details.model}`, agentDetails, tenantDetails, - parentSpanRef + parentContext, + startTime, + endTime ); // Set core inference information matching C# implementation diff --git a/packages/agents-a365-observability/src/tracing/scopes/InvokeAgentScope.ts b/packages/agents-a365-observability/src/tracing/scopes/InvokeAgentScope.ts index 9f81fcaa..cc161466 100644 --- a/packages/agents-a365-observability/src/tracing/scopes/InvokeAgentScope.ts +++ b/packages/agents-a365-observability/src/tracing/scopes/InvokeAgentScope.ts @@ -1,8 +1,7 @@ -// ------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// ------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -import { SpanKind } from '@opentelemetry/api'; +import { SpanKind, TimeInput } from '@opentelemetry/api'; import { OpenTelemetryScope } from './OpenTelemetryScope'; import { InvokeAgentDetails, @@ -10,7 +9,7 @@ import { CallerDetails, AgentDetails } from '../contracts'; -import { ParentSpanRef } from '../context/parent-span-context'; +import { ParentContext } from '../context/trace-context-propagation'; import { OpenTelemetryConstants } from '../constants'; /** @@ -23,7 +22,10 @@ export class InvokeAgentScope extends OpenTelemetryScope { * @param tenantDetails The tenant details. * @param callerAgentDetails The details of the caller agent. * @param callerDetails The details of the non-agentic caller. - * @param parentSpanRef Optional explicit parent span reference for cross-async-boundary tracing. + * @param parentContext Optional parent context for cross-async-boundary tracing. + * Accepts a ParentSpanRef (manual traceId/spanId) or an OTel Context (e.g. from extractTraceContext). + * @param startTime Optional explicit start time (ms epoch, Date, or HrTime). + * @param endTime Optional explicit end time (ms epoch, Date, or HrTime). * @returns A new InvokeAgentScope instance. */ public static start( @@ -31,9 +33,11 @@ export class InvokeAgentScope extends OpenTelemetryScope { tenantDetails: TenantDetails, callerAgentDetails?: AgentDetails, callerDetails?: CallerDetails, - parentSpanRef?: ParentSpanRef + parentContext?: ParentContext, + startTime?: TimeInput, + endTime?: TimeInput ): InvokeAgentScope { - return new InvokeAgentScope(invokeAgentDetails, tenantDetails, callerAgentDetails, callerDetails, parentSpanRef); + return new InvokeAgentScope(invokeAgentDetails, tenantDetails, callerAgentDetails, callerDetails, parentContext, startTime, endTime); } private constructor( @@ -41,7 +45,9 @@ export class InvokeAgentScope extends OpenTelemetryScope { tenantDetails: TenantDetails, callerAgentDetails?: AgentDetails, callerDetails?: CallerDetails, - parentSpanRef?: ParentSpanRef + parentContext?: ParentContext, + startTime?: TimeInput, + endTime?: TimeInput ) { super( SpanKind.CLIENT, @@ -51,7 +57,9 @@ export class InvokeAgentScope extends OpenTelemetryScope { : OpenTelemetryConstants.INVOKE_AGENT_OPERATION_NAME, invokeAgentDetails, tenantDetails, - parentSpanRef + parentContext, + startTime, + endTime ); // Set session ID and endpoint information diff --git a/packages/agents-a365-observability/src/tracing/scopes/OpenTelemetryScope.ts b/packages/agents-a365-observability/src/tracing/scopes/OpenTelemetryScope.ts index d8b71300..b539af14 100644 --- a/packages/agents-a365-observability/src/tracing/scopes/OpenTelemetryScope.ts +++ b/packages/agents-a365-observability/src/tracing/scopes/OpenTelemetryScope.ts @@ -1,11 +1,11 @@ -// ------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// ------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -import { trace, SpanKind, Span, SpanStatusCode, Attributes, context, AttributeValue, SpanContext } from '@opentelemetry/api'; +import { trace, SpanKind, Span, SpanStatusCode, Attributes, context, AttributeValue, SpanContext, TimeInput } from '@opentelemetry/api'; import { OpenTelemetryConstants } from '../constants'; import { AgentDetails, TenantDetails } from '../contracts'; -import { ParentSpanRef, createContextWithParentSpanRef } from '../context/parent-span-context'; +import { createContextWithParentSpanRef } from '../context/parent-span-context'; +import { ParentContext, isParentSpanRef } from '../context/trace-context-propagation'; import logger from '../../utils/logging'; /** @@ -15,7 +15,9 @@ export abstract class OpenTelemetryScope implements Disposable { private static readonly tracer = trace.getTracer(OpenTelemetryConstants.SOURCE_NAME); protected readonly span: Span; - private readonly startTime: number; + private readonly wallClockStartMs: number; + private customStartTime?: TimeInput; + private customEndTime?: TimeInput; private errorType?: string; private exception?: Error; private hasEnded = false; @@ -27,7 +29,14 @@ export abstract class OpenTelemetryScope implements Disposable { * @param spanName The name of the span for display purposes * @param agentDetails Optional agent details * @param tenantDetails Optional tenant details - * @param parentSpanRef Optional explicit parent span reference for cross-async-boundary tracing + * @param parentContext Optional parent context for cross-async-boundary tracing. + * Accepts a {@link ParentSpanRef} (manual traceId/spanId) or an OTel {@link Context} + * (e.g. from {@link extractTraceContext} for W3C header propagation). + * @param startTime Optional explicit start time (ms epoch, Date, or HrTime). When provided the span + * records this timestamp instead of "now", which is useful when recording an operation after it + * has already completed (e.g. a tool call whose start time was captured earlier). + * @param endTime Optional explicit end time (ms epoch, Date, or HrTime). When provided the span will + * use this timestamp when {@link dispose} is called instead of the current wall-clock time. */ protected constructor( kind: SpanKind, @@ -35,13 +44,21 @@ export abstract class OpenTelemetryScope implements Disposable { spanName: string, agentDetails?: AgentDetails, tenantDetails?: TenantDetails, - parentSpanRef?: ParentSpanRef + parentContext?: ParentContext, + startTime?: TimeInput, + endTime?: TimeInput ) { // Determine the context to use for span creation let currentContext = context.active(); - if (parentSpanRef) { - currentContext = createContextWithParentSpanRef(currentContext, parentSpanRef); - logger.info(`[A365Observability] Using explicit parent span: traceId=${parentSpanRef.traceId}, spanId=${parentSpanRef.spanId}`); + if (parentContext) { + if (isParentSpanRef(parentContext)) { + // Existing ParentSpanRef path (backward compatible) + currentContext = createContextWithParentSpanRef(currentContext, parentContext); + logger.info(`[A365Observability] Using explicit parent span: traceId=${parentContext.traceId}, spanId=${parentContext.spanId}`); + } else { + // OTel Context path (from extractTraceContext or propagation.extract) + currentContext = parentContext; + } } logger.info(`[A365Observability] Starting span: ${spanName}, operation: ${operationName} for tenantId: ${tenantDetails?.tenantId || 'unknown'}, agentId: ${agentDetails?.agentId || 'unknown'}`); @@ -49,6 +66,7 @@ export abstract class OpenTelemetryScope implements Disposable { // Start span with current context to establish parent-child relationship this.span = OpenTelemetryScope.tracer.startSpan(spanName, { kind, + startTime, attributes: { [OpenTelemetryConstants.GEN_AI_SYSTEM_KEY]: OpenTelemetryConstants.GEN_AI_SYSTEM_VALUE, [OpenTelemetryConstants.GEN_AI_OPERATION_NAME_KEY]: operationName, @@ -57,7 +75,11 @@ export abstract class OpenTelemetryScope implements Disposable { logger.info(`[A365Observability] Span[${this.span.spanContext().spanId}] ${spanName}, operation: ${operationName} started successfully`); - this.startTime = Date.now(); + this.wallClockStartMs = Date.now(); + if (startTime !== undefined) { + this.customStartTime = startTime; + } + this.customEndTime = endTime; // Set agent details if provided if (agentDetails) { @@ -171,6 +193,28 @@ export abstract class OpenTelemetryScope implements Disposable { this.span.setAttributes({ [`baggage.${key}`]: value }); } + /** + * Converts a `TimeInput` value to milliseconds since epoch. + * OTel's `TimeInput` can be a `number` (ms epoch), a `Date`, or an `HrTime` tuple `[seconds, nanoseconds]`. + */ + private static timeInputToMs(t: TimeInput): number { + if (typeof t === 'number') return t; + if (t instanceof Date) return t.getTime(); + if (Array.isArray(t) && t.length === 2) return t[0] * 1000 + t[1] / 1_000_000; + logger.warn(`[A365Observability] timeInputToMs received unexpected TimeInput (type=${typeof t}, isArray=${Array.isArray(t)}); falling back to Date.now()`); + return Date.now(); + } + + /** + * Sets a custom end time for the scope. + * When set, {@link dispose} will pass this value to `span.end()` instead of using the current wall-clock time. + * This is useful when the actual end time of the operation is known before the scope is disposed. + * @param endTime The end time as milliseconds since epoch, a Date, or an HrTime tuple. + */ + public setEndTime(endTime: TimeInput): void { + this.customEndTime = endTime; + } + /** * Finalizes the scope and records metrics */ @@ -180,7 +224,15 @@ export abstract class OpenTelemetryScope implements Disposable { return; } - const duration = (Date.now() - this.startTime) / 1000; // Convert to seconds + // Calculate duration: use custom start/end when provided, otherwise fall back to wall-clock. + const startMs = this.customStartTime !== undefined + ? OpenTelemetryScope.timeInputToMs(this.customStartTime) + : this.wallClockStartMs; + const endMs = this.customEndTime !== undefined + ? OpenTelemetryScope.timeInputToMs(this.customEndTime) + : Date.now(); + const durationMs = Math.max(0, endMs - startMs); + const duration = durationMs / 1000; const finalTags:Attributes = {}; if (this.errorType) { @@ -202,7 +254,11 @@ export abstract class OpenTelemetryScope implements Disposable { public [Symbol.dispose](): void { if (!this.hasEnded) { this.end(); - this.span.end(); + if (this.customEndTime !== undefined) { + this.span.end(this.customEndTime); + } else { + this.span.end(); + } } } diff --git a/packages/agents-a365-observability/src/tracing/scopes/OutputScope.ts b/packages/agents-a365-observability/src/tracing/scopes/OutputScope.ts new file mode 100644 index 00000000..9888b456 --- /dev/null +++ b/packages/agents-a365-observability/src/tracing/scopes/OutputScope.ts @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { SpanKind, TimeInput } from '@opentelemetry/api'; +import { OpenTelemetryScope } from './OpenTelemetryScope'; +import { AgentDetails, TenantDetails, OutputResponse } from '../contracts'; +import { ParentContext } from '../context/trace-context-propagation'; +import { OpenTelemetryConstants } from '../constants'; + +/** + * Provides OpenTelemetry tracing scope for output message tracing with parent span linking. + */ +export class OutputScope extends OpenTelemetryScope { + private _outputMessages: string[]; + + /** + * Creates and starts a new scope for output message tracing. + * @param response The response containing initial output messages. + * @param agentDetails The details of the agent producing the output. + * @param tenantDetails The tenant details. + * @param parentContext Optional parent context for cross-async-boundary tracing. + * Accepts a ParentSpanRef (manual traceId/spanId) or an OTel Context (e.g. from extractTraceContext). + * @param startTime Optional explicit start time (ms epoch, Date, or HrTime). + * @param endTime Optional explicit end time (ms epoch, Date, or HrTime). + * @returns A new OutputScope instance. + */ + public static start( + response: OutputResponse, + agentDetails: AgentDetails, + tenantDetails: TenantDetails, + parentContext?: ParentContext, + startTime?: TimeInput, + endTime?: TimeInput + ): OutputScope { + return new OutputScope(response, agentDetails, tenantDetails, parentContext, startTime, endTime); + } + + private constructor( + response: OutputResponse, + agentDetails: AgentDetails, + tenantDetails: TenantDetails, + parentContext?: ParentContext, + startTime?: TimeInput, + endTime?: TimeInput + ) { + super( + SpanKind.CLIENT, + OpenTelemetryConstants.OUTPUT_MESSAGES_OPERATION_NAME, + `${OpenTelemetryConstants.OUTPUT_MESSAGES_OPERATION_NAME} ${agentDetails.agentId}`, + agentDetails, + tenantDetails, + parentContext, + startTime, + endTime + ); + + // Initialize accumulated messages list from the response + this._outputMessages = [...response.messages]; + + // Set initial output messages attribute + this.setTagMaybe( + OpenTelemetryConstants.GEN_AI_OUTPUT_MESSAGES_KEY, + JSON.stringify(this._outputMessages) + ); + } + + /** + * Records the output messages for telemetry tracking. + * Appends the provided messages to the accumulated output messages list. + * @param messages Array of output messages to append. + */ + public recordOutputMessages(messages: string[]): void { + this._outputMessages.push(...messages); + this.setTagMaybe( + OpenTelemetryConstants.GEN_AI_OUTPUT_MESSAGES_KEY, + JSON.stringify(this._outputMessages) + ); + } +} diff --git a/packages/agents-a365-observability/src/utils/logging.ts b/packages/agents-a365-observability/src/utils/logging.ts index 84fd85bd..2e8fb5be 100644 --- a/packages/agents-a365-observability/src/utils/logging.ts +++ b/packages/agents-a365-observability/src/utils/logging.ts @@ -6,6 +6,7 @@ import { defaultObservabilityConfigurationProvider, ObservabilityConfiguration } from '../configuration'; +import { ExporterEventNames } from '../tracing/exporter/ExporterEventNames'; /** * Custom logger interface for Agent 365 observability @@ -35,14 +36,13 @@ export interface ILogger { /** * Log an event with standardized parameters - * @param eventType Standardized event name/category (e.g., ExporterEventNames.EXPORT) + * @param eventType Standardized event name from ExporterEventNames enum (e.g., ExporterEventNames.EXPORT) * @param isSuccess Whether the operation/event succeeded * @param durationMs Duration of the operation/event in milliseconds * @param message Optional message or additional details about the event, especially useful for errors or failures - * @param correlationId Optional correlation identifier to connect events/logs across components - + * @param details Optional key-value pairs with additional context (e.g., correlationId, tenantId, agentId, etc.) */ - event(eventType: string, isSuccess: boolean, durationMs: number, message?: string, correlationId?: string): void; + event(eventType: ExporterEventNames, isSuccess: boolean, durationMs: number, message?: string, details?: Record): void; } /** @@ -131,14 +131,14 @@ export class DefaultLogger implements ILogger { } } - event(eventType: string, isSuccess: boolean, durationMs: number, message?: string, correlationId?: string): void { + event(eventType: ExporterEventNames, isSuccess: boolean, durationMs: number, message?: string, details?: Record): void { const status = isSuccess ? 'succeeded' : 'failed'; const logLevelNeeded = isSuccess ? 1 : 3; if (this.getEnabledLogLevels().has(logLevelNeeded)) { const logFn = isSuccess ? console.log : console.error; const messageInfo = message ? ` - ${message}` : ''; - const correlationInfo = correlationId ? ` [${correlationId}]` : ''; - logFn(`[EVENT]: ${eventType} ${status} in ${durationMs}ms${messageInfo}${correlationInfo}`); + const detailsInfo = details && Object.keys(details).length > 0 ? ` ${JSON.stringify(details)}` : ''; + logFn(`[EVENT]: ${eventType} ${status} in ${durationMs}ms${messageInfo}${detailsInfo}`); } } } @@ -169,8 +169,9 @@ let globalLogger: ILogger = new DefaultLogger(); * info: (msg, ...args) => winstonLogger.info(msg, ...args), * warn: (msg, ...args) => winstonLogger.warn(msg, ...args), * error: (msg, ...args) => winstonLogger.error(msg, ...args), - * event: (eventType, isSuccess, durationMs, message, correlationId) => { - * winstonLogger.log({ level: isSuccess ? 'info' : 'error', eventType, isSuccess, durationMs, message, correlationId }); + * event: (eventType, isSuccess, durationMs, message, details) => { + * // eventType is ExporterEventNames enum value + * winstonLogger.log({ level: isSuccess ? 'info' : 'error', eventType, isSuccess, durationMs, message, ...details }); * } * }); * ``` @@ -212,8 +213,8 @@ export const logger: ILogger = { info: (message: string, ...args: unknown[]) => globalLogger.info(message, ...args), warn: (message: string, ...args: unknown[]) => globalLogger.warn(message, ...args), error: (message: string, ...args: unknown[]) => globalLogger.error(message, ...args), - event: (eventType: string, isSuccess: boolean, durationMs: number, message?: string, correlationId?: string) => - globalLogger.event(eventType, isSuccess, durationMs, message, correlationId) + event: (eventType: ExporterEventNames, isSuccess: boolean, durationMs: number, message?: string, details?: Record) => + globalLogger.event(eventType, isSuccess, durationMs, message, details) }; export default logger; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a9ecfba1..23cccb37 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -296,7 +296,7 @@ importers: version: 9.39.1 '@langchain/core': specifier: 'catalog:' - version: 1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13)) + version: 1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)) '@types/jest': specifier: 'catalog:' version: 30.0.0 @@ -394,6 +394,9 @@ importers: '@microsoft/agents-hosting': specifier: 'catalog:' version: 1.1.0-alpha.85 + '@opentelemetry/api': + specifier: 'catalog:' + version: 1.9.0 devDependencies: '@eslint/js': specifier: 'catalog:' @@ -586,13 +589,13 @@ importers: dependencies: '@langchain/core': specifier: 'catalog:' - version: 1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13)) + version: 1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)) '@langchain/langgraph': specifier: 'catalog:' - version: 1.1.2(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13)))(zod-to-json-schema@3.25.0(zod@4.1.13))(zod@4.1.13) + version: 1.1.2(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)))(zod-to-json-schema@3.25.0(zod@4.1.13))(zod@4.1.13) '@langchain/mcp-adapters': specifier: 'catalog:' - version: 1.1.1(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13)))(@langchain/langgraph@1.1.2(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13)))(zod-to-json-schema@3.25.0(zod@4.1.13))(zod@4.1.13))(hono@4.11.7) + version: 1.1.1(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)))(@langchain/langgraph@1.1.2(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)))(zod-to-json-schema@3.25.0(zod@4.1.13))(zod@4.1.13))(hono@4.11.7) '@microsoft/agents-a365-runtime': specifier: workspace:* version: link:../agents-a365-runtime @@ -607,7 +610,7 @@ importers: version: 4.11.7 langchain: specifier: 'catalog:' - version: 1.2.3(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13))(zod-to-json-schema@3.25.0(zod@4.1.13)) + version: 1.2.3(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13))(zod-to-json-schema@3.25.0(zod@4.1.13)) uuid: specifier: ^10.0.0 version: 10.0.0 @@ -742,6 +745,9 @@ importers: '@opentelemetry/api': specifier: 'catalog:' version: 1.9.0 + '@opentelemetry/core': + specifier: 'catalog:' + version: 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/exporter-trace-otlp-http': specifier: 'catalog:' version: 0.205.0(@opentelemetry/api@1.9.0) @@ -810,6 +816,91 @@ importers: specifier: 'catalog:' version: 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + tests-agent/langchain-agent: + dependencies: + '@langchain/core': + specifier: ^1.1.8 + version: 1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)) + '@langchain/langgraph': + specifier: ^1.0.2 + version: 1.1.2(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)))(zod-to-json-schema@3.25.0(zod@4.1.13))(zod@4.1.13) + '@langchain/mcp-adapters': + specifier: ^1.0.0 + version: 1.1.1(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)))(@langchain/langgraph@1.1.2(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)))(zod-to-json-schema@3.25.0(zod@4.1.13))(zod@4.1.13))(hono@4.11.7) + '@langchain/openai': + specifier: ^1.0.2 + version: 1.2.7(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)))(ws@8.18.3) + '@microsoft/agents-a365-notifications': + specifier: workspace:* + version: link:../../packages/agents-a365-notifications + '@microsoft/agents-a365-observability': + specifier: workspace:* + version: link:../../packages/agents-a365-observability + '@microsoft/agents-a365-observability-extensions-langchain': + specifier: workspace:* + version: link:../../packages/agents-a365-observability-extensions-langchain + '@microsoft/agents-a365-observability-hosting': + specifier: workspace:* + version: link:../../packages/agents-a365-observability-hosting + '@microsoft/agents-a365-runtime': + specifier: workspace:* + version: link:../../packages/agents-a365-runtime + '@microsoft/agents-a365-tooling': + specifier: workspace:* + version: link:../../packages/agents-a365-tooling + '@microsoft/agents-a365-tooling-extensions-langchain': + specifier: workspace:* + version: link:../../packages/agents-a365-tooling-extensions-langchain + '@microsoft/agents-activity': + specifier: ^1.1.0-alpha.85 + version: 1.1.0-alpha.85 + '@microsoft/agents-hosting': + specifier: ^1.1.0-alpha.85 + version: 1.1.0-alpha.85 + dotenv: + specifier: ^17.2.3 + version: 17.2.3 + express: + specifier: ^5.1.0 + version: 5.2.0 + langchain: + specifier: ^1.2.14 + version: 1.2.24(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13))(zod-to-json-schema@3.25.0(zod@4.1.13)) + node-fetch: + specifier: ^3.3.2 + version: 3.3.2 + uuid: + specifier: ^10.0.0 + version: 10.0.0 + devDependencies: + '@azure/monitor-opentelemetry-exporter': + specifier: ^1.0.0-beta.32 + version: 1.0.0-beta.32 + '@babel/cli': + specifier: ^7.28.3 + version: 7.28.6(@babel/core@7.28.5) + '@babel/core': + specifier: ^7.28.4 + version: 7.28.5 + '@babel/preset-env': + specifier: ^7.28.3 + version: 7.29.0(@babel/core@7.28.5) + '@microsoft/m365agentsplayground': + specifier: ^0.2.18 + version: 0.2.23 + '@types/express': + specifier: ^4.17.21 + version: 4.17.25 + '@types/node': + specifier: ^20.14.9 + version: 20.19.25 + nodemon: + specifier: ^3.1.10 + version: 3.1.11 + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@20.19.25)(typescript@5.9.3) + packages: '@anthropic-ai/claude-agent-sdk@0.1.47': @@ -866,14 +957,29 @@ packages: resolution: {integrity: sha512-Ul7A4gwmaHzYWj2Z5xBDly/W8JSC1vnKgJ898zPMZr0oSf1ah0tiL15sytjycU/PMhDZAlkWtEL1+MzNMU6uww==} engines: {node: '>=16'} + '@babel/cli@7.28.6': + resolution: {integrity: sha512-6EUNcuBbNkj08Oj4gAZ+BUU8yLCgKzgVX4gaTh09Ya2C8ICM4P+G30g4m3akRxSYAp3A/gnWchrNst7px4/nUQ==} + engines: {node: '>=6.9.0'} + hasBin: true + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + '@babel/compat-data@7.28.5': resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.29.0': + resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + engines: {node: '>=6.9.0'} + '@babel/core@7.28.5': resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} engines: {node: '>=6.9.0'} @@ -882,6 +988,10 @@ packages: resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} engines: {node: '>=6.9.0'} + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@7.27.3': resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} engines: {node: '>=6.9.0'} @@ -890,12 +1000,33 @@ packages: resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + '@babel/helper-create-class-features-plugin@7.28.5': resolution: {integrity: sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-create-class-features-plugin@7.28.6': + resolution: {integrity: sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.28.5': + resolution: {integrity: sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.6.6': + resolution: {integrity: sha512-mOAsxeeKkUKayvZR3HeTYD/fICpCPLJrU5ZjelT/PA6WHtNDBOE436YiaEUvHN454bRM3CebhDsIpieCc4texA==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + '@babel/helper-globals@7.28.0': resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} engines: {node: '>=6.9.0'} @@ -908,12 +1039,22 @@ packages: resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + '@babel/helper-module-transforms@7.28.3': resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-optimise-call-expression@7.27.1': resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} engines: {node: '>=6.9.0'} @@ -922,12 +1063,28 @@ packages: resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} engines: {node: '>=6.9.0'} + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + + '@babel/helper-remap-async-to-generator@7.27.1': + resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-replace-supers@7.27.1': resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-replace-supers@7.28.6': + resolution: {integrity: sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} engines: {node: '>=6.9.0'} @@ -944,6 +1101,10 @@ packages: resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} + '@babel/helper-wrap-function@7.28.6': + resolution: {integrity: sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==} + engines: {node: '>=6.9.0'} + '@babel/helpers@7.28.4': resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} engines: {node: '>=6.9.0'} @@ -953,6 +1114,47 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.0': + resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': + resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1': + resolution: {integrity: sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1': + resolution: {integrity: sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': + resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6': + resolution: {integrity: sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-async-generators@7.8.4': resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: @@ -974,12 +1176,24 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-import-assertions@7.28.6': + resolution: {integrity: sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-import-attributes@7.27.1': resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-import-attributes@7.28.6': + resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-import-meta@7.10.4': resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: @@ -1044,115 +1258,450 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-commonjs@7.27.1': - resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==} + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-arrow-functions@7.27.1': + resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typescript@7.28.5': - resolution: {integrity: sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==} + '@babel/plugin-transform-async-generator-functions@7.29.0': + resolution: {integrity: sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/preset-typescript@7.28.5': - resolution: {integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==} + '@babel/plugin-transform-async-to-generator@7.28.6': + resolution: {integrity: sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/template@7.27.2': - resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + '@babel/plugin-transform-block-scoped-functions@7.27.1': + resolution: {integrity: sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==} engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@babel/traverse@7.28.5': - resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} + '@babel/plugin-transform-block-scoping@7.28.6': + resolution: {integrity: sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==} engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@babel/types@7.28.5': - resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + '@babel/plugin-transform-class-properties@7.28.6': + resolution: {integrity: sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==} engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@bcoe/v8-coverage@0.2.3': - resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + '@babel/plugin-transform-class-static-block@7.28.6': + resolution: {integrity: sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 - '@cfworker/json-schema@4.1.1': - resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==} + '@babel/plugin-transform-classes@7.28.6': + resolution: {integrity: sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@cspotcode/source-map-support@0.8.1': - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} + '@babel/plugin-transform-computed-properties@7.28.6': + resolution: {integrity: sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@emnapi/core@1.7.1': - resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} + '@babel/plugin-transform-destructuring@7.28.5': + resolution: {integrity: sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@emnapi/runtime@1.7.1': - resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} + '@babel/plugin-transform-dotall-regex@7.28.6': + resolution: {integrity: sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@emnapi/wasi-threads@1.1.0': - resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + '@babel/plugin-transform-duplicate-keys@7.27.1': + resolution: {integrity: sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@eslint-community/eslint-utils@4.9.0': - resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0': + resolution: {integrity: sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==} + engines: {node: '>=6.9.0'} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@babel/core': ^7.0.0 - '@eslint-community/regexpp@4.12.2': - resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@babel/plugin-transform-dynamic-import@7.27.1': + resolution: {integrity: sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@eslint/config-array@0.21.1': - resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@babel/plugin-transform-explicit-resource-management@7.28.6': + resolution: {integrity: sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@eslint/config-helpers@0.4.2': - resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@babel/plugin-transform-exponentiation-operator@7.28.6': + resolution: {integrity: sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@eslint/core@0.17.0': - resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@babel/plugin-transform-export-namespace-from@7.27.1': + resolution: {integrity: sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@eslint/eslintrc@3.3.1': - resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@babel/plugin-transform-for-of@7.27.1': + resolution: {integrity: sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@eslint/js@9.39.1': - resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@babel/plugin-transform-function-name@7.27.1': + resolution: {integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@eslint/object-schema@2.1.7': - resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@babel/plugin-transform-json-strings@7.28.6': + resolution: {integrity: sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@eslint/plugin-kit@0.4.1': - resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@babel/plugin-transform-literals@7.27.1': + resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@grpc/grpc-js@1.14.1': - resolution: {integrity: sha512-sPxgEWtPUR3EnRJCEtbGZG2iX8LQDUls2wUS3o27jg07KqJFMq6YDeWvMo1wfpmy3rqRdS0rivpLwhqQtEyCuQ==} - engines: {node: '>=12.10.0'} + '@babel/plugin-transform-logical-assignment-operators@7.28.6': + resolution: {integrity: sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@grpc/proto-loader@0.8.0': - resolution: {integrity: sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==} - engines: {node: '>=6'} - hasBin: true + '@babel/plugin-transform-member-expression-literals@7.27.1': + resolution: {integrity: sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@hono/node-server@1.19.7': - resolution: {integrity: sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw==} - engines: {node: '>=18.14.1'} + '@babel/plugin-transform-modules-amd@7.27.1': + resolution: {integrity: sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==} + engines: {node: '>=6.9.0'} peerDependencies: - hono: ^4.11.7 + '@babel/core': ^7.0.0-0 - '@humanfs/core@0.19.1': - resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} - engines: {node: '>=18.18.0'} + '@babel/plugin-transform-modules-commonjs@7.27.1': + resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@humanfs/node@0.16.7': - resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} - engines: {node: '>=18.18.0'} + '@babel/plugin-transform-modules-commonjs@7.28.6': + resolution: {integrity: sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-systemjs@7.29.0': + resolution: {integrity: sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-umd@7.27.1': + resolution: {integrity: sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0': + resolution: {integrity: sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-new-target@7.27.1': + resolution: {integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6': + resolution: {integrity: sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-numeric-separator@7.28.6': + resolution: {integrity: sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-rest-spread@7.28.6': + resolution: {integrity: sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-super@7.27.1': + resolution: {integrity: sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-catch-binding@7.28.6': + resolution: {integrity: sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.28.6': + resolution: {integrity: sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.27.7': + resolution: {integrity: sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.28.6': + resolution: {integrity: sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-property-in-object@7.28.6': + resolution: {integrity: sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-property-literals@7.27.1': + resolution: {integrity: sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.29.0': + resolution: {integrity: sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regexp-modifiers@7.28.6': + resolution: {integrity: sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-reserved-words@7.27.1': + resolution: {integrity: sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.27.1': + resolution: {integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.28.6': + resolution: {integrity: sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-sticky-regex@7.27.1': + resolution: {integrity: sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.27.1': + resolution: {integrity: sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typeof-symbol@7.27.1': + resolution: {integrity: sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.28.5': + resolution: {integrity: sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-escapes@7.27.1': + resolution: {integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-property-regex@7.28.6': + resolution: {integrity: sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.27.1': + resolution: {integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-sets-regex@7.28.6': + resolution: {integrity: sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/preset-env@7.29.0': + resolution: {integrity: sha512-fNEdfc0yi16lt6IZo2Qxk3knHVdfMYX33czNb4v8yWhemoBhibCpQK/uYHtSKIiO+p/zd3+8fYVXhQdOVV608w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + + '@babel/preset-typescript@7.28.5': + resolution: {integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.5': + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@0.2.3': + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + + '@cfworker/json-schema@4.1.1': + resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==} + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + + '@emnapi/core@1.7.1': + resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} + + '@emnapi/runtime@1.7.1': + resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} + + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.1': + resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@grpc/grpc-js@1.14.1': + resolution: {integrity: sha512-sPxgEWtPUR3EnRJCEtbGZG2iX8LQDUls2wUS3o27jg07KqJFMq6YDeWvMo1wfpmy3rqRdS0rivpLwhqQtEyCuQ==} + engines: {node: '>=12.10.0'} + + '@grpc/proto-loader@0.8.0': + resolution: {integrity: sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==} + engines: {node: '>=6'} + hasBin: true + + '@hono/node-server@1.19.7': + resolution: {integrity: sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4.11.7 + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} @@ -1389,6 +1938,12 @@ packages: '@langchain/core': ^1.0.0 '@langchain/langgraph': ^1.0.0 + '@langchain/openai@1.2.7': + resolution: {integrity: sha512-vR9zoF0/EZ03X0Tc6woIEWRDSDSr2l64n+MQCW8NduScJtBJs5r/Ng3Lrp2bjtJQywEMQoOhcrV2DMmAIPWgnw==} + engines: {node: '>=20'} + peerDependencies: + '@langchain/core': ^1.0.0 + '@microsoft/agents-activity@1.1.0-alpha.85': resolution: {integrity: sha512-iMzeYFJZPSrXkhHpHesKQ1gjvCm6uyPlH0ojsNf8Z3EODCeiFsxHOahoLyzCuqzcWBYhNCcQ45aarNbeJ84HgA==} engines: {node: '>=20.0.0'} @@ -1397,6 +1952,10 @@ packages: resolution: {integrity: sha512-1Ii92EJSaQTuqjOBWUqoioClum+6Dh82uBmzoExMi3ZDJ9UTV6Kqg4W1h4PYhUZPmqIP3HLSVNvty84Gx/mqYg==} engines: {node: '>=20.0.0'} + '@microsoft/m365agentsplayground@0.2.23': + resolution: {integrity: sha512-egrlYlImg3dxpn7VXkjGbMCPZmimZaOXYvnFspd+H9iH8Rsjzjrp14KnSbdShzqVTR5vpsVpF+XkZnwfDDqjSw==} + hasBin: true + '@modelcontextprotocol/sdk@1.25.2': resolution: {integrity: sha512-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww==} engines: {node: '>=18'} @@ -1410,6 +1969,9 @@ packages: '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + '@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3': + resolution: {integrity: sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -2106,6 +2668,21 @@ packages: resolution: {integrity: sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + babel-plugin-polyfill-corejs2@0.4.15: + resolution: {integrity: sha512-hR3GwrRwHUfYwGfrisXPIDP3JcYfBrW7wKE7+Au6wDYl7fm/ka1NEII6kORzxNU556JjfidZeBsO10kYvtV1aw==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.14.0: + resolution: {integrity: sha512-AvDcMxJ34W4Wgy4KBIIePQTAOP1Ie2WFwkQp3dB7FQ/f0lI5+nM96zUnYEOE1P9sEg0es5VCP0HxiWu5fUHZAQ==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.6: + resolution: {integrity: sha512-hYm+XLYRMvupxiQzrvXUj7YyvFFVfv5gI0R71AJzudg1g2AI2vyCPPIFEBjk162/wFzti3inBHo7isWFuEVS/A==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + babel-preset-current-node-syntax@1.2.0: resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} peerDependencies: @@ -2127,6 +2704,14 @@ packages: resolution: {integrity: sha512-sXdt2elaVnhpDNRDz+1BDx1JQoJRuNk7oVlAlbGiFkLikHCAQiccexF/9e91zVi6RCgqspl04aP+6Cnl9zRLrA==} hasBin: true + baseline-browser-mapping@2.9.19: + resolution: {integrity: sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==} + hasBin: true + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + body-parser@2.2.1: resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==} engines: {node: '>=18'} @@ -2146,6 +2731,11 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + bs-logger@0.2.6: resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} engines: {node: '>= 6'} @@ -2190,6 +2780,9 @@ packages: caniuse-lite@1.0.30001756: resolution: {integrity: sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==} + caniuse-lite@1.0.30001770: + resolution: {integrity: sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -2198,6 +2791,10 @@ packages: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + ci-info@4.3.1: resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==} engines: {node: '>=8'} @@ -2230,6 +2827,10 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + commander@6.2.1: + resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} + engines: {node: '>= 6'} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -2255,6 +2856,9 @@ packages: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} + core-js-compat@3.48.0: + resolution: {integrity: sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q==} + cors@2.8.5: resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} engines: {node: '>= 0.10'} @@ -2271,6 +2875,10 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -2347,6 +2955,9 @@ packages: electron-to-chromium@1.5.258: resolution: {integrity: sha512-rHUggNV5jKQ0sSdWwlaRDkFc3/rRJIVnOSe9yR4zrR07m3ZxhP4N27Hlg8VeJGGYgFTxK5NqDmWI4DSH72vIJg==} + electron-to-chromium@1.5.286: + resolution: {integrity: sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==} + emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} @@ -2511,6 +3122,10 @@ packages: fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -2562,6 +3177,10 @@ packages: resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} engines: {node: '>= 12.20'} + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -2570,6 +3189,9 @@ packages: resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} engines: {node: '>= 0.8'} + fs-readdir-recursive@1.1.0: + resolution: {integrity: sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==} + fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -2615,6 +3237,7 @@ packages: glob@10.5.0: resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true glob@13.0.0: @@ -2623,7 +3246,7 @@ packages: glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} @@ -2648,6 +3271,10 @@ packages: engines: {node: '>=0.4.7'} hasBin: true + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -2694,6 +3321,9 @@ packages: resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} engines: {node: '>=0.10.0'} + ignore-by-default@1.0.1: + resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -2732,6 +3362,10 @@ packages: is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + is-core-module@2.16.1: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} @@ -3004,7 +3638,13 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - langchain@1.2.3: + langchain@1.2.24: + resolution: {integrity: sha512-NpaOmDZ4dP16sLkY+Y49Q9mrV2fdTy81t+yTw7f3K7/zZtvuQk6IH/bIzfy3bdOD8TERethvR2mOmgueRDbZBw==} + engines: {node: '>=20'} + peerDependencies: + '@langchain/core': ^1.1.24 + + langchain@1.2.3: resolution: {integrity: sha512-3k986xJuqg4az53JxV5LnGlOzIXF1d9Kq6Y9s7XjitvzhpsbFuTDV5/kiF4cx3pkNGyw0mUXC4tLz9RxucO0hw==} engines: {node: '>=20'} peerDependencies: @@ -3027,6 +3667,23 @@ packages: openai: optional: true + langsmith@0.5.4: + resolution: {integrity: sha512-qYkNIoKpf0ZYt+cYzrDV+XI3FCexApmZmp8EMs3eDTMv0OvrHMLoxJ9IpkeoXJSX24+GPk0/jXjKx2hWerpy9w==} + peerDependencies: + '@opentelemetry/api': '*' + '@opentelemetry/exporter-trace-otlp-proto': '*' + '@opentelemetry/sdk-trace-base': '*' + openai: '*' + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@opentelemetry/exporter-trace-otlp-proto': + optional: true + '@opentelemetry/sdk-trace-base': + optional: true + openai: + optional: true + leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} @@ -3055,6 +3712,9 @@ packages: lodash.clonedeep@4.5.0: resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + lodash.includes@4.3.0: resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} @@ -3102,6 +3762,10 @@ packages: lru-memoizer@2.3.0: resolution: {integrity: sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==} + make-dir@2.1.0: + resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} + engines: {node: '>=6'} + make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -3216,12 +3880,21 @@ packages: encoding: optional: true + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + nodemon@3.1.11: + resolution: {integrity: sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==} + engines: {node: '>=10'} + hasBin: true + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -3269,8 +3942,8 @@ packages: zod: optional: true - openai@6.16.0: - resolution: {integrity: sha512-fZ1uBqjFUjXzbGc35fFtYKEOxd20kd9fDpFeqWtsOZWiubY8CZ1NAlXHW3iathaFvqmNtCWMIsosCuyeI7Joxg==} + openai@6.22.0: + resolution: {integrity: sha512-7Yvy17F33Bi9RutWbsaYt5hJEEJ/krRPOrwan+f9aCPuMat1WVsb2VNSII5W1EksKT6fF69TG/xj4XzodK3JZw==} hasBin: true peerDependencies: ws: ^8.18.0 @@ -3393,6 +4066,10 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + pirates@4.0.7: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} @@ -3424,6 +4101,9 @@ packages: proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + pstree.remy@1.1.8: + resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -3449,6 +4129,28 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + regenerate-unicode-properties@10.2.2: + resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} + engines: {node: '>=4'} + + regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + + regexpu-core@6.4.0: + resolution: {integrity: sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==} + engines: {node: '>=4'} + + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + + regjsparser@0.13.0: + resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==} + hasBin: true + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -3504,6 +4206,10 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -3555,9 +4261,17 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-update-notifier@2.0.0: + resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} + engines: {node: '>=10'} + simple-wcswidth@1.1.2: resolution: {integrity: sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==} + slash@2.0.0: + resolution: {integrity: sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==} + engines: {node: '>=6'} + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -3612,6 +4326,10 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -3643,6 +4361,10 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} + touch@3.1.1: + resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} + hasBin: true + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -3733,12 +4455,31 @@ packages: engines: {node: '>=0.8.0'} hasBin: true + undefsafe@2.0.5: + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} + undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.2.1: + resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.2.0: + resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} + engines: {node: '>=4'} + unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} @@ -3752,6 +4493,12 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -3773,6 +4520,10 @@ packages: walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + web-streams-polyfill@4.0.0-beta.3: resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} engines: {node: '>= 14'} @@ -3973,14 +4724,36 @@ snapshots: jsonwebtoken: 9.0.3 uuid: 10.0.0 + '@babel/cli@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@jridgewell/trace-mapping': 0.3.31 + commander: 6.2.1 + convert-source-map: 2.0.0 + fs-readdir-recursive: 1.1.0 + glob: 7.2.3 + make-dir: 2.1.0 + slash: 2.0.0 + optionalDependencies: + '@nicolo-ribaudo/chokidar-2': 2.1.8-no-fsevents.3 + chokidar: 3.6.0 + '@babel/code-frame@7.27.1': dependencies: '@babel/helper-validator-identifier': 7.28.5 js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/compat-data@7.28.5': {} + '@babel/compat-data@7.29.0': {} + '@babel/core@7.28.5': dependencies: '@babel/code-frame': 7.27.1 @@ -3994,7 +4767,7 @@ snapshots: '@babel/types': 7.28.5 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -4009,6 +4782,14 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + '@babel/helper-annotate-as-pure@7.27.3': dependencies: '@babel/types': 7.28.5 @@ -4021,6 +4802,14 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.0 + lru-cache: 5.1.1 + semver: 6.3.1 + '@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -4034,6 +4823,37 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-create-class-features-plugin@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.28.5) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.29.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + regexpu-core: 6.4.0 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + debug: 4.4.3(supports-color@5.5.0) + lodash.debounce: 4.0.8 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + '@babel/helper-globals@7.28.0': {} '@babel/helper-member-expression-to-functions@7.28.5': @@ -4050,155 +4870,674 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.28.5 - transitivePeerDependencies: - - supports-color + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.28.5 + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-plugin-utils@7.28.6': {} + + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-wrap-function': 7.28.6 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helper-wrap-function@7.28.6': + dependencies: + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.28.4': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + + '@babel/parser@7.29.0': + dependencies: + '@babel/types': 7.29.0 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-assertions@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-async-generator-functions@7.29.0(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.5) + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-block-scoping@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-globals': 7.28.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.28.5) + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/template': 7.28.6 + + '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-dotall-regex@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-explicit-resource-management@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-exponentiation-operator@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-json-strings@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-logical-assignment-operators@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-systemjs@7.29.0(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-optimise-call-expression@7.27.1': + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@7.28.5)': dependencies: - '@babel/types': 7.28.5 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-plugin-utils@7.27.1': {} + '@babel/plugin-transform-numeric-separator@7.28.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-object-rest-spread@7.28.6(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-member-expression-to-functions': 7.28.5 - '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.5) + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color - '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) transitivePeerDependencies: - supports-color - '@babel/helper-string-parser@7.27.1': {} - - '@babel/helper-validator-identifier@7.28.5': {} - - '@babel/helper-validator-option@7.27.1': {} - - '@babel/helpers@7.28.4': + '@babel/plugin-transform-optional-catch-binding@7.28.6(@babel/core@7.28.5)': dependencies: - '@babel/template': 7.27.2 - '@babel/types': 7.28.5 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/parser@7.28.5': + '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.28.5)': dependencies: - '@babel/types': 7.28.5 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.5)': + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.5)': + '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.5)': + '@babel/plugin-transform-private-property-in-object@7.28.6(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.5)': + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-regenerator@7.29.0(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.5)': + '@babel/plugin-transform-regexp-modifiers@7.28.6(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.5)': + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.5)': + '@babel/plugin-transform-spread@7.28.6(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.5)': + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.5)': + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.5)': + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.5)': + '@babel/plugin-transform-typescript@7.28.5(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.5)': + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.5)': + '@babel/plugin-transform-unicode-property-regex@7.28.6(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.5)': + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-unicode-sets-regex@7.28.6(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.5)': + '@babel/preset-env@7.29.0(@babel/core@7.28.5)': dependencies: + '@babel/compat-data': 7.29.0 '@babel/core': 7.28.5 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.5) + '@babel/plugin-syntax-import-assertions': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.28.5) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.28.5) + '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-dotall-regex': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.0(@babel/core@7.28.5) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-explicit-resource-management': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-exponentiation-operator': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-json-strings': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-modules-systemjs': 7.29.0(@babel/core@7.28.5) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.28.5) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.5) + '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.28.5) + '@babel/plugin-transform-regexp-modifiers': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-property-regex': 7.28.6(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-sets-regex': 7.28.6(@babel/core@7.28.5) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.28.5) + babel-plugin-polyfill-corejs2: 0.4.15(@babel/core@7.28.5) + babel-plugin-polyfill-corejs3: 0.14.0(@babel/core@7.28.5) + babel-plugin-polyfill-regenerator: 0.6.6(@babel/core@7.28.5) + core-js-compat: 3.48.0 + semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-typescript@7.28.5(@babel/core@7.28.5)': + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) - transitivePeerDependencies: - - supports-color + '@babel/helper-plugin-utils': 7.28.6 + '@babel/types': 7.28.5 + esutils: 2.0.3 '@babel/preset-typescript@7.28.5(@babel/core@7.28.5)': dependencies: @@ -4217,6 +5556,12 @@ snapshots: '@babel/parser': 7.28.5 '@babel/types': 7.28.5 + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + '@babel/traverse@7.28.5': dependencies: '@babel/code-frame': 7.27.1 @@ -4225,7 +5570,19 @@ snapshots: '@babel/parser': 7.28.5 '@babel/template': 7.27.2 '@babel/types': 7.28.5 - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -4234,6 +5591,11 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@bcoe/v8-coverage@0.2.3': {} '@cfworker/json-schema@4.1.1': {} @@ -4241,7 +5603,6 @@ snapshots: '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 - optional: true '@emnapi/core@1.7.1': dependencies: @@ -4269,7 +5630,7 @@ snapshots: '@eslint/config-array@0.21.1': dependencies: '@eslint/object-schema': 2.1.7 - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -4285,7 +5646,7 @@ snapshots: '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 @@ -4602,18 +5963,17 @@ snapshots: dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - optional: true '@js-sdsl/ordered-map@4.4.2': {} - '@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13))': + '@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13))': dependencies: '@cfworker/json-schema': 4.1.1 ansi-styles: 5.2.0 camelcase: 6.3.0 decamelize: 1.2.0 js-tiktoken: 1.0.21 - langsmith: 0.4.2(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13)) + langsmith: 0.4.2(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)) mustache: 4.2.0 p-queue: 6.6.2 uuid: 10.0.0 @@ -4624,24 +5984,24 @@ snapshots: - '@opentelemetry/sdk-trace-base' - openai - '@langchain/langgraph-checkpoint@1.0.0(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13)))': + '@langchain/langgraph-checkpoint@1.0.0(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)))': dependencies: - '@langchain/core': 1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13)) + '@langchain/core': 1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)) uuid: 10.0.0 - '@langchain/langgraph-sdk@1.5.5(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13)))': + '@langchain/langgraph-sdk@1.5.5(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)))': dependencies: p-queue: 9.1.0 p-retry: 7.1.1 uuid: 10.0.0 optionalDependencies: - '@langchain/core': 1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13)) + '@langchain/core': 1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)) - '@langchain/langgraph@1.1.2(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13)))(zod-to-json-schema@3.25.0(zod@4.1.13))(zod@4.1.13)': + '@langchain/langgraph@1.1.2(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)))(zod-to-json-schema@3.25.0(zod@4.1.13))(zod@4.1.13)': dependencies: - '@langchain/core': 1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13)) - '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13))) - '@langchain/langgraph-sdk': 1.5.5(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13))) + '@langchain/core': 1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)) + '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13))) + '@langchain/langgraph-sdk': 1.5.5(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13))) '@standard-schema/spec': 1.1.0 uuid: 10.0.0 zod: 4.1.13 @@ -4651,12 +6011,12 @@ snapshots: - react - react-dom - '@langchain/mcp-adapters@1.1.1(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13)))(@langchain/langgraph@1.1.2(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13)))(zod-to-json-schema@3.25.0(zod@4.1.13))(zod@4.1.13))(hono@4.11.7)': + '@langchain/mcp-adapters@1.1.1(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)))(@langchain/langgraph@1.1.2(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)))(zod-to-json-schema@3.25.0(zod@4.1.13))(zod@4.1.13))(hono@4.11.7)': dependencies: - '@langchain/core': 1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13)) - '@langchain/langgraph': 1.1.2(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13)))(zod-to-json-schema@3.25.0(zod@4.1.13))(zod@4.1.13) + '@langchain/core': 1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)) + '@langchain/langgraph': 1.1.2(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)))(zod-to-json-schema@3.25.0(zod@4.1.13))(zod@4.1.13) '@modelcontextprotocol/sdk': 1.25.2(@cfworker/json-schema@4.1.1)(hono@4.11.7)(zod@4.1.13) - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) zod: 4.1.13 optionalDependencies: extended-eventsource: 1.7.0 @@ -4665,9 +6025,18 @@ snapshots: - hono - supports-color + '@langchain/openai@1.2.7(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)))(ws@8.18.3)': + dependencies: + '@langchain/core': 1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)) + js-tiktoken: 1.0.21 + openai: 6.22.0(ws@8.18.3)(zod@4.1.13) + zod: 4.1.13 + transitivePeerDependencies: + - ws + '@microsoft/agents-activity@1.1.0-alpha.85': dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) uuid: 10.0.0 zod: 4.1.13 transitivePeerDependencies: @@ -4686,6 +6055,8 @@ snapshots: - debug - supports-color + '@microsoft/m365agentsplayground@0.2.23': {} + '@modelcontextprotocol/sdk@1.25.2(@cfworker/json-schema@4.1.1)(hono@4.11.7)(zod@4.1.13)': dependencies: '@hono/node-server': 1.19.7(hono@4.11.7) @@ -4717,6 +6088,9 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true + '@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3': + optional: true + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -4731,7 +6105,7 @@ snapshots: '@openai/agents-core@0.4.2(@cfworker/json-schema@4.1.1)(hono@4.11.7)(ws@8.18.3)(zod@4.1.13)': dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) openai: 6.9.1(ws@8.18.3)(zod@4.1.13) optionalDependencies: '@modelcontextprotocol/sdk': 1.25.2(@cfworker/json-schema@4.1.1)(hono@4.11.7)(zod@4.1.13) @@ -4745,7 +6119,7 @@ snapshots: '@openai/agents-openai@0.4.2(@cfworker/json-schema@4.1.1)(hono@4.11.7)(ws@8.18.3)(zod@4.1.13)': dependencies: '@openai/agents-core': 0.4.2(@cfworker/json-schema@4.1.1)(hono@4.11.7)(ws@8.18.3)(zod@4.1.13) - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) openai: 6.9.1(ws@8.18.3)(zod@4.1.13) zod: 4.1.13 transitivePeerDependencies: @@ -4758,7 +6132,7 @@ snapshots: dependencies: '@openai/agents-core': 0.4.2(@cfworker/json-schema@4.1.1)(hono@4.11.7)(ws@8.18.3)(zod@4.1.13) '@types/ws': 8.18.1 - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) ws: 8.18.3 zod: 4.1.13 transitivePeerDependencies: @@ -4773,7 +6147,7 @@ snapshots: '@openai/agents-core': 0.4.2(@cfworker/json-schema@4.1.1)(hono@4.11.7)(ws@8.18.3)(zod@4.1.13) '@openai/agents-openai': 0.4.2(@cfworker/json-schema@4.1.1)(hono@4.11.7)(ws@8.18.3)(zod@4.1.13) '@openai/agents-realtime': 0.4.2(@cfworker/json-schema@4.1.1)(hono@4.11.7)(zod@4.1.13) - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) openai: 6.9.1(ws@8.18.3)(zod@4.1.13) zod: 4.1.13 transitivePeerDependencies: @@ -5134,17 +6508,13 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@tsconfig/node10@1.0.12': - optional: true + '@tsconfig/node10@1.0.12': {} - '@tsconfig/node12@1.0.11': - optional: true + '@tsconfig/node12@1.0.11': {} - '@tsconfig/node14@1.0.3': - optional: true + '@tsconfig/node14@1.0.3': {} - '@tsconfig/node16@1.0.4': - optional: true + '@tsconfig/node16@1.0.4': {} '@tybys/wasm-util@0.10.1': dependencies: @@ -5294,7 +6664,7 @@ snapshots: '@typescript-eslint/types': 8.47.0 '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.47.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) eslint: 9.39.1(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: @@ -5304,7 +6674,7 @@ snapshots: dependencies: '@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.9.3) '@typescript-eslint/types': 8.47.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -5323,7 +6693,7 @@ snapshots: '@typescript-eslint/types': 8.47.0 '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) '@typescript-eslint/utils': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) eslint: 9.39.1(jiti@2.6.1) ts-api-utils: 2.1.0(typescript@5.9.3) typescript: 5.9.3 @@ -5338,7 +6708,7 @@ snapshots: '@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.9.3) '@typescript-eslint/types': 8.47.0 '@typescript-eslint/visitor-keys': 8.47.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 @@ -5453,7 +6823,6 @@ snapshots: acorn-walk@8.3.4: dependencies: acorn: 8.15.0 - optional: true acorn@8.15.0: {} @@ -5502,8 +6871,7 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 - arg@4.1.3: - optional: true + arg@4.1.3: {} argparse@1.0.10: dependencies: @@ -5548,6 +6916,30 @@ snapshots: dependencies: '@types/babel__core': 7.20.5 + babel-plugin-polyfill-corejs2@0.4.15(@babel/core@7.28.5): + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/core': 7.28.5 + '@babel/helper-define-polyfill-provider': 0.6.6(@babel/core@7.28.5) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.14.0(@babel/core@7.28.5): + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-define-polyfill-provider': 0.6.6(@babel/core@7.28.5) + core-js-compat: 3.48.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.6(@babel/core@7.28.5): + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-define-polyfill-provider': 0.6.6(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + babel-preset-current-node-syntax@1.2.0(@babel/core@7.28.5): dependencies: '@babel/core': 7.28.5 @@ -5579,11 +6971,15 @@ snapshots: baseline-browser-mapping@2.8.29: {} + baseline-browser-mapping@2.9.19: {} + + binary-extensions@2.3.0: {} + body-parser@2.2.1: dependencies: bytes: 3.1.2 content-type: 1.0.5 - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) http-errors: 2.0.1 iconv-lite: 0.7.0 on-finished: 2.4.1 @@ -5614,6 +7010,14 @@ snapshots: node-releases: 2.0.27 update-browserslist-db: 1.1.4(browserslist@4.28.0) + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.9.19 + caniuse-lite: 1.0.30001770 + electron-to-chromium: 1.5.286 + node-releases: 2.0.27 + update-browserslist-db: 1.2.3(browserslist@4.28.1) + bs-logger@0.2.6: dependencies: fast-json-stable-stringify: 2.1.0 @@ -5650,6 +7054,8 @@ snapshots: caniuse-lite@1.0.30001756: {} + caniuse-lite@1.0.30001770: {} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -5657,6 +7063,18 @@ snapshots: char-regex@1.0.2: {} + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + ci-info@4.3.1: {} cjs-module-lexer@1.4.3: {} @@ -5683,6 +7101,8 @@ snapshots: dependencies: delayed-stream: 1.0.0 + commander@6.2.1: {} + concat-map@0.0.1: {} console-table-printer@2.15.0: @@ -5699,13 +7119,16 @@ snapshots: cookie@0.7.2: {} + core-js-compat@3.48.0: + dependencies: + browserslist: 4.28.1 + cors@2.8.5: dependencies: object-assign: 4.1.1 vary: 1.1.2 - create-require@1.1.1: - optional: true + create-require@1.1.1: {} cross-env@7.0.3: dependencies: @@ -5717,9 +7140,13 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - debug@4.4.3: + data-uri-to-buffer@4.0.1: {} + + debug@4.4.3(supports-color@5.5.0): dependencies: ms: 2.1.3 + optionalDependencies: + supports-color: 5.5.0 decamelize@1.2.0: {} @@ -5744,8 +7171,7 @@ snapshots: detect-newline@3.1.0: {} - diff@4.0.4: - optional: true + diff@4.0.4: {} dotenv@17.2.3: {} @@ -5765,6 +7191,8 @@ snapshots: electron-to-chromium@1.5.258: {} + electron-to-chromium@1.5.286: {} + emittery@0.13.1: {} emoji-regex@8.0.0: {} @@ -5826,7 +7254,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) escape-string-regexp: 4.0.0 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -5919,7 +7347,7 @@ snapshots: content-type: 1.0.5 cookie: 0.7.2 cookie-signature: 1.2.2 - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) depd: 2.0.0 encodeurl: 2.0.0 escape-html: 1.0.3 @@ -5971,6 +7399,11 @@ snapshots: dependencies: bser: 2.1.1 + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -5981,7 +7414,7 @@ snapshots: finalhandler@2.1.1: dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) encodeurl: 2.0.0 escape-html: 1.0.3 on-finished: 2.4.1 @@ -6029,10 +7462,16 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 4.0.0-beta.3 + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + forwarded@0.2.0: {} fresh@2.0.0: {} + fs-readdir-recursive@1.1.0: {} + fs.realpath@1.0.0: {} fsevents@2.3.3: @@ -6117,6 +7556,8 @@ snapshots: optionalDependencies: uglify-js: 3.19.3 + has-flag@3.0.0: {} + has-flag@4.0.0: {} has-symbols@1.1.0: {} @@ -6144,14 +7585,14 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -6165,6 +7606,8 @@ snapshots: dependencies: safer-buffer: 2.1.2 + ignore-by-default@1.0.1: {} + ignore@5.3.2: {} ignore@7.0.5: {} @@ -6199,6 +7642,10 @@ snapshots: is-arrayish@0.2.1: {} + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + is-core-module@2.16.1: dependencies: hasown: 2.0.2 @@ -6254,7 +7701,7 @@ snapshots: istanbul-lib-source-maps@5.0.6: dependencies: '@jridgewell/trace-mapping': 0.3.31 - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: - supports-color @@ -6642,7 +8089,7 @@ snapshots: dependencies: '@types/express': 4.17.25 '@types/jsonwebtoken': 9.0.10 - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) jose: 4.15.9 limiter: 1.1.5 lru-memoizer: 2.3.0 @@ -6658,12 +8105,29 @@ snapshots: dependencies: json-buffer: 3.0.1 - langchain@1.2.3(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13))(zod-to-json-schema@3.25.0(zod@4.1.13)): + langchain@1.2.24(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13))(zod-to-json-schema@3.25.0(zod@4.1.13)): + dependencies: + '@langchain/core': 1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)) + '@langchain/langgraph': 1.1.2(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)))(zod-to-json-schema@3.25.0(zod@4.1.13))(zod@4.1.13) + '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13))) + langsmith: 0.5.4(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)) + uuid: 10.0.0 + zod: 4.1.13 + transitivePeerDependencies: + - '@opentelemetry/api' + - '@opentelemetry/exporter-trace-otlp-proto' + - '@opentelemetry/sdk-trace-base' + - openai + - react + - react-dom + - zod-to-json-schema + + langchain@1.2.3(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13))(zod-to-json-schema@3.25.0(zod@4.1.13)): dependencies: - '@langchain/core': 1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13)) - '@langchain/langgraph': 1.1.2(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13)))(zod-to-json-schema@3.25.0(zod@4.1.13))(zod@4.1.13) - '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13))) - langsmith: 0.4.2(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13)) + '@langchain/core': 1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)) + '@langchain/langgraph': 1.1.2(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)))(zod-to-json-schema@3.25.0(zod@4.1.13))(zod@4.1.13) + '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.8(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13))) + langsmith: 0.4.2(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)) uuid: 10.0.0 zod: 4.1.13 transitivePeerDependencies: @@ -6675,7 +8139,21 @@ snapshots: - react-dom - zod-to-json-schema - langsmith@0.4.2(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.16.0(ws@8.18.3)(zod@4.1.13)): + langsmith@0.4.2(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)): + dependencies: + '@types/uuid': 9.0.8 + chalk: 4.1.2 + console-table-printer: 2.15.0 + p-queue: 6.6.2 + semver: 7.7.3 + uuid: 10.0.0 + optionalDependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/exporter-trace-otlp-proto': 0.204.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) + openai: 6.22.0(ws@8.18.3)(zod@4.1.13) + + langsmith@0.5.4(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.22.0(ws@8.18.3)(zod@4.1.13)): dependencies: '@types/uuid': 9.0.8 chalk: 4.1.2 @@ -6687,7 +8165,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/exporter-trace-otlp-proto': 0.204.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) - openai: 6.16.0(ws@8.18.3)(zod@4.1.13) + openai: 6.22.0(ws@8.18.3)(zod@4.1.13) leven@3.1.0: {} @@ -6712,6 +8190,8 @@ snapshots: lodash.clonedeep@4.5.0: {} + lodash.debounce@4.0.8: {} + lodash.includes@4.3.0: {} lodash.isboolean@3.0.3: {} @@ -6749,6 +8229,11 @@ snapshots: lodash.clonedeep: 4.5.0 lru-cache: 6.0.0 + make-dir@2.1.0: + dependencies: + pify: 4.0.1 + semver: 5.7.2 + make-dir@4.0.0: dependencies: semver: 7.7.3 @@ -6826,10 +8311,29 @@ snapshots: dependencies: whatwg-url: 5.0.0 + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + node-int64@0.4.0: {} node-releases@2.0.27: {} + nodemon@3.1.11: + dependencies: + chokidar: 3.6.0 + debug: 4.4.3(supports-color@5.5.0) + ignore-by-default: 1.0.1 + minimatch: 3.1.2 + pstree.remy: 1.1.8 + semver: 7.7.3 + simple-update-notifier: 2.0.0 + supports-color: 5.5.0 + touch: 3.1.1 + undefsafe: 2.0.5 + normalize-path@3.0.0: {} npm-run-path@4.0.1: @@ -6876,11 +8380,10 @@ snapshots: transitivePeerDependencies: - encoding - openai@6.16.0(ws@8.18.3)(zod@4.1.13): + openai@6.22.0(ws@8.18.3)(zod@4.1.13): optionalDependencies: ws: 8.18.3 zod: 4.1.13 - optional: true openai@6.9.1(ws@8.18.3)(zod@4.1.13): optionalDependencies: @@ -6977,6 +8480,8 @@ snapshots: picomatch@4.0.3: {} + pify@4.0.1: {} + pirates@4.0.7: {} pkce-challenge@5.0.0: {} @@ -7015,6 +8520,8 @@ snapshots: proxy-from-env@1.1.0: {} + pstree.remy@1.1.8: {} + punycode@2.3.1: {} pure-rand@7.0.1: {} @@ -7036,13 +8543,38 @@ snapshots: react-is@18.3.1: {} + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + regenerate-unicode-properties@10.2.2: + dependencies: + regenerate: 1.4.2 + + regenerate@1.4.2: {} + + regexpu-core@6.4.0: + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 10.2.2 + regjsgen: 0.8.0 + regjsparser: 0.13.0 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.2.1 + + regjsgen@0.8.0: {} + + regjsparser@0.13.0: + dependencies: + jsesc: 3.1.0 + require-directory@2.1.1: {} require-from-string@2.0.2: {} require-in-the-middle@7.5.2: dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) module-details-from-path: 1.0.4 resolve: 1.22.11 transitivePeerDependencies: @@ -7071,7 +8603,7 @@ snapshots: router@2.2.0: dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) depd: 2.0.0 is-promise: 4.0.0 parseurl: 1.3.3 @@ -7089,13 +8621,15 @@ snapshots: safer-buffer@2.1.2: {} + semver@5.7.2: {} + semver@6.3.1: {} semver@7.7.3: {} send@1.2.0: dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@5.5.0) encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 @@ -7158,8 +8692,14 @@ snapshots: signal-exit@4.1.0: {} + simple-update-notifier@2.0.0: + dependencies: + semver: 7.7.3 + simple-wcswidth@1.1.2: {} + slash@2.0.0: {} + slash@3.0.0: {} source-map-support@0.5.13: @@ -7208,6 +8748,10 @@ snapshots: strip-json-comments@3.1.1: {} + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -7236,6 +8780,8 @@ snapshots: toidentifier@1.0.1: {} + touch@3.1.1: {} + tr46@0.0.3: {} ts-api-utils@2.1.0(typescript@5.9.3): @@ -7279,7 +8825,6 @@ snapshots: typescript: 5.9.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - optional: true tslib@2.8.1: {} @@ -7315,10 +8860,23 @@ snapshots: uglify-js@3.19.3: optional: true + undefsafe@2.0.5: {} + undici-types@5.26.5: {} undici-types@6.21.0: {} + unicode-canonical-property-names-ecmascript@2.0.1: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.1 + unicode-property-aliases-ecmascript: 2.2.0 + + unicode-match-property-value-ecmascript@2.2.1: {} + + unicode-property-aliases-ecmascript@2.2.0: {} + unpipe@1.0.0: {} unrs-resolver@1.11.1: @@ -7351,14 +8909,19 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + update-browserslist-db@1.2.3(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 uuid@10.0.0: {} - v8-compile-cache-lib@3.0.1: - optional: true + v8-compile-cache-lib@3.0.1: {} v8-to-istanbul@9.3.0: dependencies: @@ -7372,6 +8935,8 @@ snapshots: dependencies: makeerror: 1.0.12 + web-streams-polyfill@3.3.3: {} + web-streams-polyfill@4.0.0-beta.3: {} webidl-conversions@3.0.1: {} @@ -7432,8 +8997,7 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 - yn@3.1.1: - optional: true + yn@3.1.1: {} yocto-queue@0.1.0: {} diff --git a/tests/observability/configuration/PerRequestSpanProcessorConfiguration.test.ts b/tests/observability/configuration/PerRequestSpanProcessorConfiguration.test.ts index 7abdf668..a07971b2 100644 --- a/tests/observability/configuration/PerRequestSpanProcessorConfiguration.test.ts +++ b/tests/observability/configuration/PerRequestSpanProcessorConfiguration.test.ts @@ -5,7 +5,6 @@ import { describe, it, expect, beforeEach, afterEach } from '@jest/globals'; import { PerRequestSpanProcessorConfiguration, defaultPerRequestSpanProcessorConfigurationProvider, - ObservabilityConfiguration } from '../../../packages/agents-a365-observability/src'; import { RuntimeConfiguration, DefaultConfigurationProvider, ClusterCategory } from '../../../packages/agents-a365-runtime/src'; @@ -21,11 +20,6 @@ describe('PerRequestSpanProcessorConfiguration', () => { }); describe('inheritance', () => { - it('should be instanceof ObservabilityConfiguration', () => { - const config = new PerRequestSpanProcessorConfiguration(); - expect(config).toBeInstanceOf(ObservabilityConfiguration); - }); - it('should be instanceof RuntimeConfiguration', () => { const config = new PerRequestSpanProcessorConfiguration(); expect(config).toBeInstanceOf(RuntimeConfiguration); @@ -36,15 +30,6 @@ describe('PerRequestSpanProcessorConfiguration', () => { expect(config.clusterCategory).toBe(ClusterCategory.gov); expect(config.isDevelopmentEnvironment).toBe(false); }); - - it('should inherit observability settings', () => { - const config = new PerRequestSpanProcessorConfiguration({ - isObservabilityExporterEnabled: () => true, - observabilityLogLevel: () => 'debug' - }); - expect(config.isObservabilityExporterEnabled).toBe(true); - expect(config.observabilityLogLevel).toBe('debug'); - }); }); describe('isPerRequestExportEnabled', () => { @@ -227,6 +212,98 @@ describe('PerRequestSpanProcessorConfiguration', () => { }); }); + describe('perRequestFlushGraceMs', () => { + it('should use override function when provided', () => { + const config = new PerRequestSpanProcessorConfiguration({ + perRequestFlushGraceMs: () => 100 + }); + expect(config.perRequestFlushGraceMs).toBe(100); + }); + + it('should fall back to env var when override not provided', () => { + process.env.A365_PER_REQUEST_FLUSH_GRACE_MS = '500'; + const config = new PerRequestSpanProcessorConfiguration({}); + expect(config.perRequestFlushGraceMs).toBe(500); + }); + + it('should fall back to default 250 when neither override nor env var', () => { + delete process.env.A365_PER_REQUEST_FLUSH_GRACE_MS; + const config = new PerRequestSpanProcessorConfiguration({}); + expect(config.perRequestFlushGraceMs).toBe(250); + }); + + it('should fall back to default for invalid env var', () => { + process.env.A365_PER_REQUEST_FLUSH_GRACE_MS = 'invalid'; + const config = new PerRequestSpanProcessorConfiguration({}); + expect(config.perRequestFlushGraceMs).toBe(250); + }); + + it('should fall back to default for negative values from env var', () => { + process.env.A365_PER_REQUEST_FLUSH_GRACE_MS = '-50'; + const config = new PerRequestSpanProcessorConfiguration({}); + expect(config.perRequestFlushGraceMs).toBe(250); + }); + + it('should fall back to default for zero from env var', () => { + process.env.A365_PER_REQUEST_FLUSH_GRACE_MS = '0'; + const config = new PerRequestSpanProcessorConfiguration({}); + expect(config.perRequestFlushGraceMs).toBe(250); + }); + + it('should fall back to default for negative override', () => { + const config = new PerRequestSpanProcessorConfiguration({ + perRequestFlushGraceMs: () => -10 + }); + expect(config.perRequestFlushGraceMs).toBe(250); + }); + }); + + describe('perRequestMaxTraceAgeMs', () => { + it('should use override function when provided', () => { + const config = new PerRequestSpanProcessorConfiguration({ + perRequestMaxTraceAgeMs: () => 60000 + }); + expect(config.perRequestMaxTraceAgeMs).toBe(60000); + }); + + it('should fall back to env var when override not provided', () => { + process.env.A365_PER_REQUEST_MAX_TRACE_AGE_MS = '120000'; + const config = new PerRequestSpanProcessorConfiguration({}); + expect(config.perRequestMaxTraceAgeMs).toBe(120000); + }); + + it('should fall back to default 1800000 when neither override nor env var', () => { + delete process.env.A365_PER_REQUEST_MAX_TRACE_AGE_MS; + const config = new PerRequestSpanProcessorConfiguration({}); + expect(config.perRequestMaxTraceAgeMs).toBe(1800000); + }); + + it('should fall back to default for invalid env var', () => { + process.env.A365_PER_REQUEST_MAX_TRACE_AGE_MS = 'not-a-number'; + const config = new PerRequestSpanProcessorConfiguration({}); + expect(config.perRequestMaxTraceAgeMs).toBe(1800000); + }); + + it('should fall back to default for negative values from env var', () => { + process.env.A365_PER_REQUEST_MAX_TRACE_AGE_MS = '-1000'; + const config = new PerRequestSpanProcessorConfiguration({}); + expect(config.perRequestMaxTraceAgeMs).toBe(1800000); + }); + + it('should fall back to default for zero from env var', () => { + process.env.A365_PER_REQUEST_MAX_TRACE_AGE_MS = '0'; + const config = new PerRequestSpanProcessorConfiguration({}); + expect(config.perRequestMaxTraceAgeMs).toBe(1800000); + }); + + it('should fall back to default for negative override', () => { + const config = new PerRequestSpanProcessorConfiguration({ + perRequestMaxTraceAgeMs: () => -5000 + }); + expect(config.perRequestMaxTraceAgeMs).toBe(1800000); + }); + }); + describe('constructor', () => { it('should accept no overrides', () => { const config = new PerRequestSpanProcessorConfiguration(); diff --git a/tests/observability/core/PerRequestSpanProcessor.test.ts b/tests/observability/core/PerRequestSpanProcessor.test.ts index f0c593c3..09de9444 100644 --- a/tests/observability/core/PerRequestSpanProcessor.test.ts +++ b/tests/observability/core/PerRequestSpanProcessor.test.ts @@ -4,10 +4,13 @@ import { BasicTracerProvider } from '@opentelemetry/sdk-trace-base'; import { PerRequestSpanProcessor } from '@microsoft/agents-a365-observability/src/tracing/PerRequestSpanProcessor'; -import { runWithExportToken } from '@microsoft/agents-a365-observability/src/tracing/context/token-context'; +import { runWithExportToken, updateExportToken, getExportToken } from '@microsoft/agents-a365-observability/src/tracing/context/token-context'; import type { SpanExporter, ReadableSpan } from '@opentelemetry/sdk-trace-base'; import { ExportResult, ExportResultCode } from '@opentelemetry/core'; import { context, trace } from '@opentelemetry/api'; +import { DefaultConfigurationProvider } from '@microsoft/agents-a365-runtime'; +import { PerRequestSpanProcessorConfiguration } from '@microsoft/agents-a365-observability/src/configuration/PerRequestSpanProcessorConfiguration'; +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks'; describe('PerRequestSpanProcessor', () => { let provider: BasicTracerProvider; @@ -247,6 +250,51 @@ describe('PerRequestSpanProcessor', () => { expect(exportedSpans[0][0].name).toBe('root-span'); }); + it('should export with refreshed token when updateExportToken is called before root span ends', async () => { + const contextManager = new AsyncLocalStorageContextManager(); + contextManager.enable(); + context.setGlobalContextManager(contextManager); + + try { + let authorizationHeader: string | undefined; + const tokenCapturingExporter: SpanExporter = { + export: (spans: ReadableSpan[], resultCallback: (result: ExportResult) => void) => { + const token = getExportToken() ?? null; + if (token) { + authorizationHeader = `Bearer ${token}`; + } + exportedSpans.push([...spans]); + resultCallback({ code: ExportResultCode.SUCCESS }); + }, + shutdown: async () => {} + }; + await recreateProvider(new PerRequestSpanProcessor(tokenCapturingExporter)); + const tracer = provider.getTracer('test'); + + await new Promise((resolve) => { + runWithExportToken('initial-token', () => { + const rootSpan = tracer.startSpan('long-running-root'); + const child = tracer.startSpan('child-work'); + child.end(); + updateExportToken('refreshed-token'); + + // Root ends → triggers flushTrace which restores rootCtx and calls exporter + rootSpan.end(); + + setTimeout(() => resolve(), 100); + }); + }); + + // Verify the exporter built the auth header with the refreshed token, + // proving the mutable TokenHolder was visible through the restored rootCtx + expect(exportedSpans.length).toBeGreaterThanOrEqual(1); + expect(authorizationHeader).toBe('Bearer refreshed-token'); + } finally { + contextManager.disable(); + context.disable(); + } + }); + it('should collect multiple spans from a single trace', async () => { const tracer = provider.getTracer('test'); @@ -345,7 +393,10 @@ describe('PerRequestSpanProcessor', () => { it('should respect custom grace flush timeout', async () => { exportedSpans = []; const customGrace = 30; - await recreateProvider(new PerRequestSpanProcessor(mockExporter, customGrace)); + const configProvider = new DefaultConfigurationProvider(() => new PerRequestSpanProcessorConfiguration({ + perRequestFlushGraceMs: () => customGrace, + })); + await recreateProvider(new PerRequestSpanProcessor(mockExporter, configProvider)); const tracer = provider.getTracer('test'); @@ -407,7 +458,11 @@ describe('PerRequestSpanProcessor', () => { const customGrace = 10; const customMaxAge = 1000; - await recreateProvider(new PerRequestSpanProcessor(mockExporter, customGrace, customMaxAge)); + const configProvider = new DefaultConfigurationProvider(() => new PerRequestSpanProcessorConfiguration({ + perRequestFlushGraceMs: () => customGrace, + perRequestMaxTraceAgeMs: () => customMaxAge, + })); + await recreateProvider(new PerRequestSpanProcessor(mockExporter, configProvider)); const tracer = provider.getTracer('test'); @@ -463,7 +518,11 @@ describe('PerRequestSpanProcessor', () => { const customGrace = 250; const customMaxAge = 10; - await recreateProvider(new PerRequestSpanProcessor(mockExporter, customGrace, customMaxAge)); + const configProvider = new DefaultConfigurationProvider(() => new PerRequestSpanProcessorConfiguration({ + perRequestFlushGraceMs: () => customGrace, + perRequestMaxTraceAgeMs: () => customMaxAge, + })); + await recreateProvider(new PerRequestSpanProcessor(mockExporter, configProvider)); const tracer = provider.getTracer('test'); @@ -490,6 +549,8 @@ describe('PerRequestSpanProcessor', () => { delete process.env.A365_PER_REQUEST_MAX_TRACES; delete process.env.A365_PER_REQUEST_MAX_SPANS_PER_TRACE; delete process.env.A365_PER_REQUEST_MAX_CONCURRENT_EXPORTS; + delete process.env.A365_PER_REQUEST_FLUSH_GRACE_MS; + delete process.env.A365_PER_REQUEST_MAX_TRACE_AGE_MS; await recreateProvider(new PerRequestSpanProcessor(mockExporter)); @@ -498,12 +559,16 @@ describe('PerRequestSpanProcessor', () => { expect(proc.maxBufferedTraces).toBe(1000); expect(proc.maxSpansPerTrace).toBe(5000); expect(proc.maxConcurrentExports).toBe(20); + expect(proc.flushGraceMs).toBe(250); + expect(proc.maxTraceAgeMs).toBe(1800000); }); it('should fallback to defaults for invalid env var values', async () => { process.env.A365_PER_REQUEST_MAX_TRACES = 'not-a-number'; process.env.A365_PER_REQUEST_MAX_SPANS_PER_TRACE = ''; process.env.A365_PER_REQUEST_MAX_CONCURRENT_EXPORTS = 'NaN'; + process.env.A365_PER_REQUEST_FLUSH_GRACE_MS = 'abc'; + process.env.A365_PER_REQUEST_MAX_TRACE_AGE_MS = ''; await recreateProvider(new PerRequestSpanProcessor(mockExporter)); @@ -512,12 +577,16 @@ describe('PerRequestSpanProcessor', () => { expect(proc.maxBufferedTraces).toBe(1000); expect(proc.maxSpansPerTrace).toBe(5000); expect(proc.maxConcurrentExports).toBe(20); + expect(proc.flushGraceMs).toBe(250); + expect(proc.maxTraceAgeMs).toBe(1800000); }); it('should parse valid numeric string env vars correctly', async () => { process.env.A365_PER_REQUEST_MAX_TRACES = '50'; process.env.A365_PER_REQUEST_MAX_SPANS_PER_TRACE = '100'; process.env.A365_PER_REQUEST_MAX_CONCURRENT_EXPORTS = '5'; + process.env.A365_PER_REQUEST_FLUSH_GRACE_MS = '500'; + process.env.A365_PER_REQUEST_MAX_TRACE_AGE_MS = '60000'; await recreateProvider(new PerRequestSpanProcessor(mockExporter)); @@ -526,6 +595,8 @@ describe('PerRequestSpanProcessor', () => { expect(proc.maxBufferedTraces).toBe(50); expect(proc.maxSpansPerTrace).toBe(100); expect(proc.maxConcurrentExports).toBe(5); + expect(proc.flushGraceMs).toBe(500); + expect(proc.maxTraceAgeMs).toBe(60000); }); it('should handle shutdown gracefully', async () => { diff --git a/tests/observability/core/custom-logger.test.ts b/tests/observability/core/custom-logger.test.ts index 5b4061a8..d4381d5f 100644 --- a/tests/observability/core/custom-logger.test.ts +++ b/tests/observability/core/custom-logger.test.ts @@ -3,6 +3,7 @@ import { setLogger, getLogger, resetLogger, ILogger } from '@microsoft/agents-a365-observability/src/utils/logging'; import { ObservabilityBuilder } from '@microsoft/agents-a365-observability/src/ObservabilityBuilder'; +import { ExporterEventNames } from '@microsoft/agents-a365-observability'; describe('Custom Logger Support', () => { beforeEach(() => { @@ -156,11 +157,11 @@ describe('Custom Logger Support', () => { setLogger(customLogger); const logger = getLogger(); - logger.event('test-event', true, 150); - logger.event('test-event-fail', false, 200); + logger.event(ExporterEventNames.EXPORT, true, 150); + logger.event(ExporterEventNames.EXPORT_GROUP, false, 200); - expect(customLogger.event).toHaveBeenCalledWith('test-event', true, 150); - expect(customLogger.event).toHaveBeenCalledWith('test-event-fail', false, 200); + expect(customLogger.event).toHaveBeenCalledWith(ExporterEventNames.EXPORT, true, 150); + expect(customLogger.event).toHaveBeenCalledWith(ExporterEventNames.EXPORT_GROUP, false, 200); expect(customLogger.event).toHaveBeenCalledTimes(2); }); }); @@ -169,63 +170,63 @@ describe('Custom Logger Support', () => { it('should respect log level settings for events (info succeeds, error fails)', () => { process.env.A365_OBSERVABILITY_LOG_LEVEL = 'info|error'; resetLogger(); - + const logSpy = jest.spyOn(console, 'log').mockImplementation(); const errorSpy = jest.spyOn(console, 'error').mockImplementation(); const logger = getLogger(); - logger.event('success-event', true, 100); - logger.event('failed-event', false, 200); + logger.event(ExporterEventNames.EXPORT, true, 100); + logger.event(ExporterEventNames.EXPORT_GROUP, false, 200); - expect(logSpy).toHaveBeenCalledWith('[EVENT]: success-event succeeded in 100ms'); - expect(errorSpy).toHaveBeenCalledWith('[EVENT]: failed-event failed in 200ms'); + expect(logSpy).toHaveBeenCalledWith('[EVENT]: agent365-export succeeded in 100ms'); + expect(errorSpy).toHaveBeenCalledWith('[EVENT]: export-group failed in 200ms'); logSpy.mockRestore(); errorSpy.mockRestore(); }); - it('should format event with optional message and correlationId correctly', () => { + it('should format event with optional message and details correctly', () => { process.env.A365_OBSERVABILITY_LOG_LEVEL = 'info|error'; resetLogger(); - + const logSpy = jest.spyOn(console, 'log').mockImplementation(); const errorSpy = jest.spyOn(console, 'error').mockImplementation(); const logger = getLogger(); - logger.event('operation', true, 150, 'Task completed successfully', 'corr-123'); - logger.event('task', false, 250, 'Connection timeout', 'corr-456'); + logger.event(ExporterEventNames.EXPORT, true, 150, 'Task completed successfully', { correlationId: 'corr-123' }); + logger.event(ExporterEventNames.EXPORT_GROUP, false, 250, 'Connection timeout', { correlationId: 'corr-456', tenantId: 'tenant1' }); - expect(logSpy).toHaveBeenCalledWith('[EVENT]: operation succeeded in 150ms - Task completed successfully [corr-123]'); - expect(errorSpy).toHaveBeenCalledWith('[EVENT]: task failed in 250ms - Connection timeout [corr-456]'); + expect(logSpy).toHaveBeenCalledWith('[EVENT]: agent365-export succeeded in 150ms - Task completed successfully {"correlationId":"corr-123"}'); + expect(errorSpy).toHaveBeenCalledWith('[EVENT]: export-group failed in 250ms - Connection timeout {"correlationId":"corr-456","tenantId":"tenant1"}'); logSpy.mockRestore(); errorSpy.mockRestore(); }); - it('should format event with only message (no correlationId)', () => { + it('should format event with only message (no details)', () => { process.env.A365_OBSERVABILITY_LOG_LEVEL = 'info'; resetLogger(); - + const logSpy = jest.spyOn(console, 'log').mockImplementation(); const logger = getLogger(); - logger.event('sync', true, 500, 'Data synced from 3 sources'); + logger.event(ExporterEventNames.EXPORT_PARTITION_SPAN_MISSING_IDENTITY, true, 500, 'Data synced from 3 sources'); - expect(logSpy).toHaveBeenCalledWith('[EVENT]: sync succeeded in 500ms - Data synced from 3 sources'); + expect(logSpy).toHaveBeenCalledWith('[EVENT]: export-partition-span-missing-identity succeeded in 500ms - Data synced from 3 sources'); logSpy.mockRestore(); }); - it('should format event with only correlationId (no message)', () => { + it('should format event with only details (no message)', () => { process.env.A365_OBSERVABILITY_LOG_LEVEL = 'error'; resetLogger(); - + const errorSpy = jest.spyOn(console, 'error').mockImplementation(); const logger = getLogger(); - logger.event('cleanup', false, 100, undefined, 'corr-789'); + logger.event(ExporterEventNames.EXPORT_GROUP, false, 100, undefined, { correlationId: 'corr-789' }); - expect(errorSpy).toHaveBeenCalledWith('[EVENT]: cleanup failed in 100ms [corr-789]'); + expect(errorSpy).toHaveBeenCalledWith('[EVENT]: export-group failed in 100ms {"correlationId":"corr-789"}'); errorSpy.mockRestore(); }); @@ -247,10 +248,10 @@ describe('Custom Logger Support', () => { const currentLogger = getLogger(); currentLogger.info('test message'); - currentLogger.event('test-event', true, 100); - + currentLogger.event(ExporterEventNames.EXPORT, true, 100); + expect(customLogger.info).toHaveBeenCalledWith('test message'); - expect(customLogger.event).toHaveBeenCalledWith('test-event', true, 100); + expect(customLogger.event).toHaveBeenCalledWith(ExporterEventNames.EXPORT, true, 100); }); }); }); diff --git a/tests/observability/core/observabilityBuilder-configProvider.test.ts b/tests/observability/core/observabilityBuilder-configProvider.test.ts new file mode 100644 index 00000000..cba65ee2 --- /dev/null +++ b/tests/observability/core/observabilityBuilder-configProvider.test.ts @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { ObservabilityBuilder } from '@microsoft/agents-a365-observability/src/ObservabilityBuilder'; +import { ObservabilityManager } from '@microsoft/agents-a365-observability/src/ObservabilityManager'; +import { ObservabilityConfiguration } from '@microsoft/agents-a365-observability/src/configuration/ObservabilityConfiguration'; +import { DefaultConfigurationProvider } from '@microsoft/agents-a365-runtime'; + +// Capture Agent365Exporter constructor args without performing network calls. +jest.mock('@microsoft/agents-a365-observability/src/tracing/exporter/Agent365Exporter', () => ({ + Agent365Exporter: class { + constructor(opts: any, configProvider?: any) { + (global as any).__capturedOpts = opts; + (global as any).__capturedConfigProvider = configProvider; + } + export() {/* no-op */} + shutdown() {/* no-op */} + forceFlush() {/* no-op */} + } +})); + +// Capture setLogger calls. +let capturedLogger: any = null; +jest.mock('@microsoft/agents-a365-observability/src/utils/logging', () => { + const actual = jest.requireActual('@microsoft/agents-a365-observability/src/utils/logging') as any; + return { ...actual, __esModule: true, default: actual.default, DefaultLogger: actual.DefaultLogger, + setLogger: (l: any) => { capturedLogger = l; actual.setLogger(l); } }; +}); + +const makeProvider = (exporterEnabled?: boolean) => + new DefaultConfigurationProvider(() => new ObservabilityConfiguration({ + ...(exporterEnabled !== undefined && { isObservabilityExporterEnabled: () => exporterEnabled }), + })); + +const capturedOpts = () => (global as any).__capturedOpts as any | undefined; +const capturedConfigProvider = () => (global as any).__capturedConfigProvider as any | undefined; + +describe('ObservabilityBuilder configProvider', () => { + beforeEach(() => { + delete process.env.ENABLE_A365_OBSERVABILITY_EXPORTER; + delete (global as any).__capturedOpts; + delete (global as any).__capturedConfigProvider; + capturedLogger = null; + jest.spyOn(console, 'log').mockImplementation(() => {}); + jest.spyOn(console, 'warn').mockImplementation(() => {}); + jest.spyOn(console, 'error').mockImplementation(() => {}); + }); + + afterEach(async () => { + delete process.env.ENABLE_A365_OBSERVABILITY_EXPORTER; + await ObservabilityManager.shutdown(); + const actualLogging = jest.requireActual('@microsoft/agents-a365-observability/src/utils/logging') as any; + if (typeof actualLogging.resetLogger === 'function') { + actualLogging.resetLogger(); + } + jest.restoreAllMocks(); + }); + + it('withConfigurationProvider returns the builder for chaining', () => { + const builder = new ObservabilityBuilder(); + expect(builder.withConfigurationProvider(makeProvider())).toBe(builder); + }); + + it('threads configProvider into Agent365ExporterOptions alongside other options', () => { + const provider = makeProvider(true); + new ObservabilityBuilder() + .withConfigurationProvider(provider) + .withExporterOptions({ maxQueueSize: 42 }) + .withTokenResolver(() => 'tok') + .build(); + + expect(capturedOpts()).toBeDefined(); + expect(capturedConfigProvider()).toBe(provider); + expect(capturedOpts().configProvider).toBeUndefined(); + expect(capturedOpts().maxQueueSize).toBe(42); + expect(capturedOpts().tokenResolver('a', 'b')).toBe('tok'); + }); + + it('creates DefaultLogger from configProvider when no custom logger is set', () => { + new ObservabilityBuilder().withConfigurationProvider(makeProvider(false)).build(); + expect(capturedLogger).toBeDefined(); + expect(typeof capturedLogger.info).toBe('function'); + }); + + it('custom logger takes precedence over configProvider-based logger', () => { + const custom = { info: jest.fn(), warn: jest.fn(), error: jest.fn(), event: jest.fn() }; + new ObservabilityBuilder() + .withConfigurationProvider(makeProvider(false)) + .withCustomLogger(custom) + .build(); + expect(capturedLogger).toBe(custom); + }); + + it('does not call setLogger when no configProvider or customLogger is provided', () => { + new ObservabilityBuilder().build(); + expect(capturedLogger).toBeNull(); + }); + + it('builds without configProvider using env var fallback (backward compat)', () => { + process.env.ENABLE_A365_OBSERVABILITY_EXPORTER = 'true'; + new ObservabilityBuilder().withTokenResolver(() => 't').build(); + expect(capturedOpts()).toBeDefined(); + expect(capturedConfigProvider()).toBeUndefined(); + }); + + it('ObservabilityManager.start() passes configProvider through to exporter', () => { + const provider = makeProvider(true); + ObservabilityManager.start({ serviceName: 'svc', tokenResolver: () => 't', configProvider: provider }); + expect(capturedConfigProvider()).toBe(provider); + }); +}); diff --git a/tests/observability/core/output-scope.test.ts b/tests/observability/core/output-scope.test.ts new file mode 100644 index 00000000..42d1b484 --- /dev/null +++ b/tests/observability/core/output-scope.test.ts @@ -0,0 +1,200 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { describe, it, expect, beforeAll, afterAll, beforeEach } from '@jest/globals'; +import { BasicTracerProvider, InMemorySpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'; +import { trace, context as otelContext } from '@opentelemetry/api'; +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks'; + +import { + OutputScope, + AgentDetails, + TenantDetails, + OutputResponse, + OpenTelemetryConstants, + ParentSpanRef, +} from '@microsoft/agents-a365-observability'; + +describe('OutputScope', () => { + const testAgentDetails: AgentDetails = { + agentId: 'test-agent-123', + agentName: 'Test Agent', + agentDescription: 'A test agent for output scope testing', + }; + + const testTenantDetails: TenantDetails = { + tenantId: '12345678-1234-5678-1234-567812345678', + }; + + let exporter: InMemorySpanExporter; + let provider: BasicTracerProvider; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let flushProvider: any; + let contextManager: AsyncLocalStorageContextManager; + + beforeAll(() => { + contextManager = new AsyncLocalStorageContextManager(); + contextManager.enable(); + otelContext.setGlobalContextManager(contextManager); + + exporter = new InMemorySpanExporter(); + const processor = new SimpleSpanProcessor(exporter); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const globalProvider: any = trace.getTracerProvider(); + if (globalProvider && typeof globalProvider.addSpanProcessor === 'function') { + globalProvider.addSpanProcessor(processor); + flushProvider = globalProvider; + } else { + provider = new BasicTracerProvider({ + spanProcessors: [processor] + }); + trace.setGlobalTracerProvider(provider); + flushProvider = provider; + } + }); + + beforeEach(() => { + exporter.reset(); + }); + + afterAll(async () => { + exporter.reset(); + await provider?.shutdown?.(); + contextManager.disable(); + otelContext.disable(); + }); + + function getLastSpan() { + const spans = exporter.getFinishedSpans(); + expect(spans.length).toBeGreaterThan(0); + const span = spans[spans.length - 1]; + return { span, attributes: span.attributes }; + } + + it('should create scope with output messages', async () => { + const response: OutputResponse = { messages: ['First message', 'Second message'] }; + + const scope = OutputScope.start(response, testAgentDetails, testTenantDetails); + + expect(scope).toBeInstanceOf(OutputScope); + scope.dispose(); + + await flushProvider.forceFlush(); + const { span, attributes } = getLastSpan(); + + // Verify span name contains operation name and agent id + expect(span.name).toContain('output_messages'); + expect(span.name).toContain(testAgentDetails.agentId); + + // Verify output messages are set as JSON array + const outputValue = attributes[OpenTelemetryConstants.GEN_AI_OUTPUT_MESSAGES_KEY] as string; + expect(outputValue).toBeDefined(); + const parsed = JSON.parse(outputValue); + expect(parsed).toEqual(['First message', 'Second message']); + }); + + it('should record output messages by appending to accumulated messages', async () => { + const response: OutputResponse = { messages: ['Initial'] }; + + const scope = OutputScope.start(response, testAgentDetails, testTenantDetails); + + scope.recordOutputMessages(['Appended 1']); + scope.recordOutputMessages(['Appended 2', 'Appended 3']); + + scope.dispose(); + + await flushProvider.forceFlush(); + const { attributes } = getLastSpan(); + + const outputValue = attributes[OpenTelemetryConstants.GEN_AI_OUTPUT_MESSAGES_KEY] as string; + expect(outputValue).toBeDefined(); + // All messages should be present (initial + all appended) as JSON array + const parsed = JSON.parse(outputValue); + expect(parsed).toEqual(['Initial', 'Appended 1', 'Appended 2', 'Appended 3']); + }); + + it('should use parent span reference to link span to parent context', async () => { + const response: OutputResponse = { messages: ['Test'] }; + const parentTraceId = '1234567890abcdef1234567890abcdef'; + const parentSpanId = 'abcdefabcdef1234'; + + const parentSpanRef: ParentSpanRef = { + traceId: parentTraceId, + spanId: parentSpanId, + }; + + const scope = OutputScope.start(response, testAgentDetails, testTenantDetails, parentSpanRef); + scope.dispose(); + + await flushProvider.forceFlush(); + const { span } = getLastSpan(); + + // Verify span inherits parent's trace_id + expect(span.spanContext().traceId).toBe(parentTraceId); + + // Verify span's parent_span_id matches + expect(span.parentSpanContext?.spanId).toBe(parentSpanId); + }); + + it('should end the span on dispose', async () => { + const response: OutputResponse = { messages: ['Test'] }; + + const scope = OutputScope.start(response, testAgentDetails, testTenantDetails); + scope.dispose(); + + await flushProvider.forceFlush(); + + // Verify span was created and ended + const spans = exporter.getFinishedSpans(); + expect(spans.length).toBe(1); + }); + + it('should create scope with empty messages', async () => { + const response: OutputResponse = { messages: [] }; + + const scope = OutputScope.start(response, testAgentDetails, testTenantDetails); + scope.dispose(); + + await flushProvider.forceFlush(); + const { span } = getLastSpan(); + expect(span.name).toContain('output_messages'); + }); + + it('should set gen_ai.operation.name to output_messages', async () => { + const response: OutputResponse = { messages: ['Test'] }; + + const scope = OutputScope.start(response, testAgentDetails, testTenantDetails); + scope.dispose(); + + await flushProvider.forceFlush(); + const { attributes } = getLastSpan(); + expect(attributes[OpenTelemetryConstants.GEN_AI_OPERATION_NAME_KEY]).toBe('output_messages'); + }); + + it('should set agent details on the span', async () => { + const response: OutputResponse = { messages: ['Test'] }; + + const scope = OutputScope.start(response, testAgentDetails, testTenantDetails); + scope.dispose(); + + await flushProvider.forceFlush(); + const { attributes } = getLastSpan(); + expect(attributes[OpenTelemetryConstants.GEN_AI_AGENT_ID_KEY]).toBe(testAgentDetails.agentId); + expect(attributes[OpenTelemetryConstants.GEN_AI_AGENT_NAME_KEY]).toBe(testAgentDetails.agentName); + }); + + it('should not throw when recordOutputMessages is called multiple times', () => { + const response: OutputResponse = { messages: ['Initial'] }; + + const scope = OutputScope.start(response, testAgentDetails, testTenantDetails); + + expect(() => { + scope.recordOutputMessages(['Message 1']); + scope.recordOutputMessages(['Message 2']); + scope.recordOutputMessages(['Message 3']); + }).not.toThrow(); + + scope.dispose(); + }); +}); diff --git a/tests/observability/core/parent-span-ref.test.ts b/tests/observability/core/parent-span-ref.test.ts index 87e42518..95b33090 100644 --- a/tests/observability/core/parent-span-ref.test.ts +++ b/tests/observability/core/parent-span-ref.test.ts @@ -3,7 +3,7 @@ import { describe, it, expect, beforeEach, beforeAll, afterAll } from '@jest/globals'; import { BasicTracerProvider, InMemorySpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'; -import { trace, context as otelContext } from '@opentelemetry/api'; +import { trace, context as otelContext, TraceFlags } from '@opentelemetry/api'; import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks'; import { ParentSpanRef, @@ -245,4 +245,131 @@ describe('ParentSpanRef - Explicit Parent Span Support', () => { expect(childSpan!.parentSpanContext?.spanId).toBe(parentSpan!.spanContext().spanId); }); }); + + describe('traceFlags propagation', () => { + it('should record child spans when parentRef.traceFlags is SAMPLED', async () => { + const parentRef: ParentSpanRef = { + traceId: '0123456789abcdef0123456789abcdef', + spanId: '0123456789abcdef', + traceFlags: TraceFlags.SAMPLED, + }; + + runWithParentSpanRef(parentRef, () => { + const invokeAgentDetails: InvokeAgentDetails = { + agentId: 'sampled-agent', + }; + + const scope = InvokeAgentScope.start(invokeAgentDetails, testTenantDetails); + scope.dispose(); + }); + + await flushProvider.forceFlush(); + + const spans = exporter.getFinishedSpans(); + const childSpan = spans.find(s => + s.name.toLowerCase().includes('invokeagent') || s.name.toLowerCase().includes('invoke_agent') + ); + + expect(childSpan).toBeDefined(); + expect(childSpan!.spanContext().traceId).toBe(parentRef.traceId); + expect(childSpan!.parentSpanContext?.spanId).toBe(parentRef.spanId); + expect(childSpan!.spanContext().traceFlags).toBe(TraceFlags.SAMPLED); + }); + + it('should not record child spans when parentRef.traceFlags is NONE', async () => { + const parentRef: ParentSpanRef = { + traceId: 'abcdef0123456789abcdef0123456789', + spanId: 'abcdef0123456789', + traceFlags: TraceFlags.NONE, + }; + + runWithParentSpanRef(parentRef, () => { + const invokeAgentDetails: InvokeAgentDetails = { + agentId: 'unsampled-agent', + }; + + const scope = InvokeAgentScope.start(invokeAgentDetails, testTenantDetails); + scope.dispose(); + }); + + await flushProvider.forceFlush(); + + const spans = exporter.getFinishedSpans(); + // When traceFlags is NONE, the span should still be created but not recorded/exported + const childSpan = spans.find(s => + (s.name.toLowerCase().includes('invokeagent') || s.name.toLowerCase().includes('invoke_agent')) && + s.spanContext().traceId === parentRef.traceId + ); + + // The span should not be exported when traceFlags is NONE + expect(childSpan).toBeUndefined(); + }); + + it('should default to SAMPLED when parentRef.traceFlags is not provided and no active span matches', async () => { + const parentRef: ParentSpanRef = { + traceId: 'fedcba9876543210fedcba9876543210', + spanId: 'fedcba9876543210', + // traceFlags is not provided — should default to SAMPLED + }; + + runWithParentSpanRef(parentRef, () => { + const invokeAgentDetails: InvokeAgentDetails = { + agentId: 'default-sampled-agent', + }; + + const scope = InvokeAgentScope.start(invokeAgentDetails, testTenantDetails); + scope.dispose(); + }); + + await flushProvider.forceFlush(); + + const spans = exporter.getFinishedSpans(); + const childSpan = spans.find(s => + (s.name.toLowerCase().includes('invokeagent') || s.name.toLowerCase().includes('invoke_agent')) && + s.spanContext().traceId === parentRef.traceId + ); + + // Should be recorded when traceFlags defaults to SAMPLED + expect(childSpan).toBeDefined(); + expect(childSpan!.spanContext().traceFlags).toBe(TraceFlags.SAMPLED); + }); + + it('should inherit traceFlags from active span when parentRef.traceFlags is not provided but traceId matches', async () => { + const tracer = trace.getTracer('test'); + const rootSpan = tracer.startSpan('active-root-span'); + const parentSpanContext = rootSpan.spanContext(); + + const parentRef: ParentSpanRef = { + traceId: parentSpanContext.traceId, + spanId: parentSpanContext.spanId, + // traceFlags is not provided + }; + + const baseCtx = trace.setSpan(otelContext.active(), rootSpan); + await otelContext.with(baseCtx, async () => { + runWithParentSpanRef(parentRef, () => { + const invokeAgentDetails: InvokeAgentDetails = { + agentId: 'inherited-flags-agent', + }; + + const scope = InvokeAgentScope.start(invokeAgentDetails, testTenantDetails); + scope.dispose(); + }); + }); + + rootSpan.end(); + + await flushProvider.forceFlush(); + + const spans = exporter.getFinishedSpans(); + const childSpan = spans.find(s => + (s.name.toLowerCase().includes('invokeagent') || s.name.toLowerCase().includes('invoke_agent')) && + s.spanContext().traceId === parentRef.traceId + ); + + // Should be recorded with traceFlags inherited from active span + expect(childSpan).toBeDefined(); + expect(childSpan!.spanContext().traceFlags).toBe(parentSpanContext.traceFlags); + }); + }); }); diff --git a/tests/observability/core/scopes.test.ts b/tests/observability/core/scopes.test.ts index 83cd160b..14d7fad1 100644 --- a/tests/observability/core/scopes.test.ts +++ b/tests/observability/core/scopes.test.ts @@ -1,4 +1,5 @@ -import { describe, it, expect, beforeAll, afterAll, jest } from '@jest/globals'; +import { describe, it, expect, beforeAll, afterAll, afterEach, jest } from '@jest/globals'; +import { trace } from '@opentelemetry/api'; import { ExecuteToolScope, @@ -14,6 +15,7 @@ import { OpenTelemetryConstants, OpenTelemetryScope, } from '@microsoft/agents-a365-observability'; +import { BasicTracerProvider, InMemorySpanExporter, SimpleSpanProcessor, ReadableSpan } from '@opentelemetry/sdk-trace-base'; // Mock console to avoid cluttering test output const originalConsoleWarn = console.warn; @@ -382,4 +384,130 @@ describe('Scopes', () => { }).not.toThrow(); }); }); + + describe('Custom start and end time', () => { + let exporter: InMemorySpanExporter; + let provider: BasicTracerProvider | undefined; + + beforeAll(() => { + exporter = new InMemorySpanExporter(); + const processor = new SimpleSpanProcessor(exporter); + + // OTel API only allows setting the global tracer provider once per process. + // Reuse the existing provider when possible so other test files are not affected. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const globalProvider: any = trace.getTracerProvider(); + if (globalProvider && typeof globalProvider.addSpanProcessor === 'function') { + globalProvider.addSpanProcessor(processor); + } else { + provider = new BasicTracerProvider({ + spanProcessors: [processor], + }); + trace.setGlobalTracerProvider(provider); + } + }); + + afterEach(() => { + exporter.reset(); + }); + + afterAll(async () => { + exporter.reset(); + await provider?.shutdown?.(); + }); + + /** Extract the last finished span from the in-memory exporter. */ + const getFinishedSpan = (): ReadableSpan => { + const spans = exporter.getFinishedSpans(); + expect(spans.length).toBeGreaterThanOrEqual(1); + return spans[spans.length - 1]; + }; + + /** Convert an hrtime tuple to milliseconds. */ + const hrtimeToMs = (hr: [number, number]): number => hr[0] * 1000 + hr[1] / 1_000_000; + + it('should record constructor-provided start and end times on the span', () => { + const customStart = 1700000000000; // 2023-11-14T22:13:20Z + const customEnd = 1700000005000; // 5 seconds later + + const scope = ExecuteToolScope.start( + { toolName: 'my-tool' }, testAgentDetails, testTenantDetails, + undefined, undefined, undefined, + customStart, customEnd + ); + scope.dispose(); + + const span = getFinishedSpan(); + expect(hrtimeToMs(span.startTime as [number, number])).toBeCloseTo(customStart, -1); + expect(hrtimeToMs(span.endTime as [number, number])).toBeCloseTo(customEnd, -1); + expect(span.attributes['operation.duration']).toBeCloseTo(5.0, 1); + }); + + it('setEndTime should override end time when called before dispose', () => { + const customStart = 1700000040000; + const laterEnd = 1700000048000; // 8 seconds later + + const scope = ExecuteToolScope.start( + { toolName: 'my-tool' }, testAgentDetails, testTenantDetails, + undefined, undefined, undefined, + customStart + ); + scope.setEndTime(laterEnd); + scope.dispose(); + + const span = getFinishedSpan(); + expect(hrtimeToMs(span.startTime as [number, number])).toBeCloseTo(customStart, -1); + expect(hrtimeToMs(span.endTime as [number, number])).toBeCloseTo(laterEnd, -1); + expect(span.attributes['operation.duration']).toBeCloseTo(8.0, 1); + }); + + it('should support Date objects as start and end times', () => { + const customStart = new Date('2023-11-14T22:13:20.000Z'); + const customEnd = new Date('2023-11-14T22:13:25.000Z'); // 5 seconds later + + const scope = ExecuteToolScope.start( + { toolName: 'my-tool' }, testAgentDetails, testTenantDetails, + undefined, undefined, undefined, + customStart, customEnd + ); + scope.dispose(); + + const span = getFinishedSpan(); + expect(hrtimeToMs(span.startTime as [number, number])).toBeCloseTo(customStart.getTime(), -1); + expect(hrtimeToMs(span.endTime as [number, number])).toBeCloseTo(customEnd.getTime(), -1); + expect(span.attributes['operation.duration']).toBeCloseTo(5.0, 1); + }); + + it('should support HrTime tuples as start and end times', () => { + // HrTime: [seconds, nanoseconds] + const customStart: [number, number] = [1700000000, 0]; // 2023-11-14T22:13:20Z + const customEnd: [number, number] = [1700000005, 500000000]; // 5.5 seconds later + + const scope = ExecuteToolScope.start( + { toolName: 'my-tool' }, testAgentDetails, testTenantDetails, + undefined, undefined, undefined, + customStart, customEnd + ); + scope.dispose(); + + const span = getFinishedSpan(); + expect(hrtimeToMs(span.startTime as [number, number])).toBeCloseTo(1700000000000, -1); + expect(hrtimeToMs(span.endTime as [number, number])).toBeCloseTo(1700000005500, -1); + expect(span.attributes['operation.duration']).toBeCloseTo(5.5, 1); + }); + + it('should use wall-clock time when no custom times are provided', () => { + const before = Date.now(); + const scope = ExecuteToolScope.start({ toolName: 'my-tool' }, testAgentDetails, testTenantDetails); + scope.dispose(); + const after = Date.now(); + + const span = getFinishedSpan(); + const spanStartMs = hrtimeToMs(span.startTime as [number, number]); + const spanEndMs = hrtimeToMs(span.endTime as [number, number]); + + expect(spanStartMs).toBeGreaterThanOrEqual(before - 1); + expect(spanEndMs).toBeLessThanOrEqual(after + 1); + }); + }); }); diff --git a/tests/observability/core/token-context.test.ts b/tests/observability/core/token-context.test.ts new file mode 100644 index 00000000..25a60cf5 --- /dev/null +++ b/tests/observability/core/token-context.test.ts @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { describe, it, expect, beforeAll, afterAll } from '@jest/globals'; +import { context, createContextKey } from '@opentelemetry/api'; +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks'; +import { + runWithExportToken, + updateExportToken, + getExportToken, +} from '@microsoft/agents-a365-observability/src/tracing/context/token-context'; + +describe('token-context', () => { + let contextManager: AsyncLocalStorageContextManager; + + beforeAll(() => { + contextManager = new AsyncLocalStorageContextManager(); + contextManager.enable(); + context.setGlobalContextManager(contextManager); + }); + + afterAll(() => { + contextManager.disable(); + context.disable(); + }); + + describe('updateExportToken', () => { + it('should return false when called outside runWithExportToken context', () => { + expect(updateExportToken('some-token')).toBe(false); + }); + + it('should return true and mutate the token visible to getExportToken', () => { + runWithExportToken('v1', () => { + expect(updateExportToken('v2')).toBe(true); + expect(getExportToken()).toBe('v2'); + + // successive updates keep working + expect(updateExportToken('v3')).toBe(true); + expect(getExportToken()).toBe('v3'); + }); + }); + }); + + describe('getExportToken - backward compatibility', () => { + it('should handle legacy raw string values stored directly in context', () => { + const legacyKey = createContextKey('a365_export_token'); + const ctxWithLegacyToken = context.active().setValue(legacyKey, 'legacy-string-token'); + + expect(getExportToken(ctxWithLegacyToken)).toBe('legacy-string-token'); + }); + + it('should return undefined for non-string, non-TokenHolder values in context', () => { + const key = createContextKey('a365_export_token'); + const ctxWithBadValue = context.active().setValue(key, 12345); + + expect(getExportToken(ctxWithBadValue)).toBeUndefined(); + }); + }); +}); diff --git a/tests/observability/core/trace-context-propagation.test.ts b/tests/observability/core/trace-context-propagation.test.ts new file mode 100644 index 00000000..8c8d2779 --- /dev/null +++ b/tests/observability/core/trace-context-propagation.test.ts @@ -0,0 +1,174 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { describe, it, expect, beforeEach, beforeAll, afterAll } from '@jest/globals'; +import { BasicTracerProvider, InMemorySpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'; +import { trace, context as otelContext, propagation, TraceFlags } from '@opentelemetry/api'; +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks'; +import { W3CTraceContextPropagator } from '@opentelemetry/core'; +import { + ParentSpanRef, + injectTraceContext, + extractTraceContext, + runWithExtractedTraceContext, + InvokeAgentScope, + TenantDetails, +} from '@microsoft/agents-a365-observability'; + +describe('Trace Context Propagation', () => { + let provider: BasicTracerProvider; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let flushProvider: any; + let exporter: InMemorySpanExporter; + let contextManager: AsyncLocalStorageContextManager; + + const testTenantDetails: TenantDetails = { tenantId: 'test-tenant' }; + + beforeAll(() => { + contextManager = new AsyncLocalStorageContextManager(); + contextManager.enable(); + otelContext.setGlobalContextManager(contextManager); + propagation.setGlobalPropagator(new W3CTraceContextPropagator()); + + exporter = new InMemorySpanExporter(); + const processor = new SimpleSpanProcessor(exporter); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const globalProvider: any = trace.getTracerProvider(); + if (globalProvider && typeof globalProvider.addSpanProcessor === 'function') { + globalProvider.addSpanProcessor(processor); + flushProvider = globalProvider; + } else { + provider = new BasicTracerProvider({ spanProcessors: [processor] }); + trace.setGlobalTracerProvider(provider); + flushProvider = provider; + } + }); + + beforeEach(() => exporter.reset()); + + afterAll(async () => { + exporter.reset(); + await provider?.shutdown?.(); + contextManager.disable(); + otelContext.disable(); + }); + + describe('injectTraceContext', () => { + it('should inject traceparent header from active span', () => { + const tracer = trace.getTracer('test'); + const span = tracer.startSpan('sender'); + const { traceId, spanId, traceFlags } = span.spanContext(); + const ctx = trace.setSpan(otelContext.active(), span); + + otelContext.with(ctx, () => { + const headers: Record = {}; + const result = injectTraceContext(headers); + expect(result).toBe(headers); + + const traceparent = headers['traceparent']; + expect(traceparent).toBeDefined(); + + // W3C traceparent format: {version}-{trace-id}-{parent-id}-{trace-flags} + const parts = traceparent.split('-'); + expect(parts).toHaveLength(4); + expect(parts[0]).toBe('00'); // version + expect(parts[1]).toBe(traceId); + expect(parts[2]).toBe(spanId); + expect(parts[3]).toBe(traceFlags.toString(16).padStart(2, '0')); + }); + + span.end(); + }); + + it('should be a no-op when no active span exists', () => { + const headers: Record = {}; + injectTraceContext(headers); + expect(headers['traceparent']).toBeUndefined(); + }); + }); + + describe('extractTraceContext', () => { + it('should extract valid traceparent into Context with correct traceId/spanId', () => { + const traceId = '0af7651916cd43dd8448eb211c80319c'; + const spanId = 'b7ad6b7169203331'; + + const extracted = extractTraceContext({ traceparent: `00-${traceId}-${spanId}-01` }); + const span = trace.getSpan(extracted); + + expect(span).toBeDefined(); + expect(span!.spanContext().traceId).toBe(traceId); + expect(span!.spanContext().spanId).toBe(spanId); + expect(span!.spanContext().traceFlags).toBe(TraceFlags.SAMPLED); + }); + + it('should return base context for missing or malformed headers', () => { + expect(trace.getSpan(extractTraceContext({}))).toBeUndefined(); + expect(trace.getSpan(extractTraceContext({ traceparent: 'invalid' }))).toBeUndefined(); + }); + }); + + describe('end-to-end inject → extract round-trip', () => { + it('should preserve trace and parent-child relationship across services', async () => { + const tracer = trace.getTracer('test'); + const senderSpan = tracer.startSpan('sender'); + const senderCtx = trace.setSpan(otelContext.active(), senderSpan); + + const headers: Record = {}; + otelContext.with(senderCtx, () => injectTraceContext(headers)); + + const result = runWithExtractedTraceContext(headers, () => { + const child = tracer.startSpan('receiver'); + expect(child.spanContext().traceId).toBe(senderSpan.spanContext().traceId); + child.end(); + return 'ok'; + }); + expect(result).toBe('ok'); + + senderSpan.end(); + await flushProvider.forceFlush(); + + const spans = exporter.getFinishedSpans(); + const receiver = spans.find(s => s.name === 'receiver'); + expect(receiver!.spanContext().traceId).toBe(senderSpan.spanContext().traceId); + expect(receiver!.parentSpanContext?.spanId).toBe(senderSpan.spanContext().spanId); + }); + }); + + describe('scope with extracted Context as ParentContext', () => { + it('should create scope as child of extracted trace context', async () => { + const traceId = '0af7651916cd43dd8448eb211c80319c'; + const spanId = 'b7ad6b7169203331'; + const extractedCtx = extractTraceContext({ traceparent: `00-${traceId}-${spanId}-01` }); + + const scope = InvokeAgentScope.start({ agentId: 'ctx-agent' }, testTenantDetails, undefined, undefined, extractedCtx); + expect(scope.getSpanContext().traceId).toBe(traceId); + scope.dispose(); + + await flushProvider.forceFlush(); + + const span = exporter.getFinishedSpans().find(s => s.name.toLowerCase().includes('invoke_agent')); + expect(span!.parentSpanContext?.spanId).toBe(spanId); + }); + }); + + describe('ParentSpanRef with isRemote', () => { + it('should propagate isRemote=true to child span parentSpanContext', async () => { + const parentRef: ParentSpanRef = { + traceId: '3af7651916cd43dd8448eb211c80319c', + spanId: 'e7ad6b7169203331', + traceFlags: TraceFlags.SAMPLED, + isRemote: true, + }; + + const scope = InvokeAgentScope.start({ agentId: 'remote-agent' }, testTenantDetails, undefined, undefined, parentRef); + scope.dispose(); + + await flushProvider.forceFlush(); + + const span = exporter.getFinishedSpans().find(s => s.spanContext().traceId === parentRef.traceId); + expect(span!.parentSpanContext?.spanId).toBe(parentRef.spanId); + expect(span!.parentSpanContext?.isRemote).toBe(true); + }); + }); +}); diff --git a/tests/observability/extension/hosting/scope-utils.test.ts b/tests/observability/extension/hosting/scope-utils.test.ts index 876d282f..a5ad82f7 100644 --- a/tests/observability/extension/hosting/scope-utils.test.ts +++ b/tests/observability/extension/hosting/scope-utils.test.ts @@ -37,6 +37,8 @@ function makeTurnContext( agenticAppId: 'agent-1', name: 'Agent One', aadObjectId: 'agent-oid', + agenticAppBlueprintId: 'agent-blueprint-1', + agenticUserId: 'agent-upn@contoso.com', role: 'assistant', tenantId: 'tenant-123' }; @@ -67,6 +69,8 @@ describe('ScopeUtils.populateFromTurnContext', () => { [OpenTelemetryConstants.GEN_AI_AGENT_NAME_KEY, 'Agent One'], [OpenTelemetryConstants.GEN_AI_AGENT_AUID_KEY, 'agent-oid'], [OpenTelemetryConstants.GEN_AI_AGENT_ID_KEY, 'agent-1'], + [OpenTelemetryConstants.GEN_AI_AGENT_BLUEPRINT_ID_KEY, 'agent-blueprint-1'], + [OpenTelemetryConstants.GEN_AI_AGENT_UPN_KEY, 'agent-upn@contoso.com'], [OpenTelemetryConstants.GEN_AI_AGENT_DESCRIPTION_KEY, 'assistant'], [OpenTelemetryConstants.TENANT_ID_KEY, 'tenant-123'], [OpenTelemetryConstants.GEN_AI_INPUT_MESSAGES_KEY, 'input text'] @@ -182,11 +186,13 @@ test('deriveTenantDetails returns undefined when only from.tenantId is present', }); test('deriveAgentDetails maps recipient fields to AgentDetails', () => { - const ctx = makeCtx({ activity: { recipient: { agenticAppId: 'aid', name: 'A', aadObjectId: 'auid', role: 'bot', tenantId: 't1' } } as any }); + const ctx = makeCtx({ activity: { recipient: { agenticAppId: 'aid', name: 'A', aadObjectId: 'auid', agenticAppBlueprintId: 'bp1', agenticUserId: 'upn1', role: 'bot', tenantId: 't1' } } as any }); expect(ScopeUtils.deriveAgentDetails(ctx)).toEqual({ agentId: 'aid', agentName: 'A', agentAUID: 'auid', + agentBlueprintId: 'bp1', + agentUPN: 'upn1', agentDescription: 'bot', tenantId: 't1', }); @@ -198,11 +204,12 @@ test('deriveAgentDetails returns undefined without recipient', () => { }); test('deriveCallerAgent maps from fields to caller AgentDetails', () => { - const ctx = makeCtx({ activity: { from: { agenticAppBlueprintId: 'bp', name: 'Caller', aadObjectId: 'uid', role: 'agent', tenantId: 't2', agenticAppId: 'agent-caller' } } as any }); + const ctx = makeCtx({ activity: { from: { agenticAppBlueprintId: 'bp', name: 'Caller', aadObjectId: 'uid', agenticUserId: 'caller-upn', role: 'agent', tenantId: 't2', agenticAppId: 'agent-caller' } } as any }); expect(ScopeUtils.deriveCallerAgent(ctx)).toEqual({ agentBlueprintId: 'bp', agentName: 'Caller', agentAUID: 'uid', + agentUPN: 'caller-upn', agentDescription: 'agent', tenantId: 't2', agentId: 'agent-caller', diff --git a/tests/observability/internal/PerRequestProcessorOverrides.test.ts b/tests/observability/internal/PerRequestProcessorOverrides.test.ts new file mode 100644 index 00000000..48e0080f --- /dev/null +++ b/tests/observability/internal/PerRequestProcessorOverrides.test.ts @@ -0,0 +1,123 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { PerRequestSpanProcessor } from '@microsoft/agents-a365-observability/src/tracing/PerRequestSpanProcessor'; +import { isPerRequestExportEnabled } from '@microsoft/agents-a365-observability/src/tracing/exporter/utils'; +import { ObservabilityBuilder } from '@microsoft/agents-a365-observability/src/ObservabilityBuilder'; +import { + setPerRequestProcessorInternalOverrides, + getPerRequestProcessorInternalOverrides, +} from '@microsoft/agents-a365-observability/src/internal/PerRequestProcessorInternalOverrides'; +import type { SpanExporter, ReadableSpan } from '@opentelemetry/sdk-trace-base'; +import { ExportResultCode } from '@opentelemetry/core'; + +// Mock Agent365Exporter to avoid network calls +jest.mock('@microsoft/agents-a365-observability/src/tracing/exporter/Agent365Exporter', () => ({ + Agent365Exporter: class { + export(_spans: unknown[], cb: (result: { code: number }) => void) { + cb({ code: 0 }); + } + shutdown() {/* no-op */} + forceFlush() {/* no-op */} + }, +})); + +const savedEnv: Record = {}; +const envKeysUsed = [ + 'ENABLE_A365_OBSERVABILITY_PER_REQUEST_EXPORT', + 'ENABLE_A365_OBSERVABILITY_EXPORTER', +]; + +beforeEach(() => { + // Save env vars that tests may mutate + for (const key of envKeysUsed) { + savedEnv[key] = process.env[key]; + } + // Suppress logger output + jest.spyOn(console, 'log').mockImplementation(() => {}); + jest.spyOn(console, 'warn').mockImplementation(() => {}); + jest.spyOn(console, 'error').mockImplementation(() => {}); +}); + +afterEach(() => { + setPerRequestProcessorInternalOverrides(undefined); + // Restore env vars + for (const key of envKeysUsed) { + if (savedEnv[key] === undefined) { + delete process.env[key]; + } else { + process.env[key] = savedEnv[key]; + } + } + jest.restoreAllMocks(); +}); + +const noopExporter: SpanExporter = { + export: (_spans: ReadableSpan[], cb: (result: { code: number }) => void) => + cb({ code: ExportResultCode.SUCCESS }), + shutdown: async () => {}, +}; + +describe('PerRequestProcessorOverrides', () => { + it('get/set round-trips correctly', () => { + expect(getPerRequestProcessorInternalOverrides()).toBeUndefined(); + + setPerRequestProcessorInternalOverrides({ isPerRequestExportEnabled: () => true }); + expect(getPerRequestProcessorInternalOverrides()?.isPerRequestExportEnabled?.()).toBe(true); + + setPerRequestProcessorInternalOverrides(undefined); + expect(getPerRequestProcessorInternalOverrides()).toBeUndefined(); + }); + + it('isPerRequestExportEnabled returns override value, ignoring env var', () => { + process.env.ENABLE_A365_OBSERVABILITY_PER_REQUEST_EXPORT = 'true'; + setPerRequestProcessorInternalOverrides({ isPerRequestExportEnabled: () => false }); + expect(isPerRequestExportEnabled()).toBe(false); + + setPerRequestProcessorInternalOverrides({ isPerRequestExportEnabled: () => true }); + expect(isPerRequestExportEnabled()).toBe(true); + }); + + it('PerRequestSpanProcessor uses overrides for guardrails', () => { + setPerRequestProcessorInternalOverrides({ + perRequestMaxTraces: () => 7, + perRequestMaxConcurrentExports: () => 3, + }); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const processor = new PerRequestSpanProcessor(noopExporter) as any; + expect(processor.maxBufferedTraces).toBe(7); + expect(processor.maxConcurrentExports).toBe(3); + // unset field falls back to config default + expect(processor.maxSpansPerTrace).toBe(5000); + }); + + it('PerRequestSpanProcessor ignores non-positive override values', () => { + setPerRequestProcessorInternalOverrides({ + perRequestMaxTraces: () => -1, + perRequestMaxSpansPerTrace: () => 0, + perRequestMaxConcurrentExports: () => 5, + }); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const processor = new PerRequestSpanProcessor(noopExporter) as any; + expect(processor.maxBufferedTraces).toBe(1000); // -1 ignored, falls back + expect(processor.maxSpansPerTrace).toBe(5000); // 0 ignored, falls back + expect(processor.maxConcurrentExports).toBe(5); // positive, kept + }); + + it('PerRequestSpanProcessor uses config defaults when no overrides set', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const processor = new PerRequestSpanProcessor(noopExporter) as any; + expect(processor.maxBufferedTraces).toBe(1000); + expect(processor.maxSpansPerTrace).toBe(5000); + expect(processor.maxConcurrentExports).toBe(20); + }); + + it('ObservabilityBuilder uses PerRequestSpanProcessor when override enables it', () => { + setPerRequestProcessorInternalOverrides({ isPerRequestExportEnabled: () => true }); + + const builder = new ObservabilityBuilder().withService('test-agent'); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const processor = (builder as any).createExportProcessor(); + expect(processor).toBeInstanceOf(PerRequestSpanProcessor); + }); +}); diff --git a/tests/observability/tracing/exporter-utils.test.ts b/tests/observability/tracing/exporter-utils.test.ts index a8618649..6dd9b365 100644 --- a/tests/observability/tracing/exporter-utils.test.ts +++ b/tests/observability/tracing/exporter-utils.test.ts @@ -4,7 +4,8 @@ import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals'; import { SpanKind, SpanStatusCode } from '@opentelemetry/api'; import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; -import { ClusterCategory } from '@microsoft/agents-a365-runtime'; +import { ClusterCategory, DefaultConfigurationProvider } from '@microsoft/agents-a365-runtime'; +import { ObservabilityConfiguration } from '@microsoft/agents-a365-observability/src/configuration/ObservabilityConfiguration'; describe('exporter/utils', () => { const originalEnv = process.env; @@ -209,6 +210,31 @@ describe('exporter/utils', () => { const { isAgent365ExporterEnabled } = await import('@microsoft/agents-a365-observability/src/tracing/exporter/utils'); expect(isAgent365ExporterEnabled()).toBe(false); }); + + it('should use configProvider override when provided (enabled)', async () => { + delete process.env.ENABLE_A365_OBSERVABILITY_EXPORTER; + const provider = new DefaultConfigurationProvider(() => new ObservabilityConfiguration({ + isObservabilityExporterEnabled: () => true, + })); + const { isAgent365ExporterEnabled } = await import('@microsoft/agents-a365-observability/src/tracing/exporter/utils'); + expect(isAgent365ExporterEnabled(provider)).toBe(true); + }); + + it('should use configProvider override when provided (disabled)', async () => { + process.env.ENABLE_A365_OBSERVABILITY_EXPORTER = 'true'; + const provider = new DefaultConfigurationProvider(() => new ObservabilityConfiguration({ + isObservabilityExporterEnabled: () => false, + })); + const { isAgent365ExporterEnabled } = await import('@microsoft/agents-a365-observability/src/tracing/exporter/utils'); + expect(isAgent365ExporterEnabled(provider)).toBe(false); + }); + + it('should return false when no env var is set and configProvider has no override', async () => { + delete process.env.ENABLE_A365_OBSERVABILITY_EXPORTER; + const provider = new DefaultConfigurationProvider(() => new ObservabilityConfiguration()); + const { isAgent365ExporterEnabled } = await import('@microsoft/agents-a365-observability/src/tracing/exporter/utils'); + expect(isAgent365ExporterEnabled(provider)).toBe(false); + }); }); describe('isPerRequestExportEnabled', () => { @@ -269,6 +295,31 @@ describe('exporter/utils', () => { const { useCustomDomainForObservability } = await import('@microsoft/agents-a365-observability/src/tracing/exporter/utils'); expect(useCustomDomainForObservability()).toBe(false); }); + + it('should use configProvider override when provided (enabled)', async () => { + delete process.env.A365_OBSERVABILITY_USE_CUSTOM_DOMAIN; + const provider = new DefaultConfigurationProvider(() => new ObservabilityConfiguration({ + useCustomDomainForObservability: () => true, + })); + const { useCustomDomainForObservability } = await import('@microsoft/agents-a365-observability/src/tracing/exporter/utils'); + expect(useCustomDomainForObservability(provider)).toBe(true); + }); + + it('should use configProvider override when provided (disabled)', async () => { + process.env.A365_OBSERVABILITY_USE_CUSTOM_DOMAIN = 'true'; + const provider = new DefaultConfigurationProvider(() => new ObservabilityConfiguration({ + useCustomDomainForObservability: () => false, + })); + const { useCustomDomainForObservability } = await import('@microsoft/agents-a365-observability/src/tracing/exporter/utils'); + expect(useCustomDomainForObservability(provider)).toBe(false); + }); + + it('should return false when no env var is set and configProvider has no override', async () => { + delete process.env.A365_OBSERVABILITY_USE_CUSTOM_DOMAIN; + const provider = new DefaultConfigurationProvider(() => new ObservabilityConfiguration()); + const { useCustomDomainForObservability } = await import('@microsoft/agents-a365-observability/src/tracing/exporter/utils'); + expect(useCustomDomainForObservability(provider)).toBe(false); + }); }); describe('resolveAgent365Endpoint', () => { @@ -339,6 +390,31 @@ describe('exporter/utils', () => { const { getAgent365ObservabilityDomainOverride } = await import('@microsoft/agents-a365-observability/src/tracing/exporter/utils'); expect(getAgent365ObservabilityDomainOverride()).toBeNull(); }); + + it('should use configProvider override when provided', async () => { + delete process.env.A365_OBSERVABILITY_DOMAIN_OVERRIDE; + const provider = new DefaultConfigurationProvider(() => new ObservabilityConfiguration({ + observabilityDomainOverride: () => 'https://override.example.com', + })); + const { getAgent365ObservabilityDomainOverride } = await import('@microsoft/agents-a365-observability/src/tracing/exporter/utils'); + expect(getAgent365ObservabilityDomainOverride(provider)).toBe('https://override.example.com'); + }); + + it('should use configProvider override even when env var is set', async () => { + process.env.A365_OBSERVABILITY_DOMAIN_OVERRIDE = 'https://env.example.com'; + const provider = new DefaultConfigurationProvider(() => new ObservabilityConfiguration({ + observabilityDomainOverride: () => 'https://provider-wins.example.com', + })); + const { getAgent365ObservabilityDomainOverride } = await import('@microsoft/agents-a365-observability/src/tracing/exporter/utils'); + expect(getAgent365ObservabilityDomainOverride(provider)).toBe('https://provider-wins.example.com'); + }); + + it('should return null when no env var is set and configProvider has no override', async () => { + delete process.env.A365_OBSERVABILITY_DOMAIN_OVERRIDE; + const provider = new DefaultConfigurationProvider(() => new ObservabilityConfiguration()); + const { getAgent365ObservabilityDomainOverride } = await import('@microsoft/agents-a365-observability/src/tracing/exporter/utils'); + expect(getAgent365ObservabilityDomainOverride(provider)).toBeNull(); + }); }); describe('parseIdentityKey', () => { diff --git a/tests/observability/utils/logging.test.ts b/tests/observability/utils/logging.test.ts index 25fdd3ce..166382d2 100644 --- a/tests/observability/utils/logging.test.ts +++ b/tests/observability/utils/logging.test.ts @@ -2,6 +2,7 @@ // Licensed under the MIT License. import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals'; +import { ExporterEventNames } from '@microsoft/agents-a365-observability'; describe('logging', () => { const originalEnv = process.env; @@ -584,4 +585,109 @@ describe('logging', () => { expect(consoleErrorSpy).toHaveBeenCalledWith('[ERROR]', 'Error message'); }); }); + + describe('event method', () => { + it('should log successful events with info level when enabled', async () => { + process.env.A365_OBSERVABILITY_LOG_LEVEL = 'info'; + const { logger } = await import('@microsoft/agents-a365-observability/src/utils/logging'); + + logger.event(ExporterEventNames.EXPORT, true, 100, 'Operation completed'); + + expect(consoleLogSpy).toHaveBeenCalledWith('[EVENT]: agent365-export succeeded in 100ms - Operation completed'); + }); + + it('should log failed events with error level when enabled', async () => { + process.env.A365_OBSERVABILITY_LOG_LEVEL = 'error'; + const { logger } = await import('@microsoft/agents-a365-observability/src/utils/logging'); + + logger.event(ExporterEventNames.EXPORT, false, 200, 'Operation failed'); + + expect(consoleErrorSpy).toHaveBeenCalledWith('[EVENT]: agent365-export failed in 200ms - Operation failed'); + }); + + it('should include details in event logs', async () => { + process.env.A365_OBSERVABILITY_LOG_LEVEL = 'info'; + const { logger } = await import('@microsoft/agents-a365-observability/src/utils/logging'); + + logger.event(ExporterEventNames.EXPORT_GROUP, true, 150, 'Success', { correlationId: 'abc123', tenantId: 'tenant1' }); + + expect(consoleLogSpy).toHaveBeenCalledWith( + '[EVENT]: export-group succeeded in 150ms - Success {"correlationId":"abc123","tenantId":"tenant1"}' + ); + }); + + it('should log event without message', async () => { + process.env.A365_OBSERVABILITY_LOG_LEVEL = 'info'; + const { logger } = await import('@microsoft/agents-a365-observability/src/utils/logging'); + + logger.event(ExporterEventNames.EXPORT, true, 100); + + expect(consoleLogSpy).toHaveBeenCalledWith('[EVENT]: agent365-export succeeded in 100ms'); + }); + + it('should log event without details', async () => { + process.env.A365_OBSERVABILITY_LOG_LEVEL = 'info'; + const { logger } = await import('@microsoft/agents-a365-observability/src/utils/logging'); + + logger.event(ExporterEventNames.EXPORT, true, 100, 'Completed'); + + expect(consoleLogSpy).toHaveBeenCalledWith('[EVENT]: agent365-export succeeded in 100ms - Completed'); + }); + + it('should log event with empty details object', async () => { + process.env.A365_OBSERVABILITY_LOG_LEVEL = 'info'; + const { logger } = await import('@microsoft/agents-a365-observability/src/utils/logging'); + + logger.event(ExporterEventNames.EXPORT, true, 100, 'Completed', {}); + + expect(consoleLogSpy).toHaveBeenCalledWith('[EVENT]: agent365-export succeeded in 100ms - Completed'); + }); + + it('should not log successful events when log level is none', async () => { + process.env.A365_OBSERVABILITY_LOG_LEVEL = 'none'; + const { logger } = await import('@microsoft/agents-a365-observability/src/utils/logging'); + + logger.event(ExporterEventNames.EXPORT, true, 100, 'Success'); + + expect(consoleLogSpy).not.toHaveBeenCalled(); + }); + + it('should not log failed events when log level is none', async () => { + process.env.A365_OBSERVABILITY_LOG_LEVEL = 'none'; + const { logger } = await import('@microsoft/agents-a365-observability/src/utils/logging'); + + logger.event(ExporterEventNames.EXPORT, false, 100, 'Failure'); + + expect(consoleErrorSpy).not.toHaveBeenCalled(); + }); + + it('should log failed events with error level and details', async () => { + process.env.A365_OBSERVABILITY_LOG_LEVEL = 'error'; + const { logger } = await import('@microsoft/agents-a365-observability/src/utils/logging'); + + logger.event(ExporterEventNames.EXPORT_GROUP, false, 250, 'Failed', { + correlationId: 'xyz789', + tenantId: 'tenant2', + agentId: 'agent1' + }); + + expect(consoleErrorSpy).toHaveBeenCalledWith( + '[EVENT]: export-group failed in 250ms - Failed {"correlationId":"xyz789","tenantId":"tenant2","agentId":"agent1"}' + ); + }); + + it('should use ExporterEventNames enum values', async () => { + process.env.A365_OBSERVABILITY_LOG_LEVEL = 'info'; + const { logger } = await import('@microsoft/agents-a365-observability/src/utils/logging'); + + // Test all enum values + logger.event(ExporterEventNames.EXPORT, true, 100); + logger.event(ExporterEventNames.EXPORT_GROUP, true, 100); + logger.event(ExporterEventNames.EXPORT_PARTITION_SPAN_MISSING_IDENTITY, true, 100); + + expect(consoleLogSpy).toHaveBeenCalledWith('[EVENT]: agent365-export succeeded in 100ms'); + expect(consoleLogSpy).toHaveBeenCalledWith('[EVENT]: export-group succeeded in 100ms'); + expect(consoleLogSpy).toHaveBeenCalledWith('[EVENT]: export-partition-span-missing-identity succeeded in 100ms'); + }); + }); }); diff --git a/tests/package.json b/tests/package.json index 94a265e3..48568a51 100644 --- a/tests/package.json +++ b/tests/package.json @@ -36,6 +36,7 @@ "@openai/agents": "catalog:", "@openai/agents-openai": "catalog:", "@opentelemetry/api": "catalog:", + "@opentelemetry/core": "catalog:", "@opentelemetry/exporter-trace-otlp-http": "catalog:", "@opentelemetry/resources": "catalog:", "@opentelemetry/sdk-node": "catalog:",