Skip to content

feat: System Message tools #6723

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Jul 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ff5e709
feat: onlyUseSystemMessageTools experimental setting
RomneyDa Jul 20, 2025
dde117e
feat: warning for models not great with tools
RomneyDa Jul 20, 2025
bdf9b37
chore: separate getBaseSystemMessage logic from construct messages
RomneyDa Jul 20, 2025
b708c35
feat: system message tools
RomneyDa Jul 20, 2025
a04dccb
feat: custom systemMessageDescription for built in tools
RomneyDa Jul 20, 2025
049f34b
fix: constructMessages missing param
RomneyDa Jul 20, 2025
9511eb5
fix: test failures - was doing a chat test in agent mode
RomneyDa Jul 20, 2025
4000133
chore: system message tools: cleanup and restore edit test
RomneyDa Jul 20, 2025
567d917
fix: system tool calling bugs and flesh out tests
RomneyDa Jul 20, 2025
3bd270a
fix: remove it.only test marker
RomneyDa Jul 20, 2025
7a01ceb
disallow empty text parts
RomneyDa Jul 22, 2025
3ae3161
fix: ONLY use system tools if experimental setting enabled
RomneyDa Jul 22, 2025
845c6c7
fix: update mode select to reflect new agent supported logic
RomneyDa Jul 22, 2025
954d816
merge main
RomneyDa Jul 23, 2025
74dd75a
fix edit tool by adjusting prompt to say "diffs" instead of "diff"
sestinj Jul 24, 2025
96780f5
remove comments
sestinj Jul 24, 2025
85ba04a
improve error message for failed read file tool
sestinj Jul 24, 2025
ec9d315
fix: system message tools array args for search and replace tool
RomneyDa Jul 24, 2025
d9e60cf
Merge branch 'dallin/system-message-tools' of https://github.com/cont…
RomneyDa Jul 24, 2025
3adf4b6
chore: revert duplicate fix logic for search and replace array args
RomneyDa Jul 24, 2025
04fc6c8
Merge branch 'dallin/system-message-tools' of https://github.com/cont…
sestinj Jul 24, 2025
8bc81c1
fix tool calls stopping after failure
sestinj Jul 24, 2025
0c9a238
Merge branch 'main' into dallin/system-message-tools
RomneyDa Jul 28, 2025
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
6 changes: 6 additions & 0 deletions core/config/sharedConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const sharedConfigSchema = z
useCurrentFileAsContext: z.boolean(),
optInNextEditFeature: z.boolean(),
enableExperimentalTools: z.boolean(),
onlyUseSystemMessageTools: z.boolean(),
codebaseToolCallingOnly: z.boolean(),
enableStaticContextualization: z.boolean(),

Expand Down Expand Up @@ -185,6 +186,11 @@ export function modifyAnyConfigWithSharedConfig<
configCopy.experimental.optInNextEditFeature =
sharedConfig.optInNextEditFeature;
}
if (sharedConfig.onlyUseSystemMessageTools !== undefined) {
configCopy.experimental.onlyUseSystemMessageTools =
sharedConfig.onlyUseSystemMessageTools;
}

if (sharedConfig.codebaseToolCallingOnly !== undefined) {
configCopy.experimental.codebaseToolCallingOnly =
sharedConfig.codebaseToolCallingOnly;
Expand Down
1 change: 0 additions & 1 deletion core/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1123,7 +1123,6 @@ declare global {
* This is needed to crawl a large number of documentation sites that are dynamically rendered.
*/
useChromiumForDocsCrawling?: boolean;
useTools?: boolean;
modelContextProtocolServers?: MCPOptions[];
}

Expand Down
2 changes: 2 additions & 0 deletions core/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,7 @@ export interface Tool {
faviconUrl?: string;
group: string;
originalFunctionName?: string;
systemMessageDescription?: string;
}

interface ToolChoice {
Expand Down Expand Up @@ -1516,6 +1517,7 @@ export interface ExperimentalConfig {
defaultContext?: DefaultContextProvider[];
promptPath?: string;
enableExperimentalTools?: boolean;
onlyUseSystemMessageTools?: boolean;

/**
* Quick actions are a way to add custom commands to the Code Lens of
Expand Down
20 changes: 1 addition & 19 deletions core/llm/autodetect.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import {
ChatMessage,
ModelCapability,
ModelDescription,
TemplateType,
} from "../index.js";
import { ChatMessage, ModelCapability, TemplateType } from "../index.js";

import {
anthropicTemplateMessages,
Expand Down Expand Up @@ -41,7 +36,6 @@ import {
xWinCoderEditPrompt,
zephyrEditPrompt,
} from "./templates/edit.js";
import { PROVIDER_TOOL_SUPPORT } from "./toolSupport.js";

const PROVIDER_HANDLES_TEMPLATING: string[] = [
"lmstudio",
Expand Down Expand Up @@ -107,17 +101,6 @@ const MODEL_SUPPORTS_IMAGES: string[] = [
"granite-vision",
];

function modelSupportsTools(modelDescription: ModelDescription) {
if (modelDescription.capabilities?.tools !== undefined) {
return modelDescription.capabilities.tools;
}
const providerSupport = PROVIDER_TOOL_SUPPORT[modelDescription.provider];
if (!providerSupport) {
return false;
}
return providerSupport(modelDescription.model) ?? false;
}

function modelSupportsImages(
provider: string,
model: string,
Expand Down Expand Up @@ -400,5 +383,4 @@ export {
autodetectTemplateType,
llmCanGenerateInParallel,
modelSupportsImages,
modelSupportsTools,
};
7 changes: 2 additions & 5 deletions core/llm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -531,18 +531,15 @@ export abstract class BaseLLM implements ILLM {
let contentToShow = "";
if (msg.role === "tool") {
contentToShow = msg.content;
} else if (msg.role === "assistant" && msg.toolCalls) {
} else if (msg.role === "assistant" && msg.toolCalls?.length) {
contentToShow = msg.toolCalls
?.map(
(toolCall) =>
`${toolCall.function?.name}(${toolCall.function?.arguments})`,
)
.join("\n");
} else if ("content" in msg) {
if (Array.isArray(msg.content)) {
msg.content = renderChatMessage(msg);
}
contentToShow = msg.content;
contentToShow = renderChatMessage(msg);
}

return `<${msg.role}>\n${contentToShow}\n\n`;
Expand Down
29 changes: 29 additions & 0 deletions core/llm/toolSupport.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { parseProxyModelName } from "@continuedev/config-yaml";
import { ModelDescription } from "..";

export const PROVIDER_TOOL_SUPPORT: Record<string, (model: string) => boolean> =
{
Expand Down Expand Up @@ -288,3 +289,31 @@ export const PROVIDER_TOOL_SUPPORT: Record<string, (model: string) => boolean> =
return false;
},
};

export function isRecommendedAgentModel(modelName: string): boolean {
// AND behavior
const recs: RegExp[][] = [
[/o[134]/],
[/deepseek/, /r1|reasoner/],
[/gemini/, /2\.5/, /pro/],
[/gpt/, /4/],
[/claude/, /sonnet/, /3\.5|3\.7|3-5|3-7|-4/],
[/claude/, /opus/, /-4/],
];
for (const combo of recs) {
if (combo.every((regex) => modelName.toLowerCase().match(regex))) {
return true;
}
}
return false;
}
export function modelSupportsNativeTools(modelDescription: ModelDescription) {
if (modelDescription.capabilities?.tools !== undefined) {
return modelDescription.capabilities.tools;
}
const providerSupport = PROVIDER_TOOL_SUPPORT[modelDescription.provider];
if (!providerSupport) {
return false;
}
return providerSupport(modelDescription.model) ?? false;
}
2 changes: 2 additions & 0 deletions core/tools/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const NO_TOOL_CALL_OUTPUT_MESSAGE = "No tool output";
export const CANCELLED_TOOL_CALL_MESSAGE = "The user cancelled this tool call.";
6 changes: 6 additions & 0 deletions core/tools/definitions/codebaseTool.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Tool } from "../..";
import { BUILT_IN_GROUP_NAME, BuiltInToolNames } from "../builtIn";
import { createSystemMessageExampleCall } from "../systemMessageTools/buildToolsSystemMessage";

export const codebaseTool: Tool = {
type: "function",
Expand All @@ -26,4 +27,9 @@ export const codebaseTool: Tool = {
},
},
},
systemMessageDescription: createSystemMessageExampleCall(
BuiltInToolNames.CodebaseTool,
`To search the codebase, use the ${BuiltInToolNames.CodebaseTool} tool with a natural language query. For example, to find authentication logic, you might respond with:`,
[["query", "How is user authentication handled in this codebase?"]],
),
};
9 changes: 9 additions & 0 deletions core/tools/definitions/createNewFile.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Tool } from "../..";
import { BUILT_IN_GROUP_NAME, BuiltInToolNames } from "../builtIn";
import { createSystemMessageExampleCall } from "../systemMessageTools/buildToolsSystemMessage";

export const createNewFileTool: Tool = {
type: "function",
Expand Down Expand Up @@ -30,4 +31,12 @@ export const createNewFileTool: Tool = {
},
},
},
systemMessageDescription: createSystemMessageExampleCall(
BuiltInToolNames.CreateNewFile,
`To create a NEW file, use the ${BuiltInToolNames.CreateNewFile} tool with the relative filepath and new contents. For example, to create a file located at 'path/to/file.txt', you would respond with:`,
[
["filepath", "path/to/the_file.txt"],
["contents", "Contents of the file"],
],
),
};
58 changes: 46 additions & 12 deletions core/tools/definitions/createRuleBlock.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
import { Tool } from "../..";
import { BUILT_IN_GROUP_NAME, BuiltInToolNames } from "../builtIn";
import { createSystemMessageExampleCall } from "../systemMessageTools/buildToolsSystemMessage";

const NAME_ARG_DESC =
"Short, descriptive name summarizing the rule's purpose (e.g. 'React Standards', 'Type Hints')";
const RULE_ARG_DESC =
"Clear, imperative instruction for future code generation (e.g. 'Use named exports', 'Add Python type hints'). Each rule should focus on one specific standard.";
const DESC_ARG_DESC =
"Description of when this rule should be applied. Required for Agent Requested rules (AI decides when to apply). Optional for other types.";
const GLOB_ARG_DESC =
"Optional file patterns to which this rule applies (e.g. ['**/*.{ts,tsx}'] or ['src/**/*.ts', 'tests/**/*.ts'])";
const REGEX_ARG_DESC =
"Optional regex patterns to match against file content. Rule applies only to files whose content matches the pattern (e.g. 'useEffect' for React hooks or '\\bclass\\b' for class definitions)";

const ALWAYS_APPLY_DESC =
"Whether this rule should always be applied. Set to false for Agent Requested and Manual rules. Omit or set to true for Always and Auto Attached rules.";

export const createRuleBlock: Tool = {
type: "function",
Expand All @@ -20,35 +35,54 @@ export const createRuleBlock: Tool = {
properties: {
name: {
type: "string",
description:
"Short, descriptive name summarizing the rule's purpose (e.g. 'React Standards', 'Type Hints')",
description: NAME_ARG_DESC,
},
rule: {
type: "string",
description:
"Clear, imperative instruction for future code generation (e.g. 'Use named exports', 'Add Python type hints'). Each rule should focus on one specific standard.",
description: RULE_ARG_DESC,
},
description: {
type: "string",
description:
"Description of when this rule should be applied. Required for Agent Requested rules (AI decides when to apply). Optional for other types.",
description: DESC_ARG_DESC,
},
globs: {
type: "string",
description:
"Optional file patterns to which this rule applies (e.g. ['**/*.{ts,tsx}'] or ['src/**/*.ts', 'tests/**/*.ts'])",
description: GLOB_ARG_DESC,
},
regex: {
type: "string",
description:
"Optional regex patterns to match against file content. Rule applies only to files whose content matches the pattern (e.g. 'useEffect' for React hooks or '\\bclass\\b' for class definitions)",
description: REGEX_ARG_DESC,
},
alwaysApply: {
type: "boolean",
description:
"Whether this rule should always be applied. Set to false for Agent Requested and Manual rules. Omit or set to true for Always and Auto Attached rules.",
description: ALWAYS_APPLY_DESC,
},
},
},
},
systemMessageDescription: createSystemMessageExampleCall(
BuiltInToolNames.CreateRuleBlock,
`Sometimes the user will provide feedback or guidance on your output. If you were not aware of these "rules", consider using the ${BuiltInToolNames.CreateRuleBlock} tool to persist the rule for future interactions.
This tool cannot be used to edit existing rules, but you can search in the ".continue/rules" folder and use the edit tool to manage rules.
To create a rule, respond with a ${BuiltInToolNames.CreateRuleBlock} tool call and the following arguments:
- name: ${NAME_ARG_DESC}
- rule: ${RULE_ARG_DESC}
- description: ${DESC_ARG_DESC}
- globs: ${GLOB_ARG_DESC}
- alwaysApply: ${ALWAYS_APPLY_DESC}
For example:`,
[
["name", "Use PropTypes"],
[
"rule",
"Always use PropTypes when declaring React component properties",
],
[
"description",
"Ensure that all prop types are explicitly declared for better type safety and code maintainability in React components.",
],
["globs", "**/*.js"],
["alwaysApply", "false"],
],
),
};
22 changes: 20 additions & 2 deletions core/tools/definitions/editFile.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { Tool } from "../..";
import { EDIT_CODE_INSTRUCTIONS } from "../../llm/defaultSystemMessages";
import { BUILT_IN_GROUP_NAME, BuiltInToolNames } from "../builtIn";
import { createSystemMessageExampleCall } from "../systemMessageTools/buildToolsSystemMessage";
import { NO_PARALLEL_TOOL_CALLING_INSRUCTION } from "./searchAndReplaceInFile";

export interface EditToolArgs {
filepath: string;
changes: string;
}

const CHANGES_DESCRIPTION =
"Any modifications to the file, showing only needed changes. Do NOT wrap this in a codeblock or write anything besides the code changes. In larger files, use brief language-appropriate placeholders for large unmodified sections, e.g. '// ... existing code ...'";

export const editFileTool: Tool = {
type: "function",
displayTitle: "Edit File",
Expand All @@ -31,10 +35,24 @@ export const editFileTool: Tool = {
},
changes: {
type: "string",
description:
"Any modifications to the file, showing only needed changes. Do NOT wrap this in a codeblock or write anything besides the code changes. In larger files, use brief language-appropriate placeholders for large unmodified sections, e.g. '// ... existing code ...'",
description: CHANGES_DESCRIPTION,
},
},
},
},
systemMessageDescription: createSystemMessageExampleCall(
BuiltInToolNames.EditExistingFile,
`To edit an EXISTING file, use the ${BuiltInToolNames.EditExistingFile} tool with
- filepath: the relative filepath to the file.
- changes: ${CHANGES_DESCRIPTION}
Only use this tool if you already know the contents of the file. Otherwise, use the ${BuiltInToolNames.ReadFile} or ${BuiltInToolNames.ReadCurrentlyOpenFile} tool to read it first.
For example:`,
[
["filepath", "path/to/the_file.ts"],
[
"changes",
"// ... existing code ...\nfunction subtract(a: number, b: number): number {\n return a - b;\n}\n// ... rest of code ...",
],
],
),
};
6 changes: 6 additions & 0 deletions core/tools/definitions/fetchUrlContent.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Tool } from "../..";
import { BUILT_IN_GROUP_NAME, BuiltInToolNames } from "../builtIn";
import { createSystemMessageExampleCall } from "../systemMessageTools/buildToolsSystemMessage";

export const fetchUrlContentTool: Tool = {
type: "function",
Expand All @@ -25,4 +26,9 @@ export const fetchUrlContentTool: Tool = {
},
},
},
systemMessageDescription: createSystemMessageExampleCall(
BuiltInToolNames.FetchUrlContent,
`To fetch the content of a URL, use the ${BuiltInToolNames.FetchUrlContent} tool. For example, to read the contents of a webpage, you might respond with:`,
[["url", "https://example.com"]],
),
};
6 changes: 6 additions & 0 deletions core/tools/definitions/globSearch.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Tool } from "../..";
import { BUILT_IN_GROUP_NAME, BuiltInToolNames } from "../builtIn";
import { createSystemMessageExampleCall } from "../systemMessageTools/buildToolsSystemMessage";

export const globSearchTool: Tool = {
type: "function",
Expand All @@ -25,4 +26,9 @@ export const globSearchTool: Tool = {
},
},
},
systemMessageDescription: createSystemMessageExampleCall(
BuiltInToolNames.FileGlobSearch,
`To return a list of files based on a glob search pattern, use the ${BuiltInToolNames.FileGlobSearch} tool`,
[["pattern", "*.py"]],
),
};
6 changes: 6 additions & 0 deletions core/tools/definitions/grepSearch.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Tool } from "../..";
import { BUILT_IN_GROUP_NAME, BuiltInToolNames } from "../builtIn";
import { createSystemMessageExampleCall } from "../systemMessageTools/buildToolsSystemMessage";

export const grepSearchTool: Tool = {
type: "function",
Expand All @@ -26,4 +27,9 @@ export const grepSearchTool: Tool = {
},
},
},
systemMessageDescription: createSystemMessageExampleCall(
BuiltInToolNames.GrepSearch,
`To perform a grep search within the project, call the ${BuiltInToolNames.GrepSearch} tool with the query pattern to match. For example:`,
[["query", ".*main_services.*"]],
),
};
9 changes: 9 additions & 0 deletions core/tools/definitions/ls.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Tool } from "../..";

import { BUILT_IN_GROUP_NAME, BuiltInToolNames } from "../builtIn";
import { createSystemMessageExampleCall } from "../systemMessageTools/buildToolsSystemMessage";

export const lsTool: Tool = {
type: "function",
Expand Down Expand Up @@ -30,4 +31,12 @@ export const lsTool: Tool = {
},
},
},
systemMessageDescription: createSystemMessageExampleCall(
BuiltInToolNames.LSTool,
`To list files and folders in a given directory, call the ${BuiltInToolNames.LSTool} tool with "dirPath" and "recursive". For example:`,
[
["dirPath", "path/to/dir"],
["recursive", "false"],
],
),
};
Loading
Loading