feat(engine): sort tools deterministically for prompt cache stability
This commit is contained in:
35
packages/engine/src/__tests__/tool-ordering.test.ts
Normal file
35
packages/engine/src/__tests__/tool-ordering.test.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that tools are sorted deterministically by name.
|
||||||
|
* This is critical for prompt caching — tool schemas are part of the
|
||||||
|
* API request, and reordering them breaks cache prefix matching.
|
||||||
|
*/
|
||||||
|
describe("deterministic tool ordering", () => {
|
||||||
|
it("sorts tools alphabetically by name", () => {
|
||||||
|
const tools = [
|
||||||
|
{ name: "write", execute: async () => {} },
|
||||||
|
{ name: "bash", execute: async () => {} },
|
||||||
|
{ name: "read", execute: async () => {} },
|
||||||
|
{ name: "edit", execute: async () => {} },
|
||||||
|
];
|
||||||
|
|
||||||
|
const sorted = [...tools].sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
|
expect(sorted.map((t) => t.name)).toEqual(["bash", "edit", "read", "write"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("is stable across repeated sorts", () => {
|
||||||
|
const tools = [
|
||||||
|
{ name: "grep" },
|
||||||
|
{ name: "bash" },
|
||||||
|
{ name: "find" },
|
||||||
|
{ name: "read" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const sorted1 = [...tools].sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
const sorted2 = [...tools].sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
|
expect(sorted1.map((t) => t.name)).toEqual(sorted2.map((t) => t.name));
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1757,6 +1757,10 @@ export async function createFnAgent(options: AgentOptions): Promise<AgentResult>
|
|||||||
boundaryContext.worktreePath,
|
boundaryContext.worktreePath,
|
||||||
boundaryContext.worktreeProjectRoot,
|
boundaryContext.worktreeProjectRoot,
|
||||||
);
|
);
|
||||||
|
// Sort tools alphabetically by name for deterministic ordering.
|
||||||
|
// Prompt caching requires the tool list to be byte-identical across
|
||||||
|
// sessions — reordering breaks cache prefix matching.
|
||||||
|
customToolList.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
// Last-chance abort hook. Fires *here* — after every awaited setup step
|
// Last-chance abort hook. Fires *here* — after every awaited setup step
|
||||||
// in createFnAgent (provider registration, worktree validation, resource
|
// in createFnAgent (provider registration, worktree validation, resource
|
||||||
// loader reload) and immediately before the actual LLM session spawn.
|
// loader reload) and immediately before the actual LLM session spawn.
|
||||||
|
|||||||
Reference in New Issue
Block a user