From cae9b7f68c3f32ae9da0d5c56febe8b74fd12b3d Mon Sep 17 00:00:00 2001 From: jsl517 Date: Fri, 14 Nov 2025 11:38:50 -0800 Subject: [PATCH 1/3] remove non prod category --- .../src/ObservabilityBuilder.ts | 3 +- .../src/tracing/exporter/Agent365Exporter.ts | 9 +- .../src/environment-utils.ts | 131 ++++++++---------- 3 files changed, 63 insertions(+), 80 deletions(-) diff --git a/packages/agents-a365-observability/src/ObservabilityBuilder.ts b/packages/agents-a365-observability/src/ObservabilityBuilder.ts index cd26049c..5f9ba1b0 100644 --- a/packages/agents-a365-observability/src/ObservabilityBuilder.ts +++ b/packages/agents-a365-observability/src/ObservabilityBuilder.ts @@ -73,8 +73,7 @@ export class ObservabilityBuilder { throw new Error('tokenResolver must be provided when Agent 365 exporter is enabled'); } return new Agent365Exporter( - this.options.tokenResolver, - this.options.clusterCategory || 'prod' + this.options.tokenResolver ); } else { return new ConsoleSpanExporter(); diff --git a/packages/agents-a365-observability/src/tracing/exporter/Agent365Exporter.ts b/packages/agents-a365-observability/src/tracing/exporter/Agent365Exporter.ts index 7f06d048..44d84201 100644 --- a/packages/agents-a365-observability/src/tracing/exporter/Agent365Exporter.ts +++ b/packages/agents-a365-observability/src/tracing/exporter/Agent365Exporter.ts @@ -1,7 +1,7 @@ import { ExportResult,ExportResultCode } from '@opentelemetry/core'; import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'; -import { PowerPlatformApiDiscovery, ClusterCategory } from '@microsoft/agents-a365-runtime'; +import { PowerPlatformApiDiscovery } from '@microsoft/agents-a365-runtime'; import { partitionByIdentity, parseIdentityKey, hexTraceId, hexSpanId, kindName, statusName } from './utils'; import logger, {formatError} from '../../utils/logging'; @@ -72,19 +72,16 @@ export type TokenResolver = (agentId: string, tenantId: string) => string | null */ export class Agent365Exporter implements SpanExporter { private readonly tokenResolver: TokenResolver; - private readonly clusterCategory: ClusterCategory; private closed = false; constructor( tokenResolver: TokenResolver, - clusterCategory: ClusterCategory = 'prod' ) { if (!tokenResolver) { logger.error('[Agent365Exporter] token_resolver is not provided'); throw new Error('token_resolver must be provided.'); } this.tokenResolver = tokenResolver; - this.clusterCategory = clusterCategory; } /** @@ -139,8 +136,8 @@ export class Agent365Exporter implements SpanExporter { const payload = this.buildExportRequest(spans); const body = JSON.stringify(payload); - // Resolve endpoint + token - const discovery = new PowerPlatformApiDiscovery(this.clusterCategory); + // Resolve endpoint + token (hardcoded to production cluster 'prod') + const discovery = new PowerPlatformApiDiscovery('prod'); const endpoint = discovery.getTenantIslandClusterEndpoint(tenantId); const url = `https://${endpoint}/maven/agent365/agents/${agentId}/traces?api-version=1`; logger.info(`[Agent365Exporter] Resolved endpoint: ${endpoint}`); diff --git a/packages/agents-a365-runtime/src/environment-utils.ts b/packages/agents-a365-runtime/src/environment-utils.ts index cb189515..841f60e7 100644 --- a/packages/agents-a365-runtime/src/environment-utils.ts +++ b/packages/agents-a365-runtime/src/environment-utils.ts @@ -1,72 +1,59 @@ -// ------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// ------------------------------------------------------------------------------ - -/** - * Utility logic for environment-related operations. - */ - -// Authentication scopes for different environments -export const TEST_OBSERVABILITY_SCOPE = 'https://api.test.powerplatform.com/.default'; -export const PREPROD_OBSERVABILITY_SCOPE = 'https://api.preprod.powerplatform.com/.default'; -export const PROD_OBSERVABILITY_SCOPE = 'https://api.powerplatform.com/.default'; -export const PROD_MCP_PLATFORM_AUTHENTICATION_SCOPE = 'ea9ffc3e-8a23-4a7d-836d-234d7c7565c1/.default'; - -// Cluster categories for different environments -export const TEST_OBSERVABILITY_CLUSTER_CATEGORY = 'test'; -export const PREPROD_OBSERVABILITY_CLUSTER_CATEGORY = 'preprod'; -export const PROD_OBSERVABILITY_CLUSTER_CATEGORY = 'prod'; - -// Default environment names -export const PRODUCTION_ENVIRONMENT_NAME = 'production'; -export const DEVELOPMENT_ENVIRONMENT_NAME = 'Development'; - -/** - * Returns the scope for authenticating to the observability service based on the current environment. - * - * @returns The authentication scope for the current environment. - */ -export function getObservabilityAuthenticationScope(): string[] { - const clusterCategory = getClusterCategory(); - - if (['local', 'dev', 'test', 'preprod'].includes(clusterCategory)) { - return [PREPROD_OBSERVABILITY_SCOPE]; - } else { - // Default to production scope for 'prod' and any other values - return [PROD_OBSERVABILITY_SCOPE]; - } -} - -/** - * Gets the cluster category from environment variables. - * - * @returns The cluster category from CLUSTER_CATEGORY env var, defaults to 'prod'. - */ -export function getClusterCategory(): string { - const clusterCategory = process.env.CLUSTER_CATEGORY; - - if (!clusterCategory) { - return 'prod'; - } - - return clusterCategory.toLowerCase(); -} - -/** - * Returns true if the current environment is a development environment. - * - * @returns True if the current environment is development, false otherwise. - */ -export function isDevelopmentEnvironment(): boolean { - const clusterCategory = getClusterCategory(); - return ['local', 'dev'].includes(clusterCategory); -} - -/** - * Gets the MCP platform authentication scope from environment variables. - * - * @returns The MCP platform authentication scope from MCP_PLATFORM_AUTHENTICATION_SCOPE env var, defaults to production scope. - */ -export function getMcpPlatformAuthenticationScope(): string { - return process.env.MCP_PLATFORM_AUTHENTICATION_SCOPE || PROD_MCP_PLATFORM_AUTHENTICATION_SCOPE; -} \ No newline at end of file +// ------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------------------------ + +/** + * Utility logic for environment-related operations. + */ + +export const PROD_OBSERVABILITY_SCOPE = 'https://api.powerplatform.com/.default'; +export const PROD_MCP_PLATFORM_AUTHENTICATION_SCOPE = 'ea9ffc3e-8a23-4a7d-836d-234d7c7565c1/.default'; +export const PROD_OBSERVABILITY_CLUSTER_CATEGORY = 'prod'; + +// Default environment names +export const PRODUCTION_ENVIRONMENT_NAME = 'production'; +export const DEVELOPMENT_ENVIRONMENT_NAME = 'Development'; + +/** + * Returns the scope for authenticating to the observability service + * + * @returns The authentication scope for the current environment. + */ +export function getObservabilityAuthenticationScope(): string[] { + // Always return production scope + return [PROD_OBSERVABILITY_SCOPE]; +} + +/** + * Gets the cluster category from environment variables. + * + * @returns The cluster category from CLUSTER_CATEGORY env var, defaults to 'prod'. + */ +export function getClusterCategory(): string { + const clusterCategory = process.env.CLUSTER_CATEGORY; + + if (!clusterCategory) { + return 'prod'; + } + + return clusterCategory.toLowerCase(); +} + +/** + * Returns true if the current environment is a development environment. + * + * @returns True if the current environment is development, false otherwise. + */ +export function isDevelopmentEnvironment(): boolean { + const clusterCategory = getClusterCategory(); + return ['local', 'dev'].includes(clusterCategory); +} + +/** + * Gets the MCP platform authentication scope from environment variables. + * + * @returns The MCP platform authentication scope from MCP_PLATFORM_AUTHENTICATION_SCOPE env var, defaults to production scope. + */ +export function getMcpPlatformAuthenticationScope(): string { + return process.env.MCP_PLATFORM_AUTHENTICATION_SCOPE || PROD_MCP_PLATFORM_AUTHENTICATION_SCOPE; +} From b9630c7f68995cf4a41f09e64b58c6c4bae990f5 Mon Sep 17 00:00:00 2001 From: jsl517 Date: Fri, 14 Nov 2025 15:53:10 -0800 Subject: [PATCH 2/3] comments --- .../src/ObservabilityBuilder.ts | 3 ++- .../src/tracing/exporter/Agent365Exporter.ts | 9 ++++++--- .../src/power-platform-api-discovery.ts | 6 +++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/agents-a365-observability/src/ObservabilityBuilder.ts b/packages/agents-a365-observability/src/ObservabilityBuilder.ts index 5f9ba1b0..cd26049c 100644 --- a/packages/agents-a365-observability/src/ObservabilityBuilder.ts +++ b/packages/agents-a365-observability/src/ObservabilityBuilder.ts @@ -73,7 +73,8 @@ export class ObservabilityBuilder { throw new Error('tokenResolver must be provided when Agent 365 exporter is enabled'); } return new Agent365Exporter( - this.options.tokenResolver + this.options.tokenResolver, + this.options.clusterCategory || 'prod' ); } else { return new ConsoleSpanExporter(); diff --git a/packages/agents-a365-observability/src/tracing/exporter/Agent365Exporter.ts b/packages/agents-a365-observability/src/tracing/exporter/Agent365Exporter.ts index 44d84201..96395d4e 100644 --- a/packages/agents-a365-observability/src/tracing/exporter/Agent365Exporter.ts +++ b/packages/agents-a365-observability/src/tracing/exporter/Agent365Exporter.ts @@ -1,7 +1,7 @@ import { ExportResult,ExportResultCode } from '@opentelemetry/core'; import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'; -import { PowerPlatformApiDiscovery } from '@microsoft/agents-a365-runtime'; +import { PowerPlatformApiDiscovery, ClusterCategory } from '@microsoft/agents-a365-runtime'; import { partitionByIdentity, parseIdentityKey, hexTraceId, hexSpanId, kindName, statusName } from './utils'; import logger, {formatError} from '../../utils/logging'; @@ -72,16 +72,19 @@ export type TokenResolver = (agentId: string, tenantId: string) => string | null */ export class Agent365Exporter implements SpanExporter { private readonly tokenResolver: TokenResolver; + private readonly clusterCategory: ClusterCategory; private closed = false; constructor( tokenResolver: TokenResolver, + clusterCategory: ClusterCategory = 'prod' ) { if (!tokenResolver) { logger.error('[Agent365Exporter] token_resolver is not provided'); throw new Error('token_resolver must be provided.'); } this.tokenResolver = tokenResolver; + this.clusterCategory = clusterCategory; } /** @@ -136,8 +139,8 @@ export class Agent365Exporter implements SpanExporter { const payload = this.buildExportRequest(spans); const body = JSON.stringify(payload); - // Resolve endpoint + token (hardcoded to production cluster 'prod') - const discovery = new PowerPlatformApiDiscovery('prod'); + // Resolve endpoint + token based on cluster category (defaults to 'prod') + const discovery = new PowerPlatformApiDiscovery(this.clusterCategory); const endpoint = discovery.getTenantIslandClusterEndpoint(tenantId); const url = `https://${endpoint}/maven/agent365/agents/${agentId}/traces?api-version=1`; logger.info(`[Agent365Exporter] Resolved endpoint: ${endpoint}`); diff --git a/packages/agents-a365-runtime/src/power-platform-api-discovery.ts b/packages/agents-a365-runtime/src/power-platform-api-discovery.ts index dcb1a595..930e3864 100644 --- a/packages/agents-a365-runtime/src/power-platform-api-discovery.ts +++ b/packages/agents-a365-runtime/src/power-platform-api-discovery.ts @@ -82,9 +82,9 @@ export class PowerPlatformApiDiscovery { private _getEnvironmentApiHostNameSuffix(): string { const apiHostNameSuffixMap: Readonly> = { local: 'api.powerplatform.localhost', - dev: 'api.dev.powerplatform.com', - test: 'api.test.powerplatform.com', - preprod: 'api.preprod.powerplatform.com', + dev: 'api.powerplatform.com', //default to prod + test: 'api.powerplatform.com', //default to prod + preprod: 'api.powerplatform.com', //default to prod firstrelease: 'api.powerplatform.com', prod: 'api.powerplatform.com', gov: 'api.gov.powerplatform.microsoft.us', From 0b1ca2d730f7d7a0f3d4c7daba7751f922b20513 Mon Sep 17 00:00:00 2001 From: jsl517 Date: Fri, 14 Nov 2025 16:05:30 -0800 Subject: [PATCH 3/3] fix tests --- .../power-platform-api-discovery.test.ts | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/tests/common/power-platform-api-discovery.test.ts b/tests/common/power-platform-api-discovery.test.ts index a611f02c..d7c5d52f 100644 --- a/tests/common/power-platform-api-discovery.test.ts +++ b/tests/common/power-platform-api-discovery.test.ts @@ -8,14 +8,15 @@ describe('getTokenAudience gets the correct token audiences for the environment' expect(new PowerPlatformApiDiscovery('local').getTokenAudience()).toEqual( 'https://api.powerplatform.localhost' ); + // Non-production categories now default to production domain expect(new PowerPlatformApiDiscovery('dev').getTokenAudience()).toEqual( - 'https://api.dev.powerplatform.com' + 'https://api.powerplatform.com' ); expect(new PowerPlatformApiDiscovery('test').getTokenAudience()).toEqual( - 'https://api.test.powerplatform.com' + 'https://api.powerplatform.com' ); expect(new PowerPlatformApiDiscovery('preprod').getTokenAudience()).toEqual( - 'https://api.preprod.powerplatform.com' + 'https://api.powerplatform.com' ); expect(new PowerPlatformApiDiscovery('firstrelease').getTokenAudience()).toEqual( 'https://api.powerplatform.com' @@ -45,12 +46,13 @@ describe('getTokenEndpointHost gets the correct host for the environment', () => expect(new PowerPlatformApiDiscovery('local').getTokenEndpointHost()).toEqual( 'api.powerplatform.localhost' ); - expect(new PowerPlatformApiDiscovery('dev').getTokenEndpointHost()).toEqual('api.dev.powerplatform.com'); + // Non-production categories now default to production domain + expect(new PowerPlatformApiDiscovery('dev').getTokenEndpointHost()).toEqual('api.powerplatform.com'); expect(new PowerPlatformApiDiscovery('test').getTokenEndpointHost()).toEqual( - 'api.test.powerplatform.com' + 'api.powerplatform.com' ); expect(new PowerPlatformApiDiscovery('preprod').getTokenEndpointHost()).toEqual( - 'api.preprod.powerplatform.com' + 'api.powerplatform.com' ); expect(new PowerPlatformApiDiscovery('firstrelease').getTokenEndpointHost()).toEqual( 'api.powerplatform.com' @@ -80,14 +82,15 @@ describe('getTenantEndpoint generates the expected tenant endpoint', () => { expect(new PowerPlatformApiDiscovery('local').getTenantEndpoint(tenantId)).toEqual( 'e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.localhost' ); + // Non-production categories now default to production domain expect(new PowerPlatformApiDiscovery('dev').getTenantEndpoint(tenantId)).toEqual( - 'e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.dev.powerplatform.com' + 'e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.com' ); expect(new PowerPlatformApiDiscovery('test').getTenantEndpoint(tenantId)).toEqual( - 'e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.test.powerplatform.com' + 'e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.com' ); expect(new PowerPlatformApiDiscovery('preprod').getTenantEndpoint(tenantId)).toEqual( - 'e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.preprod.powerplatform.com' + 'e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.com' ); expect(new PowerPlatformApiDiscovery('firstrelease').getTenantEndpoint(tenantId)).toEqual( 'e3064512cc6d4703be71a2ecaecaa9.8a.tenant.api.powerplatform.com' @@ -142,14 +145,15 @@ describe('getTenantIslandClusterEndpoint generates the expected tenant island cl expect(new PowerPlatformApiDiscovery('local').getTenantIslandClusterEndpoint(tenantId)).toEqual( 'il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.localhost' ); + // Non-production categories now default to production domain expect(new PowerPlatformApiDiscovery('dev').getTenantIslandClusterEndpoint(tenantId)).toEqual( - 'il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.dev.powerplatform.com' + 'il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.com' ); expect(new PowerPlatformApiDiscovery('test').getTenantIslandClusterEndpoint(tenantId)).toEqual( - 'il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.test.powerplatform.com' + 'il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.com' ); expect(new PowerPlatformApiDiscovery('preprod').getTenantIslandClusterEndpoint(tenantId)).toEqual( - 'il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.preprod.powerplatform.com' + 'il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.com' ); expect(new PowerPlatformApiDiscovery('firstrelease').getTenantIslandClusterEndpoint(tenantId)).toEqual( 'il-e3064512cc6d4703be71a2ecaecaa9.8a.tenant.api.powerplatform.com'