fix(coding-agent): use tool-name allowlists and remove cwd-bound singletons

- treat tools as a global allowlist across built-in, extension, and SDK tools
- remove process-cwd singleton tool usage from SDK and CLI paths
- add regression coverage for extension tool filtering

closes #3452
closes #2835
This commit is contained in:
Mario Zechner
2026-04-20 21:53:07 +02:00
parent ed89480f20
commit 27c1544839
16 changed files with 295 additions and 199 deletions

View File

@@ -5,8 +5,6 @@ export {
type BashToolDetails,
type BashToolInput,
type BashToolOptions,
bashTool,
bashToolDefinition,
createBashTool,
createBashToolDefinition,
createLocalBashOperations,
@@ -18,8 +16,6 @@ export {
type EditToolDetails,
type EditToolInput,
type EditToolOptions,
editTool,
editToolDefinition,
} from "./edit.js";
export { withFileMutationQueue } from "./file-mutation-queue.js";
export {
@@ -29,8 +25,6 @@ export {
type FindToolDetails,
type FindToolInput,
type FindToolOptions,
findTool,
findToolDefinition,
} from "./find.js";
export {
createGrepTool,
@@ -39,8 +33,6 @@ export {
type GrepToolDetails,
type GrepToolInput,
type GrepToolOptions,
grepTool,
grepToolDefinition,
} from "./grep.js";
export {
createLsTool,
@@ -49,8 +41,6 @@ export {
type LsToolDetails,
type LsToolInput,
type LsToolOptions,
lsTool,
lsToolDefinition,
} from "./ls.js";
export {
createReadTool,
@@ -59,8 +49,6 @@ export {
type ReadToolDetails,
type ReadToolInput,
type ReadToolOptions,
readTool,
readToolDefinition,
} from "./read.js";
export {
DEFAULT_MAX_BYTES,
@@ -78,80 +66,90 @@ export {
type WriteOperations,
type WriteToolInput,
type WriteToolOptions,
writeTool,
writeToolDefinition,
} from "./write.js";
import type { AgentTool } from "@mariozechner/pi-agent-core";
import type { ToolDefinition } from "../extensions/types.js";
import {
type BashToolOptions,
bashTool,
bashToolDefinition,
createBashTool,
createBashToolDefinition,
} from "./bash.js";
import { createEditTool, createEditToolDefinition, editTool, editToolDefinition } from "./edit.js";
import { createFindTool, createFindToolDefinition, findTool, findToolDefinition } from "./find.js";
import { createGrepTool, createGrepToolDefinition, grepTool, grepToolDefinition } from "./grep.js";
import { createLsTool, createLsToolDefinition, lsTool, lsToolDefinition } from "./ls.js";
import {
createReadTool,
createReadToolDefinition,
type ReadToolOptions,
readTool,
readToolDefinition,
} from "./read.js";
import { createWriteTool, createWriteToolDefinition, writeTool, writeToolDefinition } from "./write.js";
import { type BashToolOptions, createBashTool, createBashToolDefinition } from "./bash.js";
import { createEditTool, createEditToolDefinition, type EditToolOptions } from "./edit.js";
import { createFindTool, createFindToolDefinition, type FindToolOptions } from "./find.js";
import { createGrepTool, createGrepToolDefinition, type GrepToolOptions } from "./grep.js";
import { createLsTool, createLsToolDefinition, type LsToolOptions } from "./ls.js";
import { createReadTool, createReadToolDefinition, type ReadToolOptions } from "./read.js";
import { createWriteTool, createWriteToolDefinition, type WriteToolOptions } from "./write.js";
export type Tool = AgentTool<any>;
export type ToolDef = ToolDefinition<any, any>;
export const codingTools: Tool[] = [readTool, bashTool, editTool, writeTool];
export const readOnlyTools: Tool[] = [readTool, grepTool, findTool, lsTool];
export const allTools = {
read: readTool,
bash: bashTool,
edit: editTool,
write: writeTool,
grep: grepTool,
find: findTool,
ls: lsTool,
};
export const allToolDefinitions = {
read: readToolDefinition,
bash: bashToolDefinition,
edit: editToolDefinition,
write: writeToolDefinition,
grep: grepToolDefinition,
find: findToolDefinition,
ls: lsToolDefinition,
};
export type ToolName = keyof typeof allTools;
export type ToolName = "read" | "bash" | "edit" | "write" | "grep" | "find" | "ls";
export const allToolNames: Set<ToolName> = new Set(["read", "bash", "edit", "write", "grep", "find", "ls"]);
export interface ToolsOptions {
read?: ReadToolOptions;
bash?: BashToolOptions;
write?: WriteToolOptions;
edit?: EditToolOptions;
grep?: GrepToolOptions;
find?: FindToolOptions;
ls?: LsToolOptions;
}
export function createToolDefinition(toolName: ToolName, cwd: string, options?: ToolsOptions): ToolDef {
switch (toolName) {
case "read":
return createReadToolDefinition(cwd, options?.read);
case "bash":
return createBashToolDefinition(cwd, options?.bash);
case "edit":
return createEditToolDefinition(cwd, options?.edit);
case "write":
return createWriteToolDefinition(cwd, options?.write);
case "grep":
return createGrepToolDefinition(cwd, options?.grep);
case "find":
return createFindToolDefinition(cwd, options?.find);
case "ls":
return createLsToolDefinition(cwd, options?.ls);
default:
throw new Error(`Unknown tool name: ${toolName}`);
}
}
export function createTool(toolName: ToolName, cwd: string, options?: ToolsOptions): Tool {
switch (toolName) {
case "read":
return createReadTool(cwd, options?.read);
case "bash":
return createBashTool(cwd, options?.bash);
case "edit":
return createEditTool(cwd, options?.edit);
case "write":
return createWriteTool(cwd, options?.write);
case "grep":
return createGrepTool(cwd, options?.grep);
case "find":
return createFindTool(cwd, options?.find);
case "ls":
return createLsTool(cwd, options?.ls);
default:
throw new Error(`Unknown tool name: ${toolName}`);
}
}
export function createCodingToolDefinitions(cwd: string, options?: ToolsOptions): ToolDef[] {
return [
createReadToolDefinition(cwd, options?.read),
createBashToolDefinition(cwd, options?.bash),
createEditToolDefinition(cwd),
createWriteToolDefinition(cwd),
createEditToolDefinition(cwd, options?.edit),
createWriteToolDefinition(cwd, options?.write),
];
}
export function createReadOnlyToolDefinitions(cwd: string, options?: ToolsOptions): ToolDef[] {
return [
createReadToolDefinition(cwd, options?.read),
createGrepToolDefinition(cwd),
createFindToolDefinition(cwd),
createLsToolDefinition(cwd),
createGrepToolDefinition(cwd, options?.grep),
createFindToolDefinition(cwd, options?.find),
createLsToolDefinition(cwd, options?.ls),
];
}
@@ -159,11 +157,11 @@ export function createAllToolDefinitions(cwd: string, options?: ToolsOptions): R
return {
read: createReadToolDefinition(cwd, options?.read),
bash: createBashToolDefinition(cwd, options?.bash),
edit: createEditToolDefinition(cwd),
write: createWriteToolDefinition(cwd),
grep: createGrepToolDefinition(cwd),
find: createFindToolDefinition(cwd),
ls: createLsToolDefinition(cwd),
edit: createEditToolDefinition(cwd, options?.edit),
write: createWriteToolDefinition(cwd, options?.write),
grep: createGrepToolDefinition(cwd, options?.grep),
find: createFindToolDefinition(cwd, options?.find),
ls: createLsToolDefinition(cwd, options?.ls),
};
}
@@ -171,23 +169,28 @@ export function createCodingTools(cwd: string, options?: ToolsOptions): Tool[] {
return [
createReadTool(cwd, options?.read),
createBashTool(cwd, options?.bash),
createEditTool(cwd),
createWriteTool(cwd),
createEditTool(cwd, options?.edit),
createWriteTool(cwd, options?.write),
];
}
export function createReadOnlyTools(cwd: string, options?: ToolsOptions): Tool[] {
return [createReadTool(cwd, options?.read), createGrepTool(cwd), createFindTool(cwd), createLsTool(cwd)];
return [
createReadTool(cwd, options?.read),
createGrepTool(cwd, options?.grep),
createFindTool(cwd, options?.find),
createLsTool(cwd, options?.ls),
];
}
export function createAllTools(cwd: string, options?: ToolsOptions): Record<ToolName, Tool> {
return {
read: createReadTool(cwd, options?.read),
bash: createBashTool(cwd, options?.bash),
edit: createEditTool(cwd),
write: createWriteTool(cwd),
grep: createGrepTool(cwd),
find: createFindTool(cwd),
ls: createLsTool(cwd),
edit: createEditTool(cwd, options?.edit),
write: createWriteTool(cwd, options?.write),
grep: createGrepTool(cwd, options?.grep),
find: createFindTool(cwd, options?.find),
ls: createLsTool(cwd, options?.ls),
};
}