mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-18 22:29:56 +01:00
Add TTL caching for cloud agent /enabled and session provider options (#3621)
* Add TTL caching for cloud agent /enabled and session provider options
Further reduce GitHub API requests from the cloud agent sessions provider. (extension of 89771ff43a)
- Add TtlCache and SingleSlotTtlCache utilities for TTL-based caching
- Cache /enabled results for 30 minutes (only enabled=true; disabled results
always re-fetch so users aren't stuck after enabling CCA)
- Cache the full provideChatSessionProviderOptions result for 15 minutes,
covering custom agents, models, and partner agents in a single cache entry
- refresh() no longer clears TTL caches; only auth changes and the new
"Clear Cloud Agent Caches" command force-clear them
- Register "GitHub Copilot: Clear Cloud Agent Caches" command as an escape hatch
- Add unit tests for TtlCache and SingleSlotTtlCache
* Apply suggestions from code review
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -2874,6 +2874,11 @@
|
||||
"title": "%github.copilot.command.cloudSessions.openRepository.title%",
|
||||
"icon": "$(repo)",
|
||||
"category": "GitHub Copilot"
|
||||
},
|
||||
{
|
||||
"command": "github.copilot.chat.cloudSessions.clearCaches",
|
||||
"title": "%github.copilot.command.cloudSessions.clearCaches.title%",
|
||||
"category": "GitHub Copilot"
|
||||
}
|
||||
],
|
||||
"configuration": [
|
||||
|
||||
@@ -423,6 +423,7 @@
|
||||
"github.copilot.chat.applyCopilotCLIAgentSessionChanges.apply": "Apply",
|
||||
"github.copilot.command.checkoutPullRequestReroute.title": "Checkout",
|
||||
"github.copilot.command.cloudSessions.openRepository.title": "Browse repositories...",
|
||||
"github.copilot.command.cloudSessions.clearCaches.title": "Clear Cloud Agent Caches",
|
||||
"github.copilot.command.applyCopilotCLIAgentSessionChanges": "Apply Changes to Workspace",
|
||||
"github.copilot.config.githubMcpServer.enabled": "Enable built-in support for the GitHub MCP Server.",
|
||||
"github.copilot.config.githubMcpServer.toolsets": "Specify toolsets to use from the GitHub MCP Server. [Learn more](https://aka.ms/vscode-gh-mcp-toolsets).",
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { SingleSlotTtlCache, TtlCache } from '../ttlCache';
|
||||
|
||||
describe('TtlCache', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('returns undefined for missing keys', () => {
|
||||
const cache = new TtlCache<string>(1000);
|
||||
expect(cache.get('missing')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('stores and retrieves values within TTL', () => {
|
||||
const cache = new TtlCache<number>(5000);
|
||||
cache.set('key', 42);
|
||||
expect(cache.get('key')).toBe(42);
|
||||
});
|
||||
|
||||
it('expires entries after TTL elapses', () => {
|
||||
const cache = new TtlCache<string>(1000);
|
||||
cache.set('key', 'value');
|
||||
|
||||
vi.advanceTimersByTime(999);
|
||||
expect(cache.get('key')).toBe('value');
|
||||
|
||||
vi.advanceTimersByTime(1);
|
||||
expect(cache.get('key')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('supports multiple independent keys', () => {
|
||||
const cache = new TtlCache<string>(2000);
|
||||
cache.set('a', 'alpha');
|
||||
|
||||
vi.advanceTimersByTime(1000);
|
||||
cache.set('b', 'beta');
|
||||
|
||||
vi.advanceTimersByTime(1000);
|
||||
// 'a' was set 2000ms ago → expired
|
||||
expect(cache.get('a')).toBeUndefined();
|
||||
// 'b' was set 1000ms ago → still valid
|
||||
expect(cache.get('b')).toBe('beta');
|
||||
});
|
||||
|
||||
it('overwrites existing entry and resets TTL', () => {
|
||||
const cache = new TtlCache<string>(1000);
|
||||
cache.set('key', 'old');
|
||||
|
||||
vi.advanceTimersByTime(800);
|
||||
cache.set('key', 'new');
|
||||
|
||||
vi.advanceTimersByTime(800);
|
||||
// 800ms since last set → still valid
|
||||
expect(cache.get('key')).toBe('new');
|
||||
});
|
||||
|
||||
it('delete removes entry', () => {
|
||||
const cache = new TtlCache<string>(5000);
|
||||
cache.set('key', 'value');
|
||||
cache.delete('key');
|
||||
expect(cache.get('key')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('clear removes all entries', () => {
|
||||
const cache = new TtlCache<string>(5000);
|
||||
cache.set('a', '1');
|
||||
cache.set('b', '2');
|
||||
cache.clear();
|
||||
expect(cache.get('a')).toBeUndefined();
|
||||
expect(cache.get('b')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('has returns true for non-expired entries and false otherwise', () => {
|
||||
const cache = new TtlCache<string>(1000);
|
||||
expect(cache.has('key')).toBe(false);
|
||||
|
||||
cache.set('key', 'value');
|
||||
expect(cache.has('key')).toBe(true);
|
||||
|
||||
vi.advanceTimersByTime(1000);
|
||||
expect(cache.has('key')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('SingleSlotTtlCache', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('returns undefined when empty', () => {
|
||||
const cache = new SingleSlotTtlCache<string>(1000);
|
||||
expect(cache.get('any')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('stores and retrieves a value within TTL', () => {
|
||||
const cache = new SingleSlotTtlCache<number>(5000);
|
||||
cache.set('key', 42);
|
||||
expect(cache.get('key')).toBe(42);
|
||||
});
|
||||
|
||||
it('returns undefined when key does not match', () => {
|
||||
const cache = new SingleSlotTtlCache<string>(5000);
|
||||
cache.set('key1', 'value');
|
||||
expect(cache.get('key2')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('expires entry after TTL elapses', () => {
|
||||
const cache = new SingleSlotTtlCache<string>(1000);
|
||||
cache.set('key', 'value');
|
||||
|
||||
vi.advanceTimersByTime(999);
|
||||
expect(cache.get('key')).toBe('value');
|
||||
|
||||
vi.advanceTimersByTime(1);
|
||||
expect(cache.get('key')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('replaces previous entry when set with new key', () => {
|
||||
const cache = new SingleSlotTtlCache<string>(5000);
|
||||
cache.set('key1', 'a');
|
||||
cache.set('key2', 'b');
|
||||
|
||||
expect(cache.get('key1')).toBeUndefined();
|
||||
expect(cache.get('key2')).toBe('b');
|
||||
});
|
||||
|
||||
it('replaces and resets TTL on same key', () => {
|
||||
const cache = new SingleSlotTtlCache<string>(1000);
|
||||
cache.set('key', 'old');
|
||||
|
||||
vi.advanceTimersByTime(800);
|
||||
cache.set('key', 'new');
|
||||
|
||||
vi.advanceTimersByTime(800);
|
||||
expect(cache.get('key')).toBe('new');
|
||||
});
|
||||
|
||||
it('clear removes entry', () => {
|
||||
const cache = new SingleSlotTtlCache<string>(5000);
|
||||
cache.set('key', 'value');
|
||||
cache.clear();
|
||||
expect(cache.get('key')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('has returns true for non-expired matching key', () => {
|
||||
const cache = new SingleSlotTtlCache<string>(1000);
|
||||
expect(cache.has('key')).toBe(false);
|
||||
|
||||
cache.set('key', 'value');
|
||||
expect(cache.has('key')).toBe(true);
|
||||
expect(cache.has('other')).toBe(false);
|
||||
|
||||
vi.advanceTimersByTime(1000);
|
||||
expect(cache.has('key')).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,108 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* A simple TTL (time-to-live) cache that stores key-value pairs with expiration.
|
||||
* Entries are evicted lazily on access when their TTL has elapsed.
|
||||
*/
|
||||
export class TtlCache<V> {
|
||||
private readonly _entries = new Map<string, { value: V; timestamp: number }>();
|
||||
|
||||
/**
|
||||
* @param _ttlMs The time-to-live in milliseconds for cache entries.
|
||||
*/
|
||||
constructor(private readonly _ttlMs: number) { }
|
||||
|
||||
/**
|
||||
* Returns the cached value if it exists and has not expired, otherwise `undefined`.
|
||||
*/
|
||||
get(key: string): V | undefined {
|
||||
const entry = this._entries.get(key);
|
||||
if (!entry) {
|
||||
return undefined;
|
||||
}
|
||||
if (Date.now() - entry.timestamp >= this._ttlMs) {
|
||||
this._entries.delete(key);
|
||||
return undefined;
|
||||
}
|
||||
return entry.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a value in the cache with the current timestamp.
|
||||
*/
|
||||
set(key: string, value: V): void {
|
||||
this._entries.set(key, { value, timestamp: Date.now() });
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a single entry from the cache.
|
||||
*/
|
||||
delete(key: string): void {
|
||||
this._entries.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all entries from the cache.
|
||||
*/
|
||||
clear(): void {
|
||||
this._entries.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the cache has a non-expired entry for the given key.
|
||||
*/
|
||||
has(key: string): boolean {
|
||||
return this.get(key) !== undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A single-slot TTL cache that stores one value with an associated key and expiration.
|
||||
* Useful for caching a computed result (e.g. the full options object for a given repo context).
|
||||
*/
|
||||
export class SingleSlotTtlCache<V> {
|
||||
private _entry: { value: V; timestamp: number; key: string } | undefined;
|
||||
|
||||
/**
|
||||
* @param _ttlMs The time-to-live in milliseconds for the cached entry.
|
||||
*/
|
||||
constructor(private readonly _ttlMs: number) { }
|
||||
|
||||
/**
|
||||
* Returns the cached value if the key matches and the TTL has not expired, otherwise `undefined`.
|
||||
*/
|
||||
get(key: string): V | undefined {
|
||||
if (!this._entry || this._entry.key !== key) {
|
||||
return undefined;
|
||||
}
|
||||
if (Date.now() - this._entry.timestamp >= this._ttlMs) {
|
||||
this._entry = undefined;
|
||||
return undefined;
|
||||
}
|
||||
return this._entry.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a value in the cache, replacing any previous entry.
|
||||
*/
|
||||
set(key: string, value: V): void {
|
||||
this._entry = { value, timestamp: Date.now(), key };
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the cached entry.
|
||||
*/
|
||||
clear(): void {
|
||||
this._entry = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the cache has a non-expired entry for the given key.
|
||||
*/
|
||||
has(key: string): boolean {
|
||||
return this.get(key) !== undefined;
|
||||
}
|
||||
}
|
||||
+67
-30
@@ -23,6 +23,7 @@ import { Disposable, toDisposable } from '../../../util/vs/base/common/lifecycle
|
||||
import { ResourceMap } from '../../../util/vs/base/common/map';
|
||||
import { IInstantiationService } from '../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { IChatDelegationSummaryService } from '../../agents/copilotcli/common/delegationSummaryService';
|
||||
import { SingleSlotTtlCache, TtlCache } from '../common/ttlCache';
|
||||
import { isUntitledSessionId } from '../common/utils';
|
||||
import { body_suffix, CONTINUE_TRUNCATION, extractTitle, formatBodyPlaceholder, getAuthorDisplayName, getRepoId, JOBS_API_VERSION, SessionIdForPr, toOpenPullRequestWebviewUri, truncatePrompt } from '../vscode/copilotCodingAgentUtils';
|
||||
import { CopilotCloudGitOperationsManager } from './copilotCloudGitOperationsManager';
|
||||
@@ -63,9 +64,15 @@ const DEFAULT_REPOSITORY_ID = '___vscode_repository_default___';
|
||||
const ACTIVE_SESSION_POLL_INTERVAL_MS = 5 * 1000; // 5 seconds
|
||||
const SEEN_DELEGATION_PROMPT_KEY = 'seenDelegationPromptBefore';
|
||||
const OPEN_REPOSITORY_COMMAND_ID = 'github.copilot.chat.cloudSessions.openRepository';
|
||||
const CLEAR_CACHES_COMMAND_ID = 'github.copilot.chat.cloudSessions.clearCaches';
|
||||
const USER_SELECTED_REPOS_KEY = 'userSelectedRepositories';
|
||||
const USER_SELECTED_REPOS_EXPIRY_MS = 7 * 24 * 60 * 60 * 1000; // 1 week
|
||||
|
||||
// TTL for caching /enabled responses (only caches enabled=true; disabled results always re-fetch)
|
||||
const CCA_ENABLED_CACHE_TTL_MS = 30 * 60 * 1_000; // 30 minutes
|
||||
// TTL for caching session provider options (custom agents, models, partner agents, etc.)
|
||||
const OPTIONS_CACHE_TTL_MS = 15 * 60 * 1_000; // 15 minutes
|
||||
|
||||
interface UserSelectedRepository {
|
||||
name: string;
|
||||
timestamp: number;
|
||||
@@ -175,10 +182,13 @@ export class CopilotCloudSessionsProvider extends Disposable implements vscode.C
|
||||
private readonly plainTextRenderer = new PlainTextRenderer();
|
||||
private readonly gitOperationsManager = new CopilotCloudGitOperationsManager(this.logService, this._gitService, this._gitExtensionService);
|
||||
|
||||
private _partnerAgentsAvailableCache: Map<string, { id: string; name: string; at?: string }[]> | undefined;
|
||||
// TTL cache for CCA enabled status per repository (key: "owner/repo")
|
||||
// Only caches enabled=true results; disabled results always re-fetch to avoid stuck states
|
||||
private _ccaEnabledCache = new TtlCache<CCAEnabledResult>(CCA_ENABLED_CACHE_TTL_MS);
|
||||
|
||||
// Cache for CCA enabled status per repository (key: "owner/repo")
|
||||
private _ccaEnabledCache: Map<string, CCAEnabledResult> | undefined;
|
||||
// Single-slot TTL cache for the full session provider options result (custom agents, models, partner agents, etc.)
|
||||
// Caches the most recently computed options regardless of repo/workspace context
|
||||
private _optionsCache = new SingleSlotTtlCache<vscode.ChatSessionProviderOptions>(OPTIONS_CACHE_TTL_MS);
|
||||
|
||||
// Title
|
||||
private TITLE = vscode.l10n.t('Delegate to cloud agent');
|
||||
@@ -270,7 +280,10 @@ export class CopilotCloudSessionsProvider extends Disposable implements vscode.C
|
||||
|
||||
}
|
||||
const onDebouncedAuthRefresh = Event.debounce(this._authenticationService.onDidAuthenticationChange, () => { }, 500);
|
||||
this._register(onDebouncedAuthRefresh(() => this.refresh()));
|
||||
this._register(onDebouncedAuthRefresh(() => {
|
||||
this.clearOptionsCaches();
|
||||
this.refresh();
|
||||
}));
|
||||
this.telemetry.sendTelemetryEvent('copilotCloudSessions.refreshInterval', { microsoft: true, github: false }, telemetryObj);
|
||||
});
|
||||
}
|
||||
@@ -372,6 +385,13 @@ export class CopilotCloudSessionsProvider extends Disposable implements vscode.C
|
||||
});
|
||||
};
|
||||
this._register(vscode.commands.registerCommand(OPEN_REPOSITORY_COMMAND_ID, openRepositoryCommand));
|
||||
|
||||
this._register(vscode.commands.registerCommand(CLEAR_CACHES_COMMAND_ID, () => {
|
||||
this.logService.debug('copilotCloudSessionsProvider#clearCaches: clearing all cloud agent caches');
|
||||
this.clearOptionsCaches();
|
||||
this.refresh();
|
||||
this._onDidChangeChatSessionProviderOptions.fire();
|
||||
}));
|
||||
}
|
||||
|
||||
private getRefreshIntervalTime(hasHistoricalSessions: boolean): number {
|
||||
@@ -395,14 +415,25 @@ export class CopilotCloudSessionsProvider extends Disposable implements vscode.C
|
||||
this.cachedSessionItems = undefined;
|
||||
this.activeSessionIds.clear();
|
||||
this.stopActiveSessionPolling();
|
||||
this._partnerAgentsAvailableCache = undefined;
|
||||
this._ccaEnabledCache = undefined;
|
||||
// Note: _ccaEnabledCache and _optionsCache are TTL-based and NOT cleared on refresh.
|
||||
// Use clearOptionsCaches() to force-clear them (e.g. on auth change).
|
||||
this._onDidChangeChatSessionItems.fire();
|
||||
}
|
||||
|
||||
/**
|
||||
* Force-clears the TTL-based caches for /enabled and session provider options.
|
||||
* Use for auth changes or explicit user-initiated refresh where stale data is unacceptable.
|
||||
*/
|
||||
private clearOptionsCaches(): void {
|
||||
this._ccaEnabledCache.clear();
|
||||
this._optionsCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the Copilot cloud agent is enabled for a repository.
|
||||
* Results are cached per repository until refresh() is called.
|
||||
* Results are cached with a TTL: enabled=true results are cached for {@link CCA_ENABLED_CACHE_TTL_MS},
|
||||
* while enabled=false results are never cached (always re-fetched) so users who just
|
||||
* enabled CCA are not stuck in a disabled state.
|
||||
* @param owner Repository owner
|
||||
* @param repo Repository name
|
||||
* @returns CCAEnabledResult with enabled status and optional status code
|
||||
@@ -410,18 +441,21 @@ export class CopilotCloudSessionsProvider extends Disposable implements vscode.C
|
||||
private async checkCCAEnabled(owner: string, repo: string): Promise<CCAEnabledResult> {
|
||||
const cacheKey = `${owner}/${repo}`;
|
||||
|
||||
if (!this._ccaEnabledCache) {
|
||||
this._ccaEnabledCache = new Map();
|
||||
}
|
||||
|
||||
const cached = this._ccaEnabledCache.get(cacheKey);
|
||||
if (cached !== undefined) {
|
||||
if (cached !== undefined && cached.enabled === true) {
|
||||
this.logService.trace(`copilotCloudSessionsProvider#checkCCAEnabled: using cached CCA enabled status for ${owner}/${repo}: ${cached.enabled}`);
|
||||
return cached;
|
||||
}
|
||||
|
||||
const result = await this._octoKitService.isCCAEnabled(owner, repo, { createIfNone: false });
|
||||
this._ccaEnabledCache.set(cacheKey, result);
|
||||
|
||||
// Only cache enabled=true results with a TTL; disabled results should always re-fetch
|
||||
if (result.enabled === true) {
|
||||
this._ccaEnabledCache.set(cacheKey, result);
|
||||
} else {
|
||||
// Remove any stale positive cache entry
|
||||
this._ccaEnabledCache.delete(cacheKey);
|
||||
}
|
||||
|
||||
this.telemetry.sendTelemetryEvent('copilot.codingAgent.CCAIsEnabledCheck', { microsoft: true, github: false }, {
|
||||
enabled: String(result.enabled),
|
||||
@@ -530,13 +564,6 @@ export class CopilotCloudSessionsProvider extends Disposable implements vscode.C
|
||||
* TODO: Remove once given a proper API
|
||||
*/
|
||||
private async getAvailablePartnerAgents(owner: string, repo: string): Promise<{ id: string; name: string; at?: string; codiconId?: string }[]> {
|
||||
const cacheKey = `${owner}/${repo}`;
|
||||
|
||||
// Return cached result if available
|
||||
if (this._partnerAgentsAvailableCache?.has(cacheKey)) {
|
||||
return this._partnerAgentsAvailableCache.get(cacheKey)!;
|
||||
}
|
||||
|
||||
try {
|
||||
// Fetch assignable actors for the repository
|
||||
const assignableActors = await this._octoKitService.getAssignableActors(owner, repo, { createIfNone: false });
|
||||
@@ -556,11 +583,6 @@ export class CopilotCloudSessionsProvider extends Disposable implements vscode.C
|
||||
}
|
||||
}
|
||||
|
||||
if (!this._partnerAgentsAvailableCache) {
|
||||
this._partnerAgentsAvailableCache = new Map();
|
||||
}
|
||||
this._partnerAgentsAvailableCache.set(cacheKey, availableAgents);
|
||||
|
||||
return availableAgents;
|
||||
} catch (error) {
|
||||
this.logService.error(`Error fetching partner agents: ${error}`);
|
||||
@@ -625,7 +647,6 @@ export class CopilotCloudSessionsProvider extends Disposable implements vscode.C
|
||||
async provideChatSessionProviderOptions(token: vscode.CancellationToken): Promise<vscode.ChatSessionProviderOptions> {
|
||||
this.logService.trace('copilotCloudSessionsProvider#provideChatSessionProviderOptions Start');
|
||||
|
||||
const optionGroups: vscode.ChatSessionProviderOptionGroup[] = [];
|
||||
const repoIds = await getRepoId(this._gitService);
|
||||
const repoId = repoIds?.[0];
|
||||
|
||||
@@ -643,6 +664,17 @@ export class CopilotCloudSessionsProvider extends Disposable implements vscode.C
|
||||
return { optionGroups: [] };
|
||||
}
|
||||
|
||||
// Check TTL-based options cache
|
||||
const optionsCacheKey = repoIds && repoIds.length > 0
|
||||
? repoIds.map(r => `${r.org}/${r.repo}`).sort().join(',')
|
||||
: '';
|
||||
const cachedOptions = this._optionsCache.get(optionsCacheKey);
|
||||
if (cachedOptions) {
|
||||
this.logService.trace('copilotCloudSessionsProvider#provideChatSessionProviderOptions: using cached options');
|
||||
return cachedOptions;
|
||||
}
|
||||
|
||||
const optionGroups: vscode.ChatSessionProviderOptionGroup[] = [];
|
||||
try {
|
||||
// Fetch agents (requires repo), models (global), and partner agents in parallel
|
||||
const [customAgents, models, partnerAgents] = await Promise.allSettled([
|
||||
@@ -742,8 +774,13 @@ export class CopilotCloudSessionsProvider extends Disposable implements vscode.C
|
||||
});
|
||||
}
|
||||
|
||||
this.logService.trace(`copilotCloudSessionsProvider#provideChatSessionProviderOptions: Returning options: ${JSON.stringify(optionGroups, undefined, 2)}`);
|
||||
return { optionGroups };
|
||||
const result: vscode.ChatSessionProviderOptions = { optionGroups };
|
||||
|
||||
// Cache the full options result with TTL
|
||||
this._optionsCache.set(optionsCacheKey, result);
|
||||
|
||||
this.logService.debug(`copilotCloudSessionsProvider#provideChatSessionProviderOptions: Returning options: ${JSON.stringify(optionGroups, undefined, 2)}`);
|
||||
return result;
|
||||
} catch (error) {
|
||||
this.logService.error(`[copilotCloudSessionsProvider#provideChatSessionProviderOptions] Error fetching options: ${error}`);
|
||||
return { optionGroups: [] };
|
||||
@@ -2277,9 +2314,9 @@ export class CopilotCloudSessionsProvider extends Disposable implements vscode.C
|
||||
};
|
||||
|
||||
stream?.progress(vscode.l10n.t('Delegating to cloud agent'));
|
||||
this.logService.trace(`[postCopilotAgentJob] Invoking cloud agent job with payload: ${JSON.stringify(payload)}`);
|
||||
this.logService.debug(`[postCopilotAgentJob] Invoking cloud agent job with payload: ${JSON.stringify(payload)}`);
|
||||
const response = await this._octoKitService.postCopilotAgentJob(repoOwner, repoName, JOBS_API_VERSION, payload, { createIfNone: true });
|
||||
this.logService.trace(`[postCopilotAgentJob] Received response from cloud agent job invocation: ${JSON.stringify(response)}`);
|
||||
this.logService.debug(`[postCopilotAgentJob] Received response from cloud agent job invocation: ${JSON.stringify(response)}`);
|
||||
if (!this.validateRemoteAgentJobResponse(response)) {
|
||||
const statusCode = response?.status;
|
||||
switch (statusCode) {
|
||||
|
||||
Reference in New Issue
Block a user