Path: blob/main/src/vs/platform/agentHost/test/node/agentConfigurationService.test.ts
13399 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { DisposableStore } from '../../../../base/common/lifecycle.js';7import { URI } from '../../../../base/common/uri.js';8import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';9import { NullLogService } from '../../../log/common/log.js';10import { createSchema, schemaProperty } from '../../common/agentHostSchema.js';11import type { SessionConfigState, RootConfigState } from '../../common/state/protocol/state.js';12import { buildSubagentSessionUri, SessionStatus, type SessionSummary } from '../../common/state/sessionState.js';13import { AgentConfigurationService } from '../../node/agentConfigurationService.js';14import { AgentHostStateManager } from '../../node/agentHostStateManager.js';1516suite('AgentConfigurationService', () => {1718const disposables = new DisposableStore();19let manager: AgentHostStateManager;20let service: AgentConfigurationService;2122const schema = createSchema({23level: schemaProperty<'low' | 'high'>({24type: 'string',25title: 'level',26enum: ['low', 'high'],27}),28limit: schemaProperty<number>({ type: 'number', title: 'limit' }),29});3031function seedSessionConfig(sessionUri: string, values: Record<string, unknown>): void {32const state = manager.getSessionState(sessionUri);33assert.ok(state, `Session not found: ${sessionUri}`);34const mutable = state as { config?: SessionConfigState };35mutable.config = {36schema: schema.toProtocol(),37values,38};39}4041function seedRootConfig(values: Record<string, unknown>): void {42const rootMutable = manager.rootState as { config?: RootConfigState };43rootMutable.config = {44schema: schema.toProtocol(),45values,46};47}4849function makeSummary(resource: string, workingDirectory?: string): SessionSummary {50return {51resource,52provider: 'copilot',53title: 't',54status: SessionStatus.Idle,55createdAt: Date.now(),56modifiedAt: Date.now(),57project: { uri: 'file:///project', displayName: 'Project' },58workingDirectory,59};60}6162setup(() => {63manager = disposables.add(new AgentHostStateManager(new NullLogService()));64service = disposables.add(new AgentConfigurationService(manager, new NullLogService()));65});6667teardown(() => disposables.clear());6869ensureNoDisposablesAreLeakedInTestSuite();7071// ---- getEffectiveValue ------------------------------------------------7273suite('getEffectiveValue', () => {7475test('returns session value when present', () => {76const uri = URI.from({ scheme: 'copilot', path: '/a' }).toString();77manager.createSession(makeSummary(uri));78seedSessionConfig(uri, { level: 'high' });79assert.strictEqual(service.getEffectiveValue(uri, schema, 'level'), 'high');80});8182test('falls back to host value when session does not provide the key', () => {83const uri = URI.from({ scheme: 'copilot', path: '/a' }).toString();84manager.createSession(makeSummary(uri));85seedSessionConfig(uri, { limit: 5 });86seedRootConfig({ level: 'low' });87assert.strictEqual(service.getEffectiveValue(uri, schema, 'level'), 'low');88});8990test('inherits from parent subagent session', () => {91const parent = URI.from({ scheme: 'copilot', path: '/parent' }).toString();92manager.createSession(makeSummary(parent));93seedSessionConfig(parent, { level: 'high' });9495const child = buildSubagentSessionUri(parent, 'toolcall-1');96manager.createSession(makeSummary(child));9798assert.strictEqual(service.getEffectiveValue(child, schema, 'level'), 'high');99});100101test('session value takes precedence over parent and host', () => {102const parent = URI.from({ scheme: 'copilot', path: '/parent' }).toString();103manager.createSession(makeSummary(parent));104seedSessionConfig(parent, { level: 'high' });105106const child = buildSubagentSessionUri(parent, 'tc-2');107manager.createSession(makeSummary(child));108seedSessionConfig(child, { level: 'low' });109seedRootConfig({ level: 'high' });110111assert.strictEqual(service.getEffectiveValue(child, schema, 'level'), 'low');112});113114test('skips layers whose value fails schema validation and falls through', () => {115const uri = URI.from({ scheme: 'copilot', path: '/a' }).toString();116manager.createSession(makeSummary(uri));117seedSessionConfig(uri, { level: 'bogus' });118seedRootConfig({ level: 'high' });119assert.strictEqual(service.getEffectiveValue(uri, schema, 'level'), 'high');120});121122test('returns undefined when no layer provides a valid value', () => {123const uri = URI.from({ scheme: 'copilot', path: '/a' }).toString();124manager.createSession(makeSummary(uri));125seedSessionConfig(uri, {});126assert.strictEqual(service.getEffectiveValue(uri, schema, 'level'), undefined);127});128});129130// ---- getEffectiveWorkingDirectory -------------------------------------131132suite('getEffectiveWorkingDirectory', () => {133134test('returns session working directory when set', () => {135const uri = URI.from({ scheme: 'copilot', path: '/a' }).toString();136manager.createSession(makeSummary(uri, 'file:///work'));137assert.strictEqual(service.getEffectiveWorkingDirectory(uri), 'file:///work');138});139140test('falls back to parent session working directory for subagents', () => {141const parent = URI.from({ scheme: 'copilot', path: '/parent' }).toString();142manager.createSession(makeSummary(parent, 'file:///work/parent'));143144const child = buildSubagentSessionUri(parent, 'tc-3');145manager.createSession(makeSummary(child));146assert.strictEqual(service.getEffectiveWorkingDirectory(child), 'file:///work/parent');147});148149test('returns undefined when neither layer has a working directory', () => {150const uri = URI.from({ scheme: 'copilot', path: '/a' }).toString();151manager.createSession(makeSummary(uri));152assert.strictEqual(service.getEffectiveWorkingDirectory(uri), undefined);153});154});155156// ---- updateSessionConfig ----------------------------------------------157158suite('updateSessionConfig', () => {159160test('merges the patch into the session config values', () => {161const uri = URI.from({ scheme: 'copilot', path: '/a' }).toString();162manager.createSession(makeSummary(uri));163seedSessionConfig(uri, { level: 'low', limit: 1 });164165service.updateSessionConfig(uri, { limit: 42 });166167const state = manager.getSessionState(uri);168assert.deepStrictEqual(state?.config?.values, { level: 'low', limit: 42 });169});170});171});172173174