From ec4e36c518f9817cfed0efdafbcf657040911b6a Mon Sep 17 00:00:00 2001 From: ArcSolver Date: Sat, 22 Aug 2026 18:12:28 +0900 Subject: [PATCH 1/3] fix(tools): teach nested apply_patch delimiters in code mode Nested tools.apply_patch is host-executed from exec JavaScript, so a decorated *** Begin Patch *** envelope is rejected by Codex before any file is touched. Teach the exact delimiter in the shared code-mode nudge and Cursor guidance instead of rewriting exec bodies. --- src/adapters/cursor/tool-definitions.ts | 2 +- src/adapters/tool-catalog-nudge.ts | 2 +- tests/cursor-tool-definitions.test.ts | 3 +++ tests/tool-catalog-nudge.test.ts | 5 ++++- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/adapters/cursor/tool-definitions.ts b/src/adapters/cursor/tool-definitions.ts index 31d34ee5e7..d8fe23e4fe 100644 --- a/src/adapters/cursor/tool-definitions.ts +++ b/src/adapters/cursor/tool-definitions.ts @@ -651,7 +651,7 @@ export function buildCursorToolGuidanceSystemNote( // Code mode: shell/edit/MCP live inside freeform `exec` as nested helpers. Without this the // model probes for a top-level shell tool that is not there. codeMode - ? `\`${CODEX_UNIFIED_EXEC_TOOL}\` is Codex code mode: its body is JavaScript evaluated in a V8 isolate, not a shell command and not Node. Shell, file edits, and MCP are nested helpers called INSIDE that body as \`await tools.(...)\`, for example \`await tools.exec_command({cmd: \"ls\"})\`. Read the tool description and the isolate global \`ALL_TOOLS\` (not \`tools.ALL_TOOLS\`) for helpers this turn provides; absence from the top-level catalog or from \`exec\`'s description is not absence. Those nested helpers are not themselves top-level tools, so do not call \`exec_command\` or \`shell_command\` at the top level here${codeModeOtherTopLevelNames.length > 0 ? `; every other tool this turn lists, including ${quotedNames(codeModeOtherTopLevelNames)}, remains callable at the top level as usual` : ""}.` + ? `\`${CODEX_UNIFIED_EXEC_TOOL}\` is Codex code mode: its body is JavaScript evaluated in a V8 isolate, not a shell command and not Node. Shell, file edits, and MCP are nested helpers called INSIDE that body as \`await tools.(...)\`, for example \`await tools.exec_command({cmd: \"ls\"})\`. Read the tool description and the isolate global \`ALL_TOOLS\` (not \`tools.ALL_TOOLS\`) for helpers this turn provides; absence from the top-level catalog or from \`exec\`'s description is not absence. Those nested helpers are not themselves top-level tools, so do not call \`exec_command\` or \`shell_command\` at the top level here${codeModeOtherTopLevelNames.length > 0 ? `; every other tool this turn lists, including ${quotedNames(codeModeOtherTopLevelNames)}, remains callable at the top level as usual` : ""}. Nested \`tools.apply_patch(input)\` is host-executed: the string must begin exactly with \`*** Begin Patch\` and end with \`*** End Patch\` (no trailing \`***\` on those lines). OpenCodex does not rewrite JavaScript inside exec, so a decorated \`*** Begin Patch ***\` envelope is rejected by Codex before the file is touched.` : undefined, codeMode ? "In code mode the isolate returns nothing on its own: call `text(...)` (or `notify(...)`) on any value you need to see, or the call completes with empty output. There is no `require`, no `module`, and no filesystem or network globals; reach the host only through the nested helpers." diff --git a/src/adapters/tool-catalog-nudge.ts b/src/adapters/tool-catalog-nudge.ts index 626bd93d8d..b03a3b6a41 100644 --- a/src/adapters/tool-catalog-nudge.ts +++ b/src/adapters/tool-catalog-nudge.ts @@ -120,7 +120,7 @@ export function buildNonOpenAIToolCatalogNudgeFromNames( "Call only listed names with their listed argument keys; do not invent, translate, or rename tools.", "Names mentioned only in instructions, tool descriptions, argument descriptions, or nested helper APIs are not additional top-level tools.", verifiedCodeModeExecName - ? "`" + verifiedCodeModeExecName + "` is Codex code mode: its body is JavaScript evaluated in a V8 isolate. Nested helpers are called INSIDE that body as `await tools.(...)`, for example `await tools.exec_command({cmd: \"ls\"})` or `await tools.codex_app__list_threads({})`. Absence from the top-level catalog or from `" + verifiedCodeModeExecName + "`'s description is not absence: deferred helpers stay callable on `tools.`. Discover them from the isolate global `ALL_TOOLS`, not `tools.ALL_TOOLS`. Do not skip an available nested helper because it is omitted from the listed top-level names." + ? "`" + verifiedCodeModeExecName + "` is Codex code mode: its body is JavaScript evaluated in a V8 isolate. Nested helpers are called INSIDE that body as `await tools.(...)`, for example `await tools.exec_command({cmd: \"ls\"})` or `await tools.codex_app__list_threads({})`. Absence from the top-level catalog or from `" + verifiedCodeModeExecName + "`'s description is not absence: deferred helpers stay callable on `tools.`. Discover them from the isolate global `ALL_TOOLS`, not `tools.ALL_TOOLS`. Do not skip an available nested helper because it is omitted from the listed top-level names. Nested `tools.apply_patch(input)` is host-executed: the string must begin exactly with `*** Begin Patch` and end with `*** End Patch` (no trailing `***` on those lines). OpenCodex does not rewrite JavaScript inside exec, so a decorated `*** Begin Patch ***` envelope is rejected by Codex before the file is touched." : "If a listed tool exposes nested helpers such as a tools.* API, call the listed parent tool and use those helpers only inside that tool's input.", unavailableNeighborNames.length > 0 ? "Do not use neighboring-agent tool names " + quoteNames(unavailableNeighborNames) + " unless this turn's catalog lists those exact names." diff --git a/tests/cursor-tool-definitions.test.ts b/tests/cursor-tool-definitions.test.ts index 8eb0becf18..221fd75e76 100644 --- a/tests/cursor-tool-definitions.test.ts +++ b/tests/cursor-tool-definitions.test.ts @@ -483,6 +483,9 @@ describe("Cursor code mode tool guidance", () => { expect(note).toContain("isolate global `ALL_TOOLS`"); expect(note).toContain("not `tools.ALL_TOOLS`"); expect(note).toContain("absence from the top-level catalog"); + expect(note).toContain("`*** Begin Patch`"); + expect(note).toContain("no trailing `***`"); + expect(note).toContain("OpenCodex does not rewrite JavaScript inside exec"); // The flat-catalog shell-bridge guidance must NOT appear: naming a top-level // `exec_command` in code mode sends the model after a tool that does not exist. diff --git a/tests/tool-catalog-nudge.test.ts b/tests/tool-catalog-nudge.test.ts index 6316892034..09ba03278b 100644 --- a/tests/tool-catalog-nudge.test.ts +++ b/tests/tool-catalog-nudge.test.ts @@ -71,8 +71,11 @@ describe("non-OpenAI tool catalog nudge", () => { expect(note).toContain("await tools.codex_app__list_threads({})"); expect(note).toContain("isolate global `ALL_TOOLS`, not `tools.ALL_TOOLS`"); expect(note).toContain("Do not skip an available nested helper"); + expect(note).toContain("`*** Begin Patch`"); + expect(note).toContain("no trailing `***`"); + expect(note).toContain("OpenCodex does not rewrite JavaScript inside exec"); + expect(note).toContain("Nested `tools.apply_patch(input)` is host-executed"); expect(note).not.toContain("call the listed parent tool and use those helpers only inside that tool's input"); - expect(note).not.toContain("apply_patch"); }); test("keeps the generic nested-helper parent-tool rule when exec is not listed", () => { From abf46c022c0c1745c2330a38349e4aa1e0ee4f68 Mon Sep 17 00:00:00 2001 From: ArcSolver Date: Sat, 22 Aug 2026 18:49:00 +0900 Subject: [PATCH 2/3] test(pacing): drive FIFO spacing with the injected clock The macOS suite failed the wall-clock FIFO assertion at 63ms instead of 85ms. Use the existing fake pacing clock so queued starts advance at the 100ms interval without depending on runner timing. --- tests/cursor-tool-definitions.test.ts | 1 + tests/request-pacing.test.ts | 21 ++++++++++++++------- tests/tool-catalog-nudge.test.ts | 1 + 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/tests/cursor-tool-definitions.test.ts b/tests/cursor-tool-definitions.test.ts index 221fd75e76..54467e41ce 100644 --- a/tests/cursor-tool-definitions.test.ts +++ b/tests/cursor-tool-definitions.test.ts @@ -484,6 +484,7 @@ describe("Cursor code mode tool guidance", () => { expect(note).toContain("not `tools.ALL_TOOLS`"); expect(note).toContain("absence from the top-level catalog"); expect(note).toContain("`*** Begin Patch`"); + expect(note).toContain("`*** End Patch`"); expect(note).toContain("no trailing `***`"); expect(note).toContain("OpenCodex does not rewrite JavaScript inside exec"); diff --git a/tests/request-pacing.test.ts b/tests/request-pacing.test.ts index c2be3e5b8e..a5bc46e0b1 100644 --- a/tests/request-pacing.test.ts +++ b/tests/request-pacing.test.ts @@ -85,9 +85,11 @@ describe("requestPacingIntervalMs", () => { describe("provider request pacing queue", () => { test("spaces concurrent starts in one provider FIFO and exposes queue state", async () => { + const clock = fakePacingClock(); + setProviderRequestPacingRuntimeForTest(clock.runtime); const starts: number[] = []; const fetchImpl = Object.assign(async () => { - starts.push(Date.now()); + starts.push(clock.now()); return new Response("ok"); }, { preconnect() {} }) as typeof globalThis.fetch; const configured = { @@ -95,13 +97,18 @@ describe("provider request pacing queue", () => { fetch: fetchImpl, } as OcxProviderConfig & { fetch: typeof globalThis.fetch }; const send = providerFetch(configured, undefined, { providerName: "demo", modelId: "model-a" }); - const pending = [send("https://example.test/v1/chat/completions"), send("https://example.test/v1/chat/completions"), send("https://example.test/v1/chat/completions")]; - await Bun.sleep(10); + const first = send("https://example.test/v1/chat/completions"); + const second = send("https://example.test/v1/chat/completions"); + const third = send("https://example.test/v1/chat/completions"); + await first; + expect(starts).toEqual([0]); expect(providerRequestPacingStatus("demo", configured).queued).toBe(2); - await Promise.all(pending); - expect(starts).toHaveLength(3); - expect(starts[1] - starts[0]).toBeGreaterThanOrEqual(85); - expect(starts[2] - starts[1]).toBeGreaterThanOrEqual(85); + clock.advanceBy(100); + await second; + expect(starts).toEqual([0, 100]); + clock.advanceBy(100); + await third; + expect(starts).toEqual([0, 100, 200]); const status = providerRequestPacingStatus("demo", configured); expect(status.queued).toBe(0); expect(status.lastModelId).toBe("model-a"); diff --git a/tests/tool-catalog-nudge.test.ts b/tests/tool-catalog-nudge.test.ts index 09ba03278b..702d0f3e1d 100644 --- a/tests/tool-catalog-nudge.test.ts +++ b/tests/tool-catalog-nudge.test.ts @@ -72,6 +72,7 @@ describe("non-OpenAI tool catalog nudge", () => { expect(note).toContain("isolate global `ALL_TOOLS`, not `tools.ALL_TOOLS`"); expect(note).toContain("Do not skip an available nested helper"); expect(note).toContain("`*** Begin Patch`"); + expect(note).toContain("`*** End Patch`"); expect(note).toContain("no trailing `***`"); expect(note).toContain("OpenCodex does not rewrite JavaScript inside exec"); expect(note).toContain("Nested `tools.apply_patch(input)` is host-executed"); From c2ea1929fa391d5b3032a1988b5847762283acc6 Mon Sep 17 00:00:00 2001 From: ArcSolver Date: Sat, 22 Aug 2026 18:59:42 +0900 Subject: [PATCH 3/3] test(pacing): assert FIFO request identity with the injected clock Record each queued URL with its paced timestamp so a LIFO queue cannot pass the 0/100/200 spacing check. --- tests/request-pacing.test.ts | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/tests/request-pacing.test.ts b/tests/request-pacing.test.ts index a5bc46e0b1..bf8cfbb106 100644 --- a/tests/request-pacing.test.ts +++ b/tests/request-pacing.test.ts @@ -87,9 +87,9 @@ describe("provider request pacing queue", () => { test("spaces concurrent starts in one provider FIFO and exposes queue state", async () => { const clock = fakePacingClock(); setProviderRequestPacingRuntimeForTest(clock.runtime); - const starts: number[] = []; - const fetchImpl = Object.assign(async () => { - starts.push(clock.now()); + const started: Array<{ url: string; at: number }> = []; + const fetchImpl = Object.assign(async (input: Parameters[0]) => { + started.push({ url: String(input), at: clock.now() }); return new Response("ok"); }, { preconnect() {} }) as typeof globalThis.fetch; const configured = { @@ -97,18 +97,25 @@ describe("provider request pacing queue", () => { fetch: fetchImpl, } as OcxProviderConfig & { fetch: typeof globalThis.fetch }; const send = providerFetch(configured, undefined, { providerName: "demo", modelId: "model-a" }); - const first = send("https://example.test/v1/chat/completions"); - const second = send("https://example.test/v1/chat/completions"); - const third = send("https://example.test/v1/chat/completions"); + const first = send("https://example.test/v1/first"); + const second = send("https://example.test/v1/second"); + const third = send("https://example.test/v1/third"); await first; - expect(starts).toEqual([0]); + expect(started).toEqual([{ url: "https://example.test/v1/first", at: 0 }]); expect(providerRequestPacingStatus("demo", configured).queued).toBe(2); clock.advanceBy(100); await second; - expect(starts).toEqual([0, 100]); + expect(started).toEqual([ + { url: "https://example.test/v1/first", at: 0 }, + { url: "https://example.test/v1/second", at: 100 }, + ]); clock.advanceBy(100); await third; - expect(starts).toEqual([0, 100, 200]); + expect(started).toEqual([ + { url: "https://example.test/v1/first", at: 0 }, + { url: "https://example.test/v1/second", at: 100 }, + { url: "https://example.test/v1/third", at: 200 }, + ]); const status = providerRequestPacingStatus("demo", configured); expect(status.queued).toBe(0); expect(status.lastModelId).toBe("model-a");