Skip to content

Commit 4701a3c

Browse files
committed
adding first pass at implementation
1 parent 763d894 commit 4701a3c

6 files changed

Lines changed: 81 additions & 5 deletions

File tree

extensions/copilot/package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4660,6 +4660,16 @@
46604660
"onExp"
46614661
]
46624662
},
4663+
"github.copilot.chat.searchSubagent.thoroughnessEnabled": {
4664+
"type": "boolean",
4665+
"default": false,
4666+
"markdownDescription": "%github.copilot.config.searchSubagent.thoroughnessEnabled%",
4667+
"tags": [
4668+
"advanced",
4669+
"experimental",
4670+
"onExp"
4671+
]
4672+
},
46634673
"github.copilot.chat.agentDebugLog.enabled": {
46644674
"type": "boolean",
46654675
"default": false,

extensions/copilot/package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,7 @@
482482
"github.copilot.config.searchSubagent.useAgenticProxy": "Use the agentic proxy for the search subagent tool.",
483483
"github.copilot.config.searchSubagent.model": "Model to use for the search subagent. When useAgenticProxy is enabled, defaults to 'agentic-search-v3'. Otherwise defaults to the main agent model.",
484484
"github.copilot.config.searchSubagent.toolCallLimit": "Maximum number of tool calls the search subagent can make during exploration.",
485+
"github.copilot.config.searchSubagent.thoroughnessEnabled": "Enable the thoroughness parameter on the search subagent tool. When enabled, the caller can pass 'quick', 'medium', or 'thorough' to adjust the number of allowed tool-call turns (0.5×, 1×, or 2× the base toolCallLimit respectively).",
485486
"copilot.tools.executionSubagent.name": "Execution Subagent",
486487
"copilot.tools.executionSubagent.description": "Launch an execution-focused subagent that runs one or more terminal commands to accomplish a task. It is designed to select an efficient summary of the terminal outputs to return to the main agent context.",
487488
"github.copilot.config.executionSubagent.enabled": "Enable the Execution Subagent tool in Copilot Chat. The Execution Subagent is designed to run terminal commands to accomplish an execution-based task.",

extensions/copilot/src/extension/prompt/node/searchSubagentToolCallingLoop.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ export interface ISearchSubagentToolCallingLoopOptions extends IToolCallingLoopO
3737
subAgentInvocationId?: string;
3838
/** The tool_call_id from the parent agent's LLM response that triggered this subagent invocation. */
3939
parentToolCallId?: string;
40+
/** Thoroughness level for the search, passed through to the prompt when thoroughnessEnabled config is on. */
41+
thoroughness?: 'quick' | 'medium' | 'thorough';
4042
}
4143

4244
export class SearchSubagentToolCallingLoop extends ToolCallingLoop<ISearchSubagentToolCallingLoopOptions> {
@@ -109,14 +111,16 @@ export class SearchSubagentToolCallingLoop extends ToolCallingLoop<ISearchSubage
109111

110112
protected async buildPrompt(buildPromptContext: IBuildPromptContext, progress: Progress<ChatResponseReferencePart | ChatResponseProgressPart>, token: CancellationToken): Promise<IBuildPromptResult> {
111113
const endpoint = await this.getEndpoint();
112-
const maxSearchTurns = this._configurationService.getExperimentBasedConfig(ConfigKey.Advanced.SearchSubagentToolCallLimit, this._experimentationService);
114+
// Use the effective tool call limit from options (already adjusted for thoroughness in the tool)
115+
const maxSearchTurns = this.options.toolCallLimit;
113116
const renderer = PromptRenderer.create(
114117
this.instantiationService,
115118
endpoint,
116119
SearchSubagentPrompt,
117120
{
118121
promptContext: buildPromptContext,
119-
maxSearchTurns
122+
maxSearchTurns,
123+
thoroughness: this.options.thoroughness,
120124
}
121125
);
122126
return await renderer.render(progress, token);

extensions/copilot/src/extension/prompts/node/agent/searchSubagentPrompt.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,16 @@ import { ChatToolCalls } from '../panel/toolCalling';
1111

1212
export interface SearchSubagentPromptProps extends GenericBasePromptElementProps {
1313
readonly maxSearchTurns: number;
14+
/** Thoroughness level requested by the caller. Influences the speed vs. thoroughness guidance in the system prompt. */
15+
readonly thoroughness?: 'quick' | 'medium' | 'thorough';
1416
}
1517

18+
const THOROUGHNESS_GUIDANCE: Record<NonNullable<SearchSubagentPromptProps['thoroughness']>, string> = {
19+
quick: 'Prioritize speed: parallelize as many tool calls as possible, stop as soon as you have a reasonable answer, and avoid deep file reads unless strictly necessary.',
20+
medium: 'Use a balanced approach: parallelize where possible and stop when you have sufficient context.',
21+
thorough: 'Be exhaustive: explore all plausible leads, read deeper into files, and verify findings before concluding.',
22+
};
23+
1624
/**
1725
* Prompt for the search subagent that uses custom search instructions
1826
* instead of the default agent system prompt.
@@ -28,12 +36,15 @@ export class SearchSubagentPrompt extends PromptElement<SearchSubagentPromptProp
2836
const currentTurn = toolCallRounds?.length ?? 0;
2937
const isLastTurn = currentTurn >= this.props.maxSearchTurns - 1;
3038

39+
const thoroughnessGuidance = this.props.thoroughness ? THOROUGHNESS_GUIDANCE[this.props.thoroughness] : undefined;
40+
3141
return (
3242
<>
3343
<SystemMessage priority={1000}>
3444
You are an AI coding research assistant that uses search tools to gather information. You can call tools to search for information and read files across a codebase.<br />
3545
<br />
36-
Once you have thoroughly searched the repository, return a message with ONLY: the &lt;final_answer&gt; tag to provide paths and line ranges of relevant code snippets.<br />
46+
{thoroughnessGuidance && (<>{thoroughnessGuidance}<br /><br /></>)}
47+
Once you have searched the repository, return a message with ONLY: the &lt;final_answer&gt; tag to provide paths and line ranges of relevant code snippets.<br />
3748
<br />
3849
Example:<br />
3950
<br />
@@ -59,4 +70,4 @@ export class SearchSubagentPrompt extends PromptElement<SearchSubagentPromptProp
5970
</>
6071
);
6172
}
62-
}
73+
}

extensions/copilot/src/extension/tools/node/searchSubagentTool.ts

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,24 @@ export interface ISearchSubagentParams {
3232
description: string;
3333
/** Detailed instructions regarding the search subagent's objective */
3434
details: string;
35+
/**
36+
* Optional thoroughness level that controls how many tool-call turns the subagent is allowed.
37+
* - 'quick' → base limit × 0.5 (fast, surface-level scan)
38+
* - 'medium' → base limit × 1 (default, balanced)
39+
* - 'thorough'→ base limit × 2 (deep, exhaustive search)
40+
* Only active when config.github.copilot.chat.searchSubagent.thoroughnessEnabled is true.
41+
*/
42+
thoroughness?: 'quick' | 'medium' | 'thorough';
43+
}
44+
45+
const THOROUGHNESS_MULTIPLIERS: Record<NonNullable<ISearchSubagentParams['thoroughness']>, number> = {
46+
quick: 0.5,
47+
medium: 1,
48+
thorough: 2,
49+
};
50+
51+
function computeToolCallLimitForThoroughness(baseLimit: number, thoroughness: NonNullable<ISearchSubagentParams['thoroughness']>): number {
52+
return Math.max(1, Math.round(baseLimit * THOROUGHNESS_MULTIPLIERS[thoroughness]));
3553
}
3654

3755
class SearchSubagentTool implements ICopilotTool<ISearchSubagentParams> {
@@ -46,6 +64,30 @@ class SearchSubagentTool implements ICopilotTool<ISearchSubagentParams> {
4664
@IConfigurationService private readonly configurationService: IConfigurationService,
4765
@IExperimentationService private readonly experimentationService: IExperimentationService
4866
) { }
67+
68+
alternativeDefinition(tool: vscode.LanguageModelToolInformation): vscode.LanguageModelToolInformation {
69+
const thoroughnessEnabled = this.configurationService.getExperimentBasedConfig(ConfigKey.Advanced.SearchSubagentThoroughnessEnabled, this.experimentationService);
70+
if (!thoroughnessEnabled) {
71+
return tool;
72+
}
73+
74+
return {
75+
...tool,
76+
description: tool.description
77+
+ '\n- thoroughness (optional): Search thoroughness — \'quick\' (fewer turns, fast scan), \'medium\' (default), or \'thorough\' (more turns, exhaustive).',
78+
inputSchema: {
79+
...tool.inputSchema as Record<string, unknown>,
80+
properties: {
81+
...(tool.inputSchema as { properties: Record<string, unknown> }).properties,
82+
thoroughness: {
83+
type: 'string',
84+
enum: ['quick', 'medium', 'thorough'],
85+
description: 'Controls the search thoroughness and turn limit. \'quick\' uses fewer turns (fast scan), \'medium\' is the default, \'thorough\' uses more turns for an exhaustive search.',
86+
},
87+
},
88+
},
89+
};
90+
}
4991
async invoke(options: vscode.LanguageModelToolInvocationOptions<ISearchSubagentParams>, token: vscode.CancellationToken) {
5092
// Get the current working directory from workspace folders
5193
const workspaceFolders = this.workspaceService.getWorkspaceFolders();
@@ -69,15 +111,21 @@ class SearchSubagentTool implements ICopilotTool<ISearchSubagentParams> {
69111
const subAgentInvocationId = generateUuid();
70112

71113
const toolCallLimit = this.configurationService.getExperimentBasedConfig(ConfigKey.Advanced.SearchSubagentToolCallLimit, this.experimentationService);
114+
const thoroughnessEnabled = this.configurationService.getExperimentBasedConfig(ConfigKey.Advanced.SearchSubagentThoroughnessEnabled, this.experimentationService);
115+
116+
const effectiveToolCallLimit = thoroughnessEnabled && options.input.thoroughness
117+
? computeToolCallLimitForThoroughness(toolCallLimit, options.input.thoroughness)
118+
: toolCallLimit;
72119

73120
const loop = this.instantiationService.createInstance(SearchSubagentToolCallingLoop, {
74-
toolCallLimit,
121+
toolCallLimit: effectiveToolCallLimit,
75122
conversation: new Conversation(parentSessionId, [new Turn(generateUuid(), { type: 'user', message: searchInstruction })]),
76123
request: request,
77124
location: request.location,
78125
promptText: options.input.query,
79126
subAgentInvocationId: subAgentInvocationId,
80127
parentToolCallId: options.chatStreamToolCallId,
128+
thoroughness: thoroughnessEnabled ? options.input.thoroughness : undefined,
81129
});
82130

83131
const stream = this._inputContext?.stream && ChatResponseStreamImpl.filter(

extensions/copilot/src/platform/configuration/common/configurationService.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,8 @@ export namespace ConfigKey {
652652
export const SearchSubagentModel = defineSetting<string>('chat.searchSubagent.model', ConfigType.ExperimentBased, '');
653653
/** Maximum number of tool calls the search subagent can make */
654654
export const SearchSubagentToolCallLimit = defineSetting<number>('chat.searchSubagent.toolCallLimit', ConfigType.ExperimentBased, 4);
655+
/** Enable the thoroughness parameter on the search subagent tool, which adjusts turn limits based on requested thoroughness */
656+
export const SearchSubagentThoroughnessEnabled = defineSetting<boolean>('chat.searchSubagent.thoroughnessEnabled', ConfigType.ExperimentBased, false);
655657

656658
export const ExecutionSubagentToolEnabled = defineSetting<boolean>('chat.executionSubagent.enabled', ConfigType.ExperimentBased, false);
657659
/** Model to use for the execution subagent */

0 commit comments

Comments
 (0)