Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ All notable changes to the Agent365 TypeScript SDK will be documented in this fi
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- Support for `A365_OBSERVABILITY_SCOPE_OVERRIDE` (singular) environment variable for cross-SDK compatibility with .NET SDK (PR #133 parity).
- The singular form takes precedence when both singular and plural forms are set
- Maintains backward compatibility with existing `A365_OBSERVABILITY_SCOPES_OVERRIDE` (plural) that supports multiple whitespace-separated scopes
- Updated documentation in `.env.example` files

### Notes
- This change maintains full backward compatibility. Existing code using `A365_OBSERVABILITY_SCOPES_OVERRIDE` continues to work without any changes.

## [1.1.0] - 2025-12-09

### Changed
Expand Down
26 changes: 19 additions & 7 deletions packages/agents-a365-runtime/src/environment-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,31 @@ export const DEVELOPMENT_ENVIRONMENT_NAME = 'Development';
* Returns the scope for authenticating to the observability service
*
* The default is the production observability scope, but this can be overridden
* for internal development and testing scenarios using the
* `A365_OBSERVABILITY_SCOPES_OVERRIDE` environment variable.
* for internal development and testing scenarios using either the
* `A365_OBSERVABILITY_SCOPE_OVERRIDE` (singular, for a single scope) or
* `A365_OBSERVABILITY_SCOPES_OVERRIDE` (plural, supports multiple whitespace-separated scopes)
* environment variable.
*
* When the override is set to a non-empty string, it is split on whitespace
* into individual scopes.
* The singular form takes precedence and returns a single scope without splitting.
* The plural form splits on whitespace to support multiple scopes.
* This dual support provides cross-SDK compatibility with the .NET SDK while maintaining
* enhanced functionality.
*
* @returns The authentication scopes for the current environment.
*/
export function getObservabilityAuthenticationScope(): string[] {
const override = process.env.A365_OBSERVABILITY_SCOPES_OVERRIDE;
// Check singular form first for .NET SDK compatibility (PR #133)
// Returns a single scope without splitting on whitespace
const singularOverride = process.env.A365_OBSERVABILITY_SCOPE_OVERRIDE;
if (singularOverride && singularOverride.trim().length > 0) {
return [singularOverride.trim()];
}

if (override && override.trim().length > 0) {
return override.trim().split(/\s+/);
// Check plural form (original TypeScript implementation)
// Supports multiple whitespace-separated scopes
const pluralOverride = process.env.A365_OBSERVABILITY_SCOPES_OVERRIDE;
if (pluralOverride && pluralOverride.trim().length > 0) {
return pluralOverride.trim().split(/\s+/);
}

return [PROD_OBSERVABILITY_SCOPE];
Expand Down
3 changes: 2 additions & 1 deletion tests-agent/basic-agent-sdk-sample/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ CLUSTER_CATEGORY=prod # optional - defaults to 'prod' if not set
A365_OBSERVABILITY_LOG_LEVEL= # optional - set to enable observability logs, value can be 'info', 'warn', or 'error', default to 'none' if not set
Use_Custom_Resolver= # optional - set to 'true' to use custom token resolver, defaults to 'false' if not set
A365_OBSERVABILITY_DOMAIN_OVERRIDE= # optional - set to override the default observability domain
A365_OBSERVABILITY_SCOPES_OVERRIDE= # optional - set to override the default observability scopes
A365_OBSERVABILITY_SCOPE_OVERRIDE= # optional - set to override the default observability scope (singular, for single scope, cross-SDK compatible)
A365_OBSERVABILITY_SCOPES_OVERRIDE= # optional - set to override the default observability scopes (plural, supports multiple whitespace-separated scopes)
31 changes: 31 additions & 0 deletions tests/runtime/environment-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ describe('environment-utils', () => {
describe('getObservabilityAuthenticationScope', () => {
it('should return production observability scope when override is not set', () => {
delete process.env.A365_OBSERVABILITY_SCOPES_OVERRIDE;
delete process.env.A365_OBSERVABILITY_SCOPE_OVERRIDE;

const scopes = getObservabilityAuthenticationScope();

Expand All @@ -33,6 +34,7 @@ describe('environment-utils', () => {
});

it('should return overridden observability scope when A365_OBSERVABILITY_SCOPES_OVERRIDE is set', () => {
delete process.env.A365_OBSERVABILITY_SCOPE_OVERRIDE;
process.env.A365_OBSERVABILITY_SCOPES_OVERRIDE = 'https://override.example.com/.default';

const scopes = getObservabilityAuthenticationScope();
Expand All @@ -41,6 +43,7 @@ describe('environment-utils', () => {
});

it('should support multiple scopes separated by whitespace', () => {
delete process.env.A365_OBSERVABILITY_SCOPE_OVERRIDE;
process.env.A365_OBSERVABILITY_SCOPES_OVERRIDE = 'scope-one/.default scope-two/.default';

const scopes = getObservabilityAuthenticationScope();
Expand All @@ -49,12 +52,40 @@ describe('environment-utils', () => {
});

it('should fall back to production scope when override is empty or whitespace', () => {
delete process.env.A365_OBSERVABILITY_SCOPE_OVERRIDE;
process.env.A365_OBSERVABILITY_SCOPES_OVERRIDE = ' ';

const scopes = getObservabilityAuthenticationScope();

expect(scopes).toEqual([PROD_OBSERVABILITY_SCOPE]);
});

it('should return overridden observability scope when A365_OBSERVABILITY_SCOPE_OVERRIDE is set (singular form for .NET SDK compatibility)', () => {
delete process.env.A365_OBSERVABILITY_SCOPES_OVERRIDE;
process.env.A365_OBSERVABILITY_SCOPE_OVERRIDE = 'https://override.singular.com/.default';

const scopes = getObservabilityAuthenticationScope();

expect(scopes).toEqual(['https://override.singular.com/.default']);
});

it('should prioritize A365_OBSERVABILITY_SCOPE_OVERRIDE (singular) over A365_OBSERVABILITY_SCOPES_OVERRIDE (plural) when both are set', () => {
process.env.A365_OBSERVABILITY_SCOPE_OVERRIDE = 'https://singular.example.com/.default';
process.env.A365_OBSERVABILITY_SCOPES_OVERRIDE = 'https://plural.example.com/.default';

const scopes = getObservabilityAuthenticationScope();

expect(scopes).toEqual(['https://singular.example.com/.default']);
});

it('should fall back to production scope when singular override is empty or whitespace', () => {
delete process.env.A365_OBSERVABILITY_SCOPES_OVERRIDE;
process.env.A365_OBSERVABILITY_SCOPE_OVERRIDE = ' ';

const scopes = getObservabilityAuthenticationScope();

expect(scopes).toEqual([PROD_OBSERVABILITY_SCOPE]);
});
});

describe('getClusterCategory', () => {
Expand Down