Skip to content
Merged
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
42 changes: 28 additions & 14 deletions extensions/copilot/src/extension/intents/node/agentIntent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { IEnvService } from '../../../platform/env/common/envService';
import { ILogService } from '../../../platform/log/common/logService';
import { IEditLogService } from '../../../platform/multiFileEdit/common/editLogService';
import { CUSTOM_TOOL_SEARCH_NAME, isAnthropicContextEditingEnabled } from '../../../platform/networking/common/anthropic';
import { IChatEndpoint } from '../../../platform/networking/common/networking';
import { IChatEndpoint, isCAPIEndpoint } from '../../../platform/networking/common/networking';
import { modelsWithoutResponsesContextManagement } from '../../../platform/networking/common/openai';
import { INotebookService } from '../../../platform/notebook/common/notebookService';
import { GenAiMetrics } from '../../../platform/otel/common/genAiMetrics';
Expand Down Expand Up @@ -69,6 +69,7 @@ import { getAgentMaxRequests } from '../common/agentConfig';
import { addCacheBreakpoints } from './cacheBreakpoints';
import { EditCodeIntent, EditCodeIntentInvocation, EditCodeIntentInvocationOptions, mergeMetadata, toNewChatReferences } from './editCodeIntent';
import { ToolCallingLoop } from './toolCallingLoop';
import { IAuthenticationService } from '../../../platform/authentication/common/authentication';

function isResponsesCompactionContextManagementEnabled(endpoint: IChatEndpoint, configurationService: IConfigurationService, experimentationService: IExperimentationService): boolean {
return endpoint.apiType === 'responses'
Expand Down Expand Up @@ -99,8 +100,17 @@ export function isTodoToolExplicitlyEnabled(request: vscode.ChatRequest): boolea
*
* @internal - exported for testing
*/
export function isBackgroundTodoAgentEnabled(configurationService: IConfigurationService, experimentationService: IExperimentationService, request: vscode.ChatRequest): boolean {
return configurationService.getExperimentBasedConfig(ConfigKey.Advanced.BackgroundTodoAgentEnabled, experimentationService)
export function isBackgroundTodoAgentEnabled(
endpoint: IChatEndpoint,
configurationService: IConfigurationService,
experimentationService: IExperimentationService,
authenticationService: IAuthenticationService,
request: vscode.ChatRequest): boolean {
const token = authenticationService.copilotToken;

// Only enable for a signed in no-free plan user talking to the CAPI endpoint.
const isEnabledForToken = token !== undefined && !token.isFreeUser && !token.isNoAuthUser && isCAPIEndpoint(endpoint);
return isEnabledForToken && configurationService.getExperimentBasedConfig(ConfigKey.Advanced.BackgroundTodoAgentEnabled, experimentationService)
&& !isTodoToolExplicitlyEnabled(request);
}

Expand Down Expand Up @@ -148,6 +158,8 @@ export const getAgentTools = async (accessor: ServicesAccessor, request: vscode.
const experimentationService = accessor.get<IExperimentationService>(IExperimentationService);
const endpointProvider = accessor.get<IEndpointProvider>(IEndpointProvider);
const editToolLearningService = accessor.get<IEditToolLearningService>(IEditToolLearningService);
const authenticationService = accessor.get<IAuthenticationService>(IAuthenticationService);

model ??= await endpointProvider.getChatEndpoint(request);

const allowTools: Record<string, boolean> = {};
Expand Down Expand Up @@ -180,10 +192,9 @@ export const getAgentTools = async (accessor: ServicesAccessor, request: vscode.
allowTools[ToolName.CoreRunTest] = await testService.hasAnyTests();
allowTools[ToolName.CoreRunTask] = tasksService.getTasks().length > 0;

// BYOK endpoints that own their `Authorization` have no Copilot token for the
// agentic proxy or override models the subagents rely on, so disable them
// entirely and skip the (otherwise unnecessary) config and endpoint lookups.
if (model.ownsAuthorization) {
// The specialized subagents must only run when
// the main agent is on CAPI.
if (!isCAPIEndpoint(model)) {
allowTools[ToolName.SearchSubagent] = false;
allowTools[ToolName.ExploreSubagent] = false;
allowTools[ToolName.ExecutionSubagent] = false;
Expand Down Expand Up @@ -222,7 +233,7 @@ export const getAgentTools = async (accessor: ServicesAccessor, request: vscode.
allowTools[ToolName.CoreManageTodoList] = false;
}

if (isBackgroundTodoAgentEnabled(configurationService, experimentationService, request)) {
if (isBackgroundTodoAgentEnabled(model, configurationService, experimentationService, authenticationService, request)) {
allowTools[ToolName.CoreManageTodoList] = false;
}

Expand Down Expand Up @@ -383,12 +394,12 @@ export class AgentIntent extends EditCodeIntent {
// Do NOT pass the request `token` as parentToken — it may be cancelled
// by the framework after the turn ends, which would immediately abort
// the background pass even on a normal completion.
if (isBackgroundTodoAgentEnabled(this.configurationService, this.expService, request)) {
const todoProcessor = this._backgroundTodoProcessors.get(conversation.sessionId);
const todoProcessor = this._backgroundTodoProcessors.get(conversation.sessionId);
if (todoProcessor !== undefined) {
const currentTurn = conversation.getLatestTurn();
const invocation = currentTurn.getMetadata(IntentInvocationMetadata)?.value;
const executionContext = invocation instanceof AgentIntentInvocation ? invocation.getBackgroundTodoExecutionContext() : undefined;
if (todoProcessor && executionContext) {
if (executionContext) {
todoProcessor.requestFinalReview(currentTurn.id, executionContext);
await todoProcessor.waitForCompletion();
}
Expand Down Expand Up @@ -567,6 +578,7 @@ export class AgentIntentInvocation extends EditCodeIntentInvocation implements I
@IAutomodeService private readonly automodeService: IAutomodeService,
@IOTelService protected override readonly otelService: IOTelService,
@ISessionTranscriptService private readonly sessionTranscriptService: ISessionTranscriptService,
@IAuthenticationService private readonly authenticationService: IAuthenticationService,
) {
super(intent, location, endpoint, request, intentOptions, instantiationService, codeMapperService, envService, promptPathRepresentationService, endpointProvider, workspaceService, toolsService, configurationService, editLogService, commandService, telemetryService, notebookService, otelService);
}
Expand Down Expand Up @@ -928,7 +940,7 @@ export class AgentIntentInvocation extends EditCodeIntentInvocation implements I
}

// ── Background todo processing ──────────────────────────────────
this._maybeStartBackgroundTodoPass(promptContext, token);
this._maybeStartBackgroundTodoPass(endpoint, promptContext, token);

const lastMessage = result.messages.at(-1);
if (lastMessage?.role === Raw.ChatRole.User) {
Expand Down Expand Up @@ -1349,6 +1361,7 @@ export class AgentIntentInvocation extends EditCodeIntentInvocation implements I
* Kick off a background todo pass if the policy says to run.
*/
private _maybeStartBackgroundTodoPass(
endpoint: IChatEndpoint,
promptContext: IBuildPromptContext,
token: vscode.CancellationToken,
): void {
Comment thread
vritant24 marked this conversation as resolved.
Expand All @@ -1373,10 +1386,9 @@ export class AgentIntentInvocation extends EditCodeIntentInvocation implements I
telemetryService: this.telemetryService,
promptContext,
};
this._backgroundTodoExecutionContext = executionContext;

const { decision, reason, delta } = processor.shouldRun({
backgroundTodoAgentEnabled: isBackgroundTodoAgentEnabled(this.configurationService, this.expService, this.request),
backgroundTodoAgentEnabled: isBackgroundTodoAgentEnabled(endpoint, this.configurationService, this.expService, this.authenticationService, this.request),
todoToolExplicitlyEnabled: isTodoToolExplicitlyEnabled(this.request),
isAgentPrompt: this.prompt === AgentPrompt,
promptContext,
Expand All @@ -1387,6 +1399,7 @@ export class AgentIntentInvocation extends EditCodeIntentInvocation implements I

if (decision === BackgroundTodoDecision.Wait && reason === 'processorInProgress' && delta) {
// Coalesce into the queue so the latest context is not lost.
this._backgroundTodoExecutionContext = executionContext;
processor.requestRegularPass(delta, executionContext, token, turnId);
return;
}
Expand All @@ -1395,6 +1408,7 @@ export class AgentIntentInvocation extends EditCodeIntentInvocation implements I
return;
}

this._backgroundTodoExecutionContext = executionContext;
processor.requestRegularPass(delta, executionContext, token, turnId);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { ICodeMapperService } from '../../prompts/node/codeMapper/codeMapperServ
import { IToolsService } from '../../tools/common/toolsService';
import { getAgentMaxRequests } from '../common/agentConfig';
import { AgentIntentInvocation } from './agentIntent';
import { IAuthenticationService } from '../../../platform/authentication/common/authentication';


const getTools = (instaService: IInstantiationService, request: vscode.ChatRequest): Promise<vscode.LanguageModelToolInformation[]> =>
Expand Down Expand Up @@ -130,8 +131,9 @@ export class AskAgentIntentInvocation extends AgentIntentInvocation {
@IAutomodeService automodeService: IAutomodeService,
@IOTelService otelService: IOTelService,
@ISessionTranscriptService sessionTranscriptService: ISessionTranscriptService,
@IAuthenticationService authenticationService: IAuthenticationService,
) {
super(intent, location, endpoint, request, { processCodeblocks: true }, instantiationService, codeMapperService, envService, promptPathRepresentationService, endpointProvider, workspaceService, toolsService, configurationService, editLogService, commandService, telemetryService, notebookService, logService, expService, automodeService, otelService, sessionTranscriptService);
super(intent, location, endpoint, request, { processCodeblocks: true }, instantiationService, codeMapperService, envService, promptPathRepresentationService, endpointProvider, workspaceService, toolsService, configurationService, editLogService, commandService, telemetryService, notebookService, logService, expService, automodeService, otelService, sessionTranscriptService, authenticationService);
}

public override async getAvailableTools(): Promise<vscode.LanguageModelToolInformation[]> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { ToolName } from '../../tools/common/toolNames';
import { IToolsService } from '../../tools/common/toolsService';
import { AgentIntentInvocation } from './agentIntent';
import { EditCodeIntentOptions } from './editCodeIntent';
import { IAuthenticationService } from '../../../platform/authentication/common/authentication';

const getTools = (instaService: IInstantiationService, request: vscode.ChatRequest): Promise<vscode.LanguageModelToolInformation[]> =>
instaService.invokeFunction(async accessor => {
Expand Down Expand Up @@ -91,8 +92,9 @@ export class EditCode2IntentInvocation extends AgentIntentInvocation {
@IAutomodeService automodeService: IAutomodeService,
@IOTelService otelService: IOTelService,
@ISessionTranscriptService sessionTranscriptService: ISessionTranscriptService,
@IAuthenticationService authenticationService: IAuthenticationService,
) {
super(intent, location, endpoint, request, intentOptions, instantiationService, codeMapperService, envService, promptPathRepresentationService, endpointProvider, workspaceService, toolsService, configurationService, editLogService, commandService, telemetryService, notebookService, logService, expService, automodeService, otelService, sessionTranscriptService);
super(intent, location, endpoint, request, intentOptions, instantiationService, codeMapperService, envService, promptPathRepresentationService, endpointProvider, workspaceService, toolsService, configurationService, editLogService, commandService, telemetryService, notebookService, logService, expService, automodeService, otelService, sessionTranscriptService, authenticationService);
}

public override async getAvailableTools(): Promise<vscode.LanguageModelToolInformation[]> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { IToolsService } from '../../tools/common/toolsService';
import { getAgentMaxRequests } from '../common/agentConfig';
import { EditCodeIntent, EditCodeIntentOptions } from './editCodeIntent';
import { EditCode2IntentInvocation } from './editCodeIntent2';
import { IAuthenticationService } from '../../../platform/authentication/common/authentication';

const getTools = (instaService: IInstantiationService, request: vscode.ChatRequest): Promise<vscode.LanguageModelToolInformation[]> =>
instaService.invokeFunction(async accessor => {
Expand Down Expand Up @@ -109,8 +110,9 @@ export class NotebookEditorIntentInvocation extends EditCode2IntentInvocation {
@IAutomodeService automodeService: IAutomodeService,
@IOTelService otelService: IOTelService,
@ISessionTranscriptService sessionTranscriptService: ISessionTranscriptService,
@IAuthenticationService authenticationService: IAuthenticationService,
) {
super(intent, location, endpoint, request, intentOptions, instantiationService, codeMapperService, envService, promptPathRepresentationService, endpointProvider, workspaceService, toolsService, configurationService, editLogService, commandService, telemetryService, notebookService, logService, expService, automodeService, otelService, sessionTranscriptService);
super(intent, location, endpoint, request, intentOptions, instantiationService, codeMapperService, envService, promptPathRepresentationService, endpointProvider, workspaceService, toolsService, configurationService, editLogService, commandService, telemetryService, notebookService, logService, expService, automodeService, otelService, sessionTranscriptService, authenticationService);
}

protected override prompt = NotebookInlinePrompt;
Expand Down
Loading
Loading