Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 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
12 changes: 8 additions & 4 deletions core/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1091,7 +1091,7 @@ export class Core {

on(
"tools/evaluatePolicy",
async ({ data: { toolName, basePolicy, args } }) => {
async ({ data: { toolName, basePolicy, parsedArgs, processedArgs } }) => {
const { config } = await this.configHandler.loadConfig();
if (!config) {
throw new Error("Config not loaded");
Expand All @@ -1104,12 +1104,16 @@ export class Core {

// Extract display value for specific tools
let displayValue: string | undefined;
if (toolName === "runTerminalCommand" && args.command) {
displayValue = args.command as string;
if (toolName === "runTerminalCommand" && parsedArgs.command) {
displayValue = parsedArgs.command as string;
}

if (tool.evaluateToolCallPolicy) {
const evaluatedPolicy = tool.evaluateToolCallPolicy(basePolicy, args);
const evaluatedPolicy = tool.evaluateToolCallPolicy(
basePolicy,
parsedArgs,
processedArgs,
);
return { policy: evaluatedPolicy, displayValue };
}
return { policy: basePolicy, displayValue };
Expand Down
1 change: 1 addition & 0 deletions core/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1110,6 +1110,7 @@ export interface Tool {
evaluateToolCallPolicy?: (
basePolicy: ToolPolicy,
parsedArgs: Record<string, unknown>,
processedArgs?: Record<string, unknown>,
) => ToolPolicy;
}

Expand Down
20 changes: 20 additions & 0 deletions core/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
"system-ca": "^1.0.3",
"tar": "^7.4.3",
"tree-sitter-wasms": "^0.1.11",
"untildify": "^6.0.0",
"uuid": "^9.0.1",
"vectordb": "^0.4.20",
"web-tree-sitter": "^0.21.0",
Expand Down
7 changes: 6 additions & 1 deletion core/protocol/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,12 @@ export type ToCoreFromIdeOrWebviewProtocol = {
},
];
"tools/evaluatePolicy": [
{ toolName: string; basePolicy: ToolPolicy; args: Record<string, unknown> },
{
toolName: string;
basePolicy: ToolPolicy;
parsedArgs: Record<string, unknown>;
processedArgs?: Record<string, unknown>;
},
{ policy: ToolPolicy; displayValue?: string },
];
"tools/preprocessArgs": [
Expand Down
27 changes: 26 additions & 1 deletion core/tools/definitions/createNewFile.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { ToolPolicy } from "@continuedev/terminal-security";
import { Tool } from "../..";
import { ResolvedPath, resolveInputPath } from "../../util/pathResolver";
import { BUILT_IN_GROUP_NAME, BuiltInToolNames } from "../builtIn";
import { evaluateFileAccessPolicy } from "../policies/fileAccess";

export const createNewFileTool: Tool = {
type: "function",
Expand All @@ -21,7 +24,7 @@ export const createNewFileTool: Tool = {
filepath: {
type: "string",
description:
"The path where the new file should be created, relative to the root of the workspace",
"The path where the new file should be created. Can be a relative path (from workspace root), absolute path, tilde path (~/...), or file:// URI.",
},
contents: {
type: "string",
Expand All @@ -38,4 +41,26 @@ export const createNewFileTool: Tool = {
["contents", "Contents of the file"],
],
},
preprocessArgs: async (args, { ide }) => {
const filepath = args.filepath as string;
const resolvedPath = await resolveInputPath(ide, filepath);

// Store the resolved path info in args for policy evaluation
return {
resolvedPath,
};
},
evaluateToolCallPolicy: (
basePolicy: ToolPolicy,
_: Record<string, unknown>,
processedArgs?: Record<string, unknown>,
): ToolPolicy => {
const resolvedPath = processedArgs?.resolvedPath as
| ResolvedPath
| null
| undefined;
if (!resolvedPath) return basePolicy;

return evaluateFileAccessPolicy(basePolicy, resolvedPath.isWithinWorkspace);
},
};
29 changes: 28 additions & 1 deletion core/tools/definitions/ls.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { Tool } from "../..";

import { ToolPolicy } from "@continuedev/terminal-security";
import { ResolvedPath, resolveInputPath } from "../../util/pathResolver";
import { BUILT_IN_GROUP_NAME, BuiltInToolNames } from "../builtIn";
import { evaluateFileAccessPolicy } from "../policies/fileAccess";

export const lsTool: Tool = {
type: "function",
Expand All @@ -20,7 +23,7 @@ export const lsTool: Tool = {
dirPath: {
type: "string",
description:
"The directory path relative to the root of the project. Use forward slash paths like '/'. rather than e.g. '.'",
"The directory path. Can be relative to project root, absolute path, tilde path (~/...), or file:// URI. Use forward slash paths",
},
recursive: {
type: "boolean",
Expand All @@ -39,4 +42,28 @@ export const lsTool: Tool = {
],
},
toolCallIcon: "FolderIcon",
preprocessArgs: async (args, { ide }) => {
const dirPath = args.dirPath as string;

// Default to current directory if no path provided
const pathToResolve = dirPath || ".";
const resolvedPath = await resolveInputPath(ide, pathToResolve);

return {
resolvedPath,
};
},
evaluateToolCallPolicy: (
basePolicy: ToolPolicy,
_: Record<string, unknown>,
processedArgs?: Record<string, unknown>,
): ToolPolicy => {
const resolvedPath = processedArgs?.resolvedPath as
| ResolvedPath
| null
| undefined;
if (!resolvedPath) return basePolicy;

return evaluateFileAccessPolicy(basePolicy, resolvedPath.isWithinWorkspace);
},
};
27 changes: 26 additions & 1 deletion core/tools/definitions/readFile.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { ToolPolicy } from "@continuedev/terminal-security";
import { Tool } from "../..";
import { ResolvedPath, resolveInputPath } from "../../util/pathResolver";
import { BUILT_IN_GROUP_NAME, BuiltInToolNames } from "../builtIn";
import { evaluateFileAccessPolicy } from "../policies/fileAccess";

export const readFileTool: Tool = {
type: "function",
Expand All @@ -21,7 +24,7 @@ export const readFileTool: Tool = {
filepath: {
type: "string",
description:
"The path of the file to read, relative to the root of the workspace (NOT uri or absolute path)",
"The path of the file to read. Can be a relative path (from workspace root), absolute path, tilde path (~/...), or file:// URI",
},
},
},
Expand All @@ -32,4 +35,26 @@ export const readFileTool: Tool = {
},
defaultToolPolicy: "allowedWithoutPermission",
toolCallIcon: "DocumentIcon",
preprocessArgs: async (args, { ide }) => {
const filepath = args.filepath as string;
const resolvedPath = await resolveInputPath(ide, filepath);

// Store the resolved path info in args for policy evaluation
return {
resolvedPath,
};
},
evaluateToolCallPolicy: (
basePolicy: ToolPolicy,
_: Record<string, unknown>,
processedArgs?: Record<string, unknown>,
): ToolPolicy => {
const resolvedPath = processedArgs?.resolvedPath as
| ResolvedPath
| null
| undefined;
if (!resolvedPath) return basePolicy;

return evaluateFileAccessPolicy(basePolicy, resolvedPath.isWithinWorkspace);
},
};
24 changes: 24 additions & 0 deletions core/tools/definitions/readFileRange.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { ToolPolicy } from "@continuedev/terminal-security";
import { Tool } from "../..";
import { ResolvedPath, resolveInputPath } from "../../util/pathResolver";
import { BUILT_IN_GROUP_NAME, BuiltInToolNames } from "../builtIn";
import { evaluateFileAccessPolicy } from "../policies/fileAccess";

export const readFileRangeTool: Tool = {
type: "function",
Expand Down Expand Up @@ -49,4 +52,25 @@ export const readFileRangeTool: Tool = {
},
defaultToolPolicy: "allowedWithoutPermission",
toolCallIcon: "DocumentIcon",
preprocessArgs: async (args, { ide }) => {
const filepath = args.filepath as string;
const resolvedPath = await resolveInputPath(ide, filepath);

return {
resolvedPath,
};
},
evaluateToolCallPolicy: (
basePolicy: ToolPolicy,
_: Record<string, unknown>,
processedArgs?: Record<string, unknown>,
): ToolPolicy => {
const resolvedPath = processedArgs?.resolvedPath as
| ResolvedPath
| null
| undefined;
if (!resolvedPath) return basePolicy;

return evaluateFileAccessPolicy(basePolicy, resolvedPath.isWithinWorkspace);
},
};
24 changes: 24 additions & 0 deletions core/tools/definitions/viewSubdirectory.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { ToolPolicy } from "@continuedev/terminal-security";
import { Tool } from "../..";
import { ResolvedPath, resolveInputPath } from "../../util/pathResolver";
import { BUILT_IN_GROUP_NAME, BuiltInToolNames } from "../builtIn";
import { evaluateFileAccessPolicy } from "../policies/fileAccess";

export const viewSubdirectoryTool: Tool = {
type: "function",
Expand Down Expand Up @@ -31,4 +34,25 @@ export const viewSubdirectoryTool: Tool = {
},
defaultToolPolicy: "allowedWithPermission",
toolCallIcon: "FolderOpenIcon",
preprocessArgs: async (args, { ide }) => {
const directoryPath = args.directory_path as string;
const resolvedPath = await resolveInputPath(ide, directoryPath);

return {
resolvedPath,
};
},
evaluateToolCallPolicy: (
basePolicy: ToolPolicy,
_: Record<string, unknown>,
processedArgs?: Record<string, unknown>,
): ToolPolicy => {
const resolvedPath = processedArgs?.resolvedPath as
| ResolvedPath
| null
| undefined;
if (!resolvedPath) return basePolicy;

return evaluateFileAccessPolicy(basePolicy, resolvedPath.isWithinWorkspace);
},
};
17 changes: 9 additions & 8 deletions core/tools/implementations/lsTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import ignore from "ignore";

import { ToolImpl } from ".";
import { walkDir } from "../../indexing/walkDir";
import { resolveRelativePathInDir } from "../../util/ideUtils";
import { resolveInputPath } from "../../util/pathResolver";
import { ContinueError, ContinueErrorReason } from "../../util/errors";

export function resolveLsToolDirPath(dirPath: string | undefined) {
if (!dirPath || dirPath === ".") {
return "/";
}
if (dirPath.startsWith(".")) {
// Don't strip leading slash from absolute paths - let the resolver handle it
if (dirPath.startsWith(".") && !dirPath.startsWith("./")) {
return dirPath.slice(1);
}
return dirPath.replace(/\\/g, "/");
Expand All @@ -19,15 +20,15 @@ const MAX_LS_TOOL_LINES = 200;

export const lsToolImpl: ToolImpl = async (args, extras) => {
const dirPath = resolveLsToolDirPath(args?.dirPath);
const uri = await resolveRelativePathInDir(dirPath, extras.ide);
if (!uri) {
const resolvedPath = await resolveInputPath(extras.ide, dirPath);
if (!resolvedPath) {
throw new ContinueError(
ContinueErrorReason.DirectoryNotFound,
`Directory ${args.dirPath} not found. Make sure to use forward-slash paths`,
`Directory ${args.dirPath} not found or is not accessible. You can use absolute paths, relative paths, or paths starting with ~`,
);
}

const entries = await walkDir(uri, extras.ide, {
const entries = await walkDir(resolvedPath.uri, extras.ide, {
returnRelativeUrisPaths: true,
include: "both",
recursive: args?.recursive ?? false,
Expand All @@ -39,12 +40,12 @@ export const lsToolImpl: ToolImpl = async (args, extras) => {
let content =
lines.length > 0
? lines.join("\n")
: `No files/folders found in ${dirPath}`;
: `No files/folders found in ${resolvedPath.displayPath}`;

const contextItems = [
{
name: "File/folder list",
description: `Files/folders in ${dirPath}`,
description: `Files/folders in ${resolvedPath.displayPath}`,
content,
},
];
Expand Down
2 changes: 1 addition & 1 deletion core/tools/implementations/lsTool.vitest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ test("resolveLsToolDirPath handles dot", () => {
});

test("resolveLsToolDirPath handles dot relative", () => {
expect(resolveLsToolDirPath("./hi")).toBe("/hi");
expect(resolveLsToolDirPath("./hi")).toBe("./hi");
});

test("resolveLsToolDirPath normalizes backslashes to forward slashes", () => {
Expand Down
Loading
Loading