Compare commits

...

9 Commits

30 changed files with 1355 additions and 1187 deletions
+1 -1
View File
@@ -2440,7 +2440,7 @@ export class Config {
);
if (this.getUseWriteTodos()) {
maybeRegister(WriteTodosTool, () =>
registry.registerTool(new WriteTodosTool(this.messageBus)),
registry.registerTool(new WriteTodosTool(this, this.messageBus)),
);
}
if (this.isPlanEnabled()) {
@@ -63,6 +63,16 @@ export class PromptProvider {
const activeSnippets = isModernModel ? snippets : legacySnippets;
const contextFilenames = getAllGeminiMdFilenames();
const registry =
typeof config.getToolRegistry === 'function'
? config.getToolRegistry()
: undefined;
const allTools =
typeof registry?.getAllTools === 'function' ? registry.getAllTools() : [];
const toolInstructions = allTools
.map((t) => t.instructions)
.filter((i): i is string => !!i);
// --- Context Gathering ---
let planModeToolsList = PLAN_MODE_TOOLS.filter((t) =>
enabledToolNames.has(t),
@@ -201,6 +211,8 @@ export class PromptProvider {
: this.withSection('finalReminder', () => ({
readFileToolName: READ_FILE_TOOL_NAME,
})),
toolInstructions:
toolInstructions.length > 0 ? toolInstructions : undefined,
} as snippets.SystemPromptOptions;
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
@@ -35,6 +35,7 @@ export interface SystemPromptOptions {
interactiveYoloMode?: boolean;
gitRepo?: GitRepoOptions;
finalReminder?: FinalReminderOptions;
toolInstructions?: string[];
}
export interface PreambleOptions {
@@ -113,6 +114,8 @@ ${
: renderPrimaryWorkflows(options.primaryWorkflows)
}
${renderToolInstructions(options.toolInstructions)}
${renderOperationalGuidelines(options.operationalGuidelines)}
${renderInteractiveYoloMode(options.interactiveYoloMode)}
@@ -244,6 +247,15 @@ ${newApplicationSteps(options)}
`.trim();
}
export function renderToolInstructions(instructions?: string[]): string {
if (!instructions || instructions.length === 0) return '';
return `
# Detailed Tool Usage Reference
${instructions.join('\n\n')}
`.trim();
}
export function renderOperationalGuidelines(
options?: OperationalGuidelinesOptions,
): string {
+12
View File
@@ -35,6 +35,7 @@ export interface SystemPromptOptions {
sandbox?: SandboxMode;
interactiveYoloMode?: boolean;
gitRepo?: GitRepoOptions;
toolInstructions?: string[];
}
export interface PreambleOptions {
@@ -110,6 +111,8 @@ ${
: renderPrimaryWorkflows(options.primaryWorkflows)
}
${renderToolInstructions(options.toolInstructions)}
${renderOperationalGuidelines(options.operationalGuidelines)}
${renderInteractiveYoloMode(options.interactiveYoloMode)}
@@ -268,6 +271,15 @@ ${newApplicationSteps(options)}
`.trim();
}
export function renderToolInstructions(instructions?: string[]): string {
if (!instructions || instructions.length === 0) return '';
return `
# Detailed Tool Usage Reference
${instructions.join('\n\n')}
`.trim();
}
export function renderOperationalGuidelines(
options?: OperationalGuidelinesOptions,
): string {
@@ -1,69 +1,9 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`ShellTool > getDescription > should return the non-windows description when not on windows 1`] = `
"This tool executes a given shell command as \`bash -c <command>\`. Command can start background processes using \`&\`. Command is executed as a subprocess that leads its own process group. Command process group can be terminated as \`kill -- -PGID\` or signaled as \`kill -s SIGNAL -- -PGID\`.
exports[`ShellTool > getDescription > should return the non-windows description when not on windows 1`] = `"This tool executes a given shell command as \`bash -c <command>\`."`;
Efficiency Guidelines:
- Quiet Flags: Always prefer silent or quiet flags (e.g., \`npm install --silent\`, \`git --no-pager\`) to reduce output volume while still capturing necessary information.
- Pagination: Always disable terminal pagination to ensure commands terminate (e.g., use \`git --no-pager\`, \`systemctl --no-pager\`, or set \`PAGER=cat\`).
exports[`ShellTool > getDescription > should return the windows description when on windows 1`] = `"This tool executes a given shell command as \`powershell.exe -NoProfile -Command <command>\`."`;
The following information is returned:
exports[`ShellTool > getSchema > should return the base schema when no modelId is provided 1`] = `"This tool executes a given shell command as \`bash -c <command>\`."`;
Output: Combined stdout/stderr. Can be \`(empty)\` or partial on error and for any unwaited background processes.
Exit Code: Only included if non-zero (command failed).
Error: Only included if a process-level error occurred (e.g., spawn failure).
Signal: Only included if process was terminated by a signal.
Background PIDs: Only included if background processes were started.
Process Group PGID: Only included if available."
`;
exports[`ShellTool > getDescription > should return the windows description when on windows 1`] = `
"This tool executes a given shell command as \`powershell.exe -NoProfile -Command <command>\`. Command can start background processes using PowerShell constructs such as \`Start-Process -NoNewWindow\` or \`Start-Job\`.
Efficiency Guidelines:
- Quiet Flags: Always prefer silent or quiet flags (e.g., \`npm install --silent\`, \`git --no-pager\`) to reduce output volume while still capturing necessary information.
- Pagination: Always disable terminal pagination to ensure commands terminate (e.g., use \`git --no-pager\`, \`systemctl --no-pager\`, or set \`PAGER=cat\`).
The following information is returned:
Output: Combined stdout/stderr. Can be \`(empty)\` or partial on error and for any unwaited background processes.
Exit Code: Only included if non-zero (command failed).
Error: Only included if a process-level error occurred (e.g., spawn failure).
Signal: Only included if process was terminated by a signal.
Background PIDs: Only included if background processes were started.
Process Group PGID: Only included if available."
`;
exports[`ShellTool > getSchema > should return the base schema when no modelId is provided 1`] = `
"This tool executes a given shell command as \`bash -c <command>\`. Command can start background processes using \`&\`. Command is executed as a subprocess that leads its own process group. Command process group can be terminated as \`kill -- -PGID\` or signaled as \`kill -s SIGNAL -- -PGID\`.
Efficiency Guidelines:
- Quiet Flags: Always prefer silent or quiet flags (e.g., \`npm install --silent\`, \`git --no-pager\`) to reduce output volume while still capturing necessary information.
- Pagination: Always disable terminal pagination to ensure commands terminate (e.g., use \`git --no-pager\`, \`systemctl --no-pager\`, or set \`PAGER=cat\`).
The following information is returned:
Output: Combined stdout/stderr. Can be \`(empty)\` or partial on error and for any unwaited background processes.
Exit Code: Only included if non-zero (command failed).
Error: Only included if a process-level error occurred (e.g., spawn failure).
Signal: Only included if process was terminated by a signal.
Background PIDs: Only included if background processes were started.
Process Group PGID: Only included if available."
`;
exports[`ShellTool > getSchema > should return the schema from the resolver when modelId is provided 1`] = `
"This tool executes a given shell command as \`bash -c <command>\`. Command can start background processes using \`&\`. Command is executed as a subprocess that leads its own process group. Command process group can be terminated as \`kill -- -PGID\` or signaled as \`kill -s SIGNAL -- -PGID\`.
Efficiency Guidelines:
- Quiet Flags: Always prefer silent or quiet flags (e.g., \`npm install --silent\`, \`git --no-pager\`) to reduce output volume while still capturing necessary information.
- Pagination: Always disable terminal pagination to ensure commands terminate (e.g., use \`git --no-pager\`, \`systemctl --no-pager\`, or set \`PAGER=cat\`).
The following information is returned:
Output: Combined stdout/stderr. Can be \`(empty)\` or partial on error and for any unwaited background processes.
Exit Code: Only included if non-zero (command failed).
Error: Only included if a process-level error occurred (e.g., spawn failure).
Signal: Only included if process was terminated by a signal.
Background PIDs: Only included if background processes were started.
Process Group PGID: Only included if available."
`;
exports[`ShellTool > getSchema > should return the schema from the resolver when modelId is provided 1`] = `"This tool executes a given shell command as \`bash -c <command>\`."`;
+1 -1
View File
@@ -201,6 +201,6 @@ export class ActivateSkillTool extends BaseDeclarativeTool<
return resolveToolDeclaration(
getActivateSkillDefinition(skillNames),
modelId,
);
).declaration;
}
}
+1 -1
View File
@@ -111,7 +111,7 @@ export class AskUserTool extends BaseDeclarativeTool<
}
override getSchema(modelId?: string) {
return resolveToolDeclaration(ASK_USER_DEFINITION, modelId);
return resolveToolDeclaration(ASK_USER_DEFINITION, modelId).declaration;
}
}
File diff suppressed because it is too large Load Diff
@@ -269,8 +269,8 @@ export const EDIT_DEFINITION: ToolDefinition = {
name: EDIT_TOOL_NAME,
description: `Replaces text within a file. By default, replaces a single occurrence, but can replace multiple occurrences when \`expected_replacements\` is specified. This tool requires providing significant context around the change to ensure precise targeting. Always use the ${READ_FILE_TOOL_NAME} tool to examine the file's current content before attempting a text replacement.
The user has the ability to modify the \`new_string\` content. If modified, this will be stated in the response.
The user has the ability to modify the \`new_string\` content. If modified, this will be stated in the response.`,
instructions: `
Expectation for required parameters:
1. \`old_string\` MUST be the exact literal text to replace (including all whitespace, indentation, newlines, and surrounding code etc.).
2. \`new_string\` MUST be the exact literal text to replace \`old_string\` with (also including all whitespace, indentation, newlines, and surrounding code etc.). Ensure the resulting code is correct and idiomatic and that \`old_string\` and \`new_string\` are different.
@@ -420,12 +420,12 @@ export const LS_DEFINITION: ToolDefinition = {
// ============================================================================
/**
* Generates the platform-specific description for the shell tool.
* Generates the platform-specific description and instructions for the shell tool.
*/
export function getShellToolDescription(
enableInteractiveShell: boolean,
enableEfficiency: boolean,
): string {
): { description: string; instructions: string } {
const efficiencyGuidelines = enableEfficiency
? `
@@ -449,12 +449,18 @@ export function getShellToolDescription(
const backgroundInstructions = enableInteractiveShell
? 'To run a command in the background, set the `is_background` parameter to true. Do NOT use PowerShell background constructs.'
: 'Command can start background processes using PowerShell constructs such as `Start-Process -NoNewWindow` or `Start-Job`.';
return `This tool executes a given shell command as \`powershell.exe -NoProfile -Command <command>\`. ${backgroundInstructions}${efficiencyGuidelines}${returnedInfo}`;
return {
description: `This tool executes a given shell command as \`powershell.exe -NoProfile -Command <command>\`.`,
instructions: `${backgroundInstructions}${efficiencyGuidelines}${returnedInfo}`,
};
} else {
const backgroundInstructions = enableInteractiveShell
? 'To run a command in the background, set the `is_background` parameter to true. Do NOT use `&` to background commands.'
: 'Command can start background processes using `&`.';
return `This tool executes a given shell command as \`bash -c <command>\`. ${backgroundInstructions} Command is executed as a subprocess that leads its own process group. Command process group can be terminated as \`kill -- -PGID\` or signaled as \`kill -s SIGNAL -- -PGID\`.${efficiencyGuidelines}${returnedInfo}`;
return {
description: `This tool executes a given shell command as \`bash -c <command>\`.`,
instructions: `${backgroundInstructions} Command is executed as a subprocess that leads its own process group. Command process group can be terminated as \`kill -- -PGID\` or signaled as \`kill -s SIGNAL -- -PGID\`.${efficiencyGuidelines}${returnedInfo}`,
};
}
}
@@ -475,13 +481,15 @@ export function getShellDefinition(
enableInteractiveShell: boolean,
enableEfficiency: boolean,
): ToolDefinition {
const { description, instructions } = getShellToolDescription(
enableInteractiveShell,
enableEfficiency,
);
return {
base: {
name: SHELL_TOOL_NAME,
description: getShellToolDescription(
enableInteractiveShell,
enableEfficiency,
),
description,
instructions,
parametersJsonSchema: {
type: 'object',
properties: {
@@ -655,23 +663,6 @@ Use this tool for complex queries that require multiple steps. If you find that
DO NOT use this tool for simple tasks that can be completed in less than 2 steps. If the user query is simple and straightforward, do not use the tool. If you can respond with an answer in a single turn then this tool is not required.
## Task state definitions
- pending: Work has not begun on a given subtask.
- in_progress: Marked just prior to beginning work on a given subtask. You should only have one subtask as in_progress at a time.
- completed: Subtask was successfully completed with no errors or issues. If the subtask required more steps to complete, update the todo list with the subtasks. All steps should be identified as completed only when they are completed.
- cancelled: As you update the todo list, some tasks are not required anymore due to the dynamic nature of the task. In this case, mark the subtasks as cancelled.
## Methodology for using this tool
1. Use this todo list as soon as you receive a user request based on the complexity of the task.
2. Keep track of every subtask that you update the list with.
3. Mark a subtask as in_progress before you begin working on it. You should only have one subtask as in_progress at a time.
4. Update the subtask list as you proceed in executing the task. The subtask list is not static and should reflect your progress and current plans, which may evolve as you acquire new information.
5. Mark a subtask as completed when you have completed it.
6. Mark a subtask as cancelled if the subtask is no longer needed.
7. You must update the todo list as soon as you start, stop or cancel a subtask. Don't batch or wait to update the todo list.
## Examples of When to Use the Todo List
@@ -709,6 +700,23 @@ Agent:
The agent did not use the todo list because this task could be completed by a tight loop of execute test->edit->execute test.
</reasoning>
</example>`,
instructions: `
## Task state definitions
- pending: Work has not begun on a given subtask.
- in_progress: Marked just prior to beginning work on a given subtask. You should only have one subtask as in_progress at a time.
- completed: Subtask was successfully completed with no errors or issues. If the subtask required more steps to complete, update the todo list with the subtasks. All steps should be identified as completed only when they are completed.
- cancelled: As you update the todo list, some tasks are not required anymore due to the dynamic nature of the task. In this case, mark the subtasks as cancelled.
## Methodology for using this tool
1. Use this todo list as soon as you receive a user request based on the complexity of the task.
2. Keep track of every subtask that you update the list with.
3. Mark a subtask as in_progress before you begin working on it. You should only have one subtask as in_progress at a time.
4. Update the subtask list as you proceed in executing the task. The subtask list is not static and should reflect your progress and current plans, which may evolve as you acquire new information.
5. Mark a subtask as completed when you have completed it.
6. Mark a subtask as cancelled if the subtask is no longer needed.
7. You must update the todo list as soon as you start, stop or cancel a subtask. Don't batch or wait to update the todo list.`,
parametersJsonSchema: {
type: 'object',
properties: {
@@ -25,7 +25,7 @@ describe('resolveToolDeclaration', () => {
it('should return the base definition when no modelId is provided', () => {
const result = resolveToolDeclaration(mockDefinition);
expect(result).toEqual(mockDefinition.base);
expect(result.declaration).toEqual(mockDefinition.base);
});
it('should return overridden description when modelId matches override criteria', () => {
@@ -43,8 +43,8 @@ describe('resolveToolDeclaration', () => {
definitionWithOverride,
'special-model',
);
expect(result.description).toBe('Overridden description');
expect(result.name).toBe(mockDefinition.base.name);
expect(result.declaration.description).toBe('Overridden description');
expect(result.declaration.name).toBe(mockDefinition.base.name);
});
it('should return base definition when modelId does not match override criteria', () => {
@@ -62,11 +62,13 @@ describe('resolveToolDeclaration', () => {
definitionWithOverride,
'regular-model',
);
expect(result.description).toBe(mockDefinition.base.description);
expect(result.declaration.description).toBe(
mockDefinition.base.description,
);
});
it('should return the base definition when a modelId is provided but no overrides exist', () => {
const result = resolveToolDeclaration(mockDefinition, 'gemini-1.5-pro');
expect(result).toEqual(mockDefinition.base);
expect(result.declaration).toEqual(mockDefinition.base);
});
});
@@ -12,23 +12,38 @@ import type { ToolDefinition } from './types.js';
*
* @param definition The tool definition containing the base declaration and optional overrides.
* @param modelId Optional model identifier to apply specific overrides.
* @returns The FunctionDeclaration to be sent to the API.
* @returns An object containing the FunctionDeclaration for the API and optional instructions for the system prompt.
*/
export function resolveToolDeclaration(
definition: ToolDefinition,
modelId?: string,
): FunctionDeclaration {
): { declaration: FunctionDeclaration; instructions?: string } {
const { instructions: baseInstructions, ...baseDeclaration } =
definition.base;
if (!modelId || !definition.overrides) {
return definition.base;
return {
declaration: baseDeclaration,
instructions: baseInstructions,
};
}
const override = definition.overrides(modelId);
if (!override) {
return definition.base;
return {
declaration: baseDeclaration,
instructions: baseInstructions,
};
}
const { instructions: overrideInstructions, ...overrideDeclaration } =
override;
return {
...definition.base,
...override,
declaration: {
...baseDeclaration,
...overrideDeclaration,
},
instructions: overrideInstructions ?? baseInstructions,
};
}
+4 -2
View File
@@ -11,10 +11,12 @@ import { type FunctionDeclaration } from '@google/genai';
*/
export interface ToolDefinition {
/** The base declaration for the tool. */
base: FunctionDeclaration;
base: FunctionDeclaration & { instructions?: string };
/**
* Optional overrides for specific model families or versions.
*/
overrides?: (modelId: string) => Partial<FunctionDeclaration> | undefined;
overrides?: (
modelId: string,
) => (Partial<FunctionDeclaration> & { instructions?: string }) | undefined;
}
+14 -3
View File
@@ -910,15 +910,26 @@ export class EditTool
private readonly config: Config,
messageBus: MessageBus,
) {
const modelId =
typeof config.getActiveModel === 'function'
? config.getActiveModel()
: undefined;
const { declaration, instructions } = resolveToolDeclaration(
EDIT_DEFINITION,
modelId,
);
super(
EditTool.Name,
EDIT_DISPLAY_NAME,
EDIT_DEFINITION.base.description!,
declaration.description!,
Kind.Edit,
EDIT_DEFINITION.base.parametersJsonSchema,
declaration.parametersJsonSchema,
messageBus,
true, // isOutputMarkdown
false, // canUpdateOutput
undefined, // extensionName
undefined, // extensionId
instructions,
);
}
@@ -962,7 +973,7 @@ export class EditTool
}
override getSchema(modelId?: string) {
return resolveToolDeclaration(EDIT_DEFINITION, modelId);
return resolveToolDeclaration(EDIT_DEFINITION, modelId).declaration;
}
getModifyContext(_: AbortSignal): ModifyContext<EditToolParams> {
+2 -1
View File
@@ -57,7 +57,8 @@ export class EnterPlanModeTool extends BaseDeclarativeTool<
}
override getSchema(modelId?: string) {
return resolveToolDeclaration(ENTER_PLAN_MODE_DEFINITION, modelId);
return resolveToolDeclaration(ENTER_PLAN_MODE_DEFINITION, modelId)
.declaration;
}
}
+2 -1
View File
@@ -112,7 +112,8 @@ export class ExitPlanModeTool extends BaseDeclarativeTool<
override getSchema(modelId?: string) {
const plansDir = this.config.storage.getProjectTempPlansDir();
return resolveToolDeclaration(getExitPlanModeDefinition(plansDir), modelId);
return resolveToolDeclaration(getExitPlanModeDefinition(plansDir), modelId)
.declaration;
}
}
+2 -1
View File
@@ -183,6 +183,7 @@ export class GetInternalDocsTool extends BaseDeclarativeTool<
}
override getSchema(modelId?: string) {
return resolveToolDeclaration(GET_INTERNAL_DOCS_DEFINITION, modelId);
return resolveToolDeclaration(GET_INTERNAL_DOCS_DEFINITION, modelId)
.declaration;
}
}
+1 -1
View File
@@ -339,6 +339,6 @@ export class GlobTool extends BaseDeclarativeTool<GlobToolParams, ToolResult> {
}
override getSchema(modelId?: string) {
return resolveToolDeclaration(GLOB_DEFINITION, modelId);
return resolveToolDeclaration(GLOB_DEFINITION, modelId).declaration;
}
}
+1 -1
View File
@@ -743,6 +743,6 @@ export class GrepTool extends BaseDeclarativeTool<GrepToolParams, ToolResult> {
}
override getSchema(modelId?: string) {
return resolveToolDeclaration(GREP_DEFINITION, modelId);
return resolveToolDeclaration(GREP_DEFINITION, modelId).declaration;
}
}
+1 -1
View File
@@ -322,6 +322,6 @@ export class LSTool extends BaseDeclarativeTool<LSToolParams, ToolResult> {
}
override getSchema(modelId?: string) {
return resolveToolDeclaration(LS_DEFINITION, modelId);
return resolveToolDeclaration(LS_DEFINITION, modelId).declaration;
}
}
+1 -1
View File
@@ -313,7 +313,7 @@ export class MemoryTool
}
override getSchema(modelId?: string) {
return resolveToolDeclaration(MEMORY_DEFINITION, modelId);
return resolveToolDeclaration(MEMORY_DEFINITION, modelId).declaration;
}
getModifyContext(_abortSignal: AbortSignal): ModifyContext<SaveMemoryParams> {
+1 -1
View File
@@ -243,6 +243,6 @@ export class ReadFileTool extends BaseDeclarativeTool<
}
override getSchema(modelId?: string) {
return resolveToolDeclaration(READ_FILE_DEFINITION, modelId);
return resolveToolDeclaration(READ_FILE_DEFINITION, modelId).declaration;
}
}
+2 -1
View File
@@ -493,6 +493,7 @@ export class ReadManyFilesTool extends BaseDeclarativeTool<
}
override getSchema(modelId?: string) {
return resolveToolDeclaration(READ_MANY_FILES_DEFINITION, modelId);
return resolveToolDeclaration(READ_MANY_FILES_DEFINITION, modelId)
.declaration;
}
}
+1 -1
View File
@@ -660,6 +660,6 @@ export class RipGrepTool extends BaseDeclarativeTool<
}
override getSchema(modelId?: string) {
return resolveToolDeclaration(RIP_GREP_DEFINITION, modelId);
return resolveToolDeclaration(RIP_GREP_DEFINITION, modelId).declaration;
}
}
+14 -3
View File
@@ -466,19 +466,30 @@ export class ShellTool extends BaseDeclarativeTool<
void initializeShellParsers().catch(() => {
// Errors are surfaced when parsing commands.
});
const modelId =
typeof config.getActiveModel === 'function'
? config.getActiveModel()
: undefined;
const definition = getShellDefinition(
config.getEnableInteractiveShell(),
config.getEnableShellOutputEfficiency(),
);
const { declaration, instructions } = resolveToolDeclaration(
definition,
modelId,
);
super(
ShellTool.Name,
'Shell',
definition.base.description!,
declaration.description!,
Kind.Execute,
definition.base.parametersJsonSchema,
declaration.parametersJsonSchema,
messageBus,
false, // output is not markdown
true, // output can be updated
undefined, // extensionName
undefined, // extensionId
instructions,
);
}
@@ -519,6 +530,6 @@ export class ShellTool extends BaseDeclarativeTool<
this.config.getEnableInteractiveShell(),
this.config.getEnableShellOutputEfficiency(),
);
return resolveToolDeclaration(definition, modelId);
return resolveToolDeclaration(definition, modelId).declaration;
}
}
+1
View File
@@ -361,6 +361,7 @@ export abstract class DeclarativeTool<
readonly canUpdateOutput: boolean = false,
readonly extensionName?: string,
readonly extensionId?: string,
readonly instructions?: string,
) {}
getSchema(_modelId?: string): FunctionDeclaration {
+1 -1
View File
@@ -461,6 +461,6 @@ export class WebFetchTool extends BaseDeclarativeTool<
}
override getSchema(modelId?: string) {
return resolveToolDeclaration(WEB_FETCH_DEFINITION, modelId);
return resolveToolDeclaration(WEB_FETCH_DEFINITION, modelId).declaration;
}
}
+1 -1
View File
@@ -239,6 +239,6 @@ export class WebSearchTool extends BaseDeclarativeTool<
}
override getSchema(modelId?: string) {
return resolveToolDeclaration(WEB_SEARCH_DEFINITION, modelId);
return resolveToolDeclaration(WEB_SEARCH_DEFINITION, modelId).declaration;
}
}
+1 -1
View File
@@ -502,7 +502,7 @@ export class WriteFileTool
}
override getSchema(modelId?: string) {
return resolveToolDeclaration(WRITE_FILE_DEFINITION, modelId);
return resolveToolDeclaration(WRITE_FILE_DEFINITION, modelId).declaration;
}
getModifyContext(
+2 -1
View File
@@ -7,9 +7,10 @@
import { describe, expect, it } from 'vitest';
import { WriteTodosTool, type WriteTodosToolParams } from './write-todos.js';
import { createMockMessageBus } from '../test-utils/mock-message-bus.js';
import { makeFakeConfig } from '../test-utils/config.js';
describe('WriteTodosTool', () => {
const tool = new WriteTodosTool(createMockMessageBus());
const tool = new WriteTodosTool(makeFakeConfig(), createMockMessageBus());
const signal = new AbortController().signal;
describe('validation', () => {
+25 -4
View File
@@ -12,6 +12,7 @@ import {
type Todo,
type ToolResult,
} from './tools.js';
import type { Config } from '../config/config.js';
import type { MessageBus } from '../confirmation-bus/message-bus.js';
import { WRITE_TODOS_TOOL_NAME } from './tool-names.js';
import { WRITE_TODOS_DEFINITION } from './definitions/coreTools.js';
@@ -81,21 +82,41 @@ export class WriteTodosTool extends BaseDeclarativeTool<
> {
static readonly Name = WRITE_TODOS_TOOL_NAME;
constructor(messageBus: MessageBus) {
constructor(
private readonly config: Config,
messageBus: MessageBus,
) {
const modelId =
typeof config.getActiveModel === 'function'
? config.getActiveModel()
: undefined;
const { declaration, instructions } = resolveToolDeclaration(
WRITE_TODOS_DEFINITION,
modelId,
);
super(
WriteTodosTool.Name,
'WriteTodos',
WRITE_TODOS_DEFINITION.base.description!,
declaration.description!,
Kind.Other,
WRITE_TODOS_DEFINITION.base.parametersJsonSchema,
declaration.parametersJsonSchema,
messageBus,
true, // isOutputMarkdown
false, // canUpdateOutput
undefined, // extensionName
undefined, // extensionId
instructions,
);
}
override getSchema(modelId?: string) {
return resolveToolDeclaration(WRITE_TODOS_DEFINITION, modelId);
const activeModel =
modelId ??
(typeof this.config.getActiveModel === 'function'
? this.config.getActiveModel()
: undefined);
return resolveToolDeclaration(WRITE_TODOS_DEFINITION, activeModel)
.declaration;
}
protected override validateToolParamValues(