diff --git a/packages/core/src/codemode/instructions.ts b/packages/core/src/codemode/instructions.ts index 859c941f2759..093847878076 100644 --- a/packages/core/src/codemode/instructions.ts +++ b/packages/core/src/codemode/instructions.ts @@ -8,13 +8,15 @@ import { CodeModeCatalog } from "./catalog.js" // prettier-ignore const prompt = (hasMoreTools: boolean) => `# Code Mode -Use the \`execute\` tool to call the tools listed below. They cannot be called directly${hasMoreTools ? ", and neither can \`search\`. Both" : ". They"} only work inside code you pass to \`execute\`. +Code Mode adds the extra tools listed below, which you call from JavaScript inside the \`execute\` tool. Your regular tools (such as \`read\`, \`edit\`, \`shell\` and \`grep\`) are separate: keep calling them directly as usual. They do not exist inside \`execute\`. -${hasMoreTools ? `The catalog is partial. Inside \`execute\`, use \`search(...)\` to find a tool, then call it by the \`path\` in the result. \`search\` is synchronous. Call it without \`await\`; it does not return a Promise. Do not guess tool names. +The tools listed below cannot be called directly${hasMoreTools ? ", and neither can \`search\`. Both" : ". They"} only work inside code you pass to \`execute\`. + +${hasMoreTools ? `The catalog is partial. Before calling a tool that is not listed below, find it with the global \`search(...)\` function (not \`tools.search\`). Do not guess tool names. \`search\` is synchronous. Call it without \`await\`; it does not return a Promise. Each result's \`path\` is the complete call expression: write it out as code, for example \`await tools.ns.tool_name({ ... })\`. Do not use it as a key such as \`tools[path]\`. - ${searchSignature}` : "The catalog is complete. Do not guess tool names."} -## Available tools` +## Code Mode tools (call only inside \`execute\`)` export function render(catalog: CodeModeCatalog.Summary) { if (catalog.total === 0) diff --git a/packages/core/test/codemode/catalog.test.ts b/packages/core/test/codemode/catalog.test.ts index 520ae8e89609..32f84b343326 100644 --- a/packages/core/test/codemode/catalog.test.ts +++ b/packages/core/test/codemode/catalog.test.ts @@ -117,21 +117,24 @@ describe("CodeModeCatalog.summarize", () => { describe("CodeModeInstructions.render", () => { test("inlines complete catalogs without search guidance", () => { const instructions = render([lookup]) - expect(instructions).toContain("## Available tools") + expect(instructions).toContain("## Code Mode tools (call only inside `execute`)") expect(instructions).toContain("- orders (1 tool)") expect(instructions).toContain(` - ${lookup.signature} // Look up an order by ID`) expect(instructions).not.toContain("search") - expect(instructions).toContain("They cannot be called directly. They only work inside code you pass to `execute`.") + expect(instructions).toContain("Your regular tools (such as `read`, `edit`, `shell` and `grep`) are separate") + expect(instructions).toContain( + "The tools listed below cannot be called directly. They only work inside code you pass to `execute`.", + ) expect(instructions).toContain("The catalog is complete. Do not guess tool names.") }) test("adds search guidance when the catalog exceeds the budget", () => { const partial = render([lookup], 0) - expect(partial).toContain("## Available tools") + expect(partial).toContain("## Code Mode tools (call only inside `execute`)") expect(partial).toContain("- orders (1 tool, none shown)") - expect(partial).toContain("They cannot be called directly, and neither can `search`.") + expect(partial).toContain("The tools listed below cannot be called directly, and neither can `search`.") expect(partial).toContain( - "The catalog is partial. Inside `execute`, use `search(...)` to find a tool, then call it by the `path` in the result. `search` is synchronous. Call it without `await`; it does not return a Promise.", + "The catalog is partial. Before calling a tool that is not listed below, find it with the global `search(...)` function (not `tools.search`). Do not guess tool names. `search` is synchronous. Call it without `await`; it does not return a Promise.", ) expect(partial).toContain("- search({") expect(partial).toContain(" /** @integer @exclusiveMinimum 0 */\n limit?: number,") @@ -194,7 +197,7 @@ describe("CodeModeInstructions.update", () => { `Changed tool listings supersede the previously listed ones:\n - ${changed.signature} // Echo text`, ) expect(text).toContain("The following tools are no longer available and must not be called: tools.orders.lookup.") - expect(text).not.toContain("## Available tools") + expect(text).not.toContain("## Code Mode tools (call only inside `execute`)") }) test("names removed tools with exact callable expressions including bracket notation", () => { @@ -225,14 +228,14 @@ describe("CodeModeInstructions.update", () => { "The Code Mode tool catalog has changed. This catalog supersedes the previous Code Mode tool catalog.", ) expect(text).toContain("The catalog is partial.") - expect(text).toContain("## Available tools") + expect(text).toContain("## Code Mode tools (call only inside `execute`)") }) test("falls back to full replacement when the delta is larger than the catalog", () => { const previous = Array.from({ length: 200 }, (_, index) => entry(`bulk.tool${index}`, `Tool ${index}`)) const text = update([...previous, echo], [echo]) expect(text).toContain("This catalog supersedes the previous Code Mode tool catalog.") - expect(text).toContain("## Available tools") + expect(text).toContain("## Code Mode tools (call only inside `execute`)") expect(text).toContain("The catalog is complete.") expect(text).not.toContain("The following tools are no longer available") }) @@ -243,6 +246,6 @@ describe("CodeModeInstructions.update", () => { expect(text).toContain("`alpha` now has 11 tools") expect(text).toContain("search them again before relying on previous results") expect(text).not.toContain("tools.alpha.tool10(input:") - expect(text).not.toContain("## Available tools") + expect(text).not.toContain("## Code Mode tools (call only inside `execute`)") }) }) diff --git a/packages/core/test/codemode/instructions.test.ts b/packages/core/test/codemode/instructions.test.ts index 5c6f760f4037..b7df6051f6cc 100644 --- a/packages/core/test/codemode/instructions.test.ts +++ b/packages/core/test/codemode/instructions.test.ts @@ -46,16 +46,18 @@ describe("CodeModeInstructions", () => { it.effect("renders the initial catalog, semantic deltas, and removal", () => Effect.gen(function* () { const initialized = yield* readInitial(CodeModeInstructions.make({ tools: [echo] })) - expect(initialized.text).toContain("Use the `execute` tool to call the tools listed below.") + expect(initialized.text).toContain( + "Code Mode adds the extra tools listed below, which you call from JavaScript inside the `execute` tool.", + ) expect(initialized.text).toContain("The catalog is complete.") - expect(initialized.text).toContain("## Available tools") + expect(initialized.text).toContain("## Code Mode tools (call only inside `execute`)") expect(initialized.text).toContain(` - ${echo.signature} // Echo text`) const added = yield* readUpdate(CodeModeInstructions.make({ tools: [echo, lookup] }), initialized) expect(added.text).toContain("The Code Mode tool catalog has changed.") expect(added.text).toContain("New tools are available in addition to those previously listed:") expect(added.text).toContain(` - ${lookup.signature} // Look up an order`) - expect(added.text).not.toContain("## Available tools") + expect(added.text).not.toContain("## Code Mode tools (call only inside `execute`)") const removed = yield* readUpdate(CodeModeInstructions.make({ tools: [echo] }), { values: added.values }) expect(removed.text).toBe(