diff --git a/packages/core/src/codemode/instructions.ts b/packages/core/src/codemode/instructions.ts index 3a546d79c2..da12c1c35e 100644 --- a/packages/core/src/codemode/instructions.ts +++ b/packages/core/src/codemode/instructions.ts @@ -6,17 +6,13 @@ import { Instructions } from "../instructions/index" import { CodeModeCatalog } from "./catalog" // prettier-ignore -const prompt = (hasMoreTools: boolean) => `Run JavaScript to orchestrate tool calls and compose their results. Imports, direct filesystem access, and timers are unavailable. Do not use \`fetch\`; all external access goes through \`tools\`. +const prompt = (hasMoreTools: boolean) => `The Code Mode tool catalog below is ${hasMoreTools ? "partial" : "complete"}. -Inside Code Mode, \`tools\` contains only the tools shown below${hasMoreTools ? " or returned by `search`" : ""}; surrounding top-level agent tools are not available and must not be called from the code. - -Prefer an explicit \`return\`; if omitted, the final top-level expression becomes the result. Await tool calls before returning; any calls still pending when execution ends are interrupted. Run independent calls concurrently with \`Promise.all\`. - -Do not infer or normalize tool names; use only the exact signatures shown below${hasMoreTools ? " or returned by `search`" : ""}, preserving bracket notation such as \`tools.["tool-name"](input)\`.${hasMoreTools ? ` +Inside Code Mode, \`tools\` contains only the tools shown below${hasMoreTools ? " or returned by `search`" : ""}; surrounding top-level agent tools are not available and must not be called from the code.${hasMoreTools ? ` ## Search -Only some tool signatures are shown. Use \`search\` to discover exact paths and signatures for additional tools: +Use \`search\` to discover exact paths and signatures for additional tools: - ${searchSignature}` : ""} diff --git a/packages/core/src/tool/execute.ts b/packages/core/src/tool/execute.ts index ddf7dbe481..c1b297156d 100644 --- a/packages/core/src/tool/execute.ts +++ b/packages/core/src/tool/execute.ts @@ -34,10 +34,11 @@ type CollectedFiles = { // Invariant model-facing guidance; the changing tool catalog is delivered through Instructions. const description = [ - "Run JavaScript in a confined Code Mode runtime through { code }.", - "Call Code Mode tools through `tools` using the exact paths and signatures from the instructions.", - "Use `search({ query })` to discover exact signatures when needed.", - "Await important calls and use `Promise.all` for independent calls.", + "Run JavaScript to orchestrate tool calls and compose their results through `{ code }` in a confined Code Mode runtime.", + "Imports, direct filesystem access, and timers are unavailable. Do not use `fetch`; all external access goes through `tools`.", + 'Call Code Mode tools through `tools` using only exact paths and signatures from the current catalog or `search`. Do not infer or normalize tool names; preserve bracket notation such as `tools.["tool-name"](input)`.', + "Prefer an explicit `return`; if omitted, the final top-level expression becomes the result.", + "Await every call whose completion matters; pending calls are interrupted when execution ends. Run independent calls concurrently with `Promise.all`.", ].join("\n") export const create = (registrations: ReadonlyMap) => { diff --git a/packages/core/test/codemode/catalog.test.ts b/packages/core/test/codemode/catalog.test.ts index 6831b46ad5..c880c5d53f 100644 --- a/packages/core/test/codemode/catalog.test.ts +++ b/packages/core/test/codemode/catalog.test.ts @@ -66,37 +66,23 @@ describe("CodeModeInstructions.render", () => { 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("Do not infer or normalize tool names") - expect(instructions).toContain('`tools.["tool-name"](input)`') + expect(instructions).toContain("The Code Mode tool catalog below is complete.") expect(instructions).toContain( "`tools` contains only the tools shown below; surrounding top-level agent tools are not available and must not be called from the code.", ) }) - test("describes the runtime and execution lifecycle concisely", () => { - const instructions = render([lookup]) - expect(instructions).toContain("Run JavaScript to orchestrate tool calls and compose their results.") - expect(instructions).toContain("Imports, direct filesystem access, and timers are unavailable.") - expect(instructions).toContain("Do not use `fetch`; all external access goes through `tools`.") - expect(instructions).toContain( - "Prefer an explicit `return`; if omitted, the final top-level expression becomes the result.", - ) - expect(instructions).toContain("any calls still pending when execution ends are interrupted") - expect(instructions).toContain("Run independent calls concurrently with `Promise.all`.") - }) - test("adds search guidance when the catalog exceeds the budget", () => { const partial = render([lookup], 0) expect(partial).toContain("## Available tools") expect(partial).toContain("- orders (1 tool, none shown)") expect(partial).toContain("## Search") - expect(partial).toContain("Only some tool signatures are shown.") + expect(partial).toContain("The Code Mode tool catalog below is partial.") expect(partial).toContain( "`tools` contains only the tools shown below or returned by `search`; surrounding top-level agent tools are not available and must not be called from the code.", ) expect(partial).toContain("- search(input: {") expect(partial).toContain(" limit?: number,\n offset?: number,") - expect(partial).toContain("or returned by `search`") expect(partial).not.toContain("tools.orders.lookup(input:") }) diff --git a/packages/core/test/tool-execute.test.ts b/packages/core/test/tool-execute.test.ts index 21498a2fbd..00f6bef32b 100644 --- a/packages/core/test/tool-execute.test.ts +++ b/packages/core/test/tool-execute.test.ts @@ -14,6 +14,18 @@ const context = { progress: () => Effect.void, } +test("execute describes invariant Code Mode behavior", () => { + expect(ExecuteTool.create(new Map()).description).toBe( + [ + "Run JavaScript to orchestrate tool calls and compose their results through `{ code }` in a confined Code Mode runtime.", + "Imports, direct filesystem access, and timers are unavailable. Do not use `fetch`; all external access goes through `tools`.", + 'Call Code Mode tools through `tools` using only exact paths and signatures from the current catalog or `search`. Do not infer or normalize tool names; preserve bracket notation such as `tools.["tool-name"](input)`.', + "Prefer an explicit `return`; if omitted, the final top-level expression becomes the result.", + "Await every call whose completion matters; pending calls are interrupted when execution ends. Run independent calls concurrently with `Promise.all`.", + ].join("\n"), + ) +}) + test("canonical execution distinguishes declared, model-only, and raw schema outputs", async () => { const declared = Tool.make({ description: "Declared",