diff --git a/packages/codemode/src/tool-runtime.ts b/packages/codemode/src/tool-runtime.ts index 5c39703bee0b..f6fedee00294 100644 --- a/packages/codemode/src/tool-runtime.ts +++ b/packages/codemode/src/tool-runtime.ts @@ -409,7 +409,13 @@ export const make = ( keys: (path) => namespaceKeys(root, path), search: (args) => Effect.suspend(() => executeTool("search", searchTool, args)), execute: (path, args) => - Effect.suspend(() => executeTool(canonicalSegments(path).join("."), resolve(root, path), args)), + Effect.suspend(() => { + const segments = canonicalSegments(path) + // Models often write `tools.search(...)` for the bare `search(...)`; honor it unless a tool owns that path. + if (segments.length === 1 && segments[0] === "search" && lookup(root, segments) === undefined) + return executeTool("search", searchTool, args) + return executeTool(segments.join("."), resolve(root, path), args) + }), } } diff --git a/packages/codemode/test/tool-paths.test.ts b/packages/codemode/test/tool-paths.test.ts index 3e384f4e0389..63e1e2bed99f 100644 --- a/packages/codemode/test/tool-paths.test.ts +++ b/packages/codemode/test/tool-paths.test.ts @@ -337,3 +337,17 @@ describe("tool arguments cross in a useful form where JSON.stringify would give expect(seen).toEqual({ s: [1, 2], r: {}, p: "a=1&b=2", m: {} }) }) }) + +describe("tools.search alias", () => { + test("tools.search(...) behaves like the bare search(...) when no tool owns that path", async () => { + const runtime = CodeMode.make({ tools: { api: { list: echo("List things", "listed") } } }) + const direct = await value(runtime, `return search({ query: "list" })`) + expect(await value(runtime, `return tools.search({ query: "list" })`)).toStrictEqual(direct) + expect(await value(runtime, `return (await tools.search({ query: "list" })).items[0].path`)).toBe("tools.api.list") + }) + + test("a registered root-level search tool takes precedence", async () => { + const runtime = CodeMode.make({ tools: { search: echo("Custom search", "custom") } }) + expect(await value(runtime, `return await tools.search({})`)).toBe("custom") + }) +})