fix(coding-agent): preserved mixed-case plugin tool names on refresh
This commit is contained in:
@@ -51,6 +51,10 @@
|
||||
|
||||
- Continued Cursor turns that died with `NGHTTP2_INTERNAL_ERROR` / `NGHTTP2_REFUSED_STREAM` after tool calls already had results, instead of leaving the agent idle until the user typed "continue". HTTP/2 stream resets now use the same preserve-and-continue path as idle stream stalls, without requiring the Cursor exec-resolved marker that MCP/todo blocks never carry.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Stopped mixed-case plugin tool names from being lowercased during tool-set refresh, which unmounted them from `xd://` whenever MCP tools connected.
|
||||
|
||||
## [17.3.3] - 2026-08-14
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -41,10 +41,14 @@ const LEGACY_BUILTIN_TOOL_NAME_ALIASES: ReadonlyMap<string, BuiltinToolName> = n
|
||||
["find", "glob"],
|
||||
]);
|
||||
|
||||
/** Return the canonical tool name for current and legacy built-in tool IDs. */
|
||||
const CANONICAL_TOOL_NAMES: Record<string, true> = Object.fromEntries(
|
||||
[...BUILTIN_TOOL_NAMES, ...HIDDEN_TOOL_NAMES].map(name => [name, true]),
|
||||
);
|
||||
|
||||
/** Canonicalize built-in IDs and legacy aliases. Leave plugin names unchanged. */
|
||||
export function normalizeToolName(name: string): string {
|
||||
const normalized = name.toLowerCase();
|
||||
return LEGACY_BUILTIN_TOOL_NAME_ALIASES.get(normalized) ?? normalized;
|
||||
const lower = name.toLowerCase();
|
||||
return LEGACY_BUILTIN_TOOL_NAME_ALIASES.get(lower) ?? (Object.hasOwn(CANONICAL_TOOL_NAMES, lower) ? lower : name);
|
||||
}
|
||||
|
||||
/** Normalize and deduplicate tool names while preserving first-seen order. */
|
||||
|
||||
@@ -1289,4 +1289,21 @@ These tools became available:
|
||||
expect(session.getToolByName(newTool.name)).toBeDefined();
|
||||
expect(session.getActiveToolNames()).toContain(newTool.name);
|
||||
});
|
||||
|
||||
it("keeps mixed-case plugin devices mounted when MCP tools refresh", async () => {
|
||||
const xdevState = createTestXdevState();
|
||||
const { session, toolRegistry } = newSession(async toolNames => `tools:${toolNames.join(",")}`, {
|
||||
xdev: xdevState,
|
||||
});
|
||||
const pluginTool = { ...createBasicTool("CaseAdd", "Case Add"), loadMode: "discoverable" as const };
|
||||
toolRegistry.set(pluginTool.name, pluginTool);
|
||||
xdevState.mountedNames.add(pluginTool.name);
|
||||
|
||||
await session.refreshMCPTools([
|
||||
createMcpCustomTool("mcp__nucleus_search", "nucleus", "search", "Search nucleus"),
|
||||
]);
|
||||
|
||||
expect(session.getMountedXdevToolNames()).toContain("CaseAdd");
|
||||
expect(session.getToolByName("CaseAdd")).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { normalizeToolName } from "../src/tools/builtin-names";
|
||||
|
||||
describe("normalizeToolName", () => {
|
||||
it("maps legacy builtin aliases without touching plugin case", () => {
|
||||
expect(normalizeToolName("search")).toBe("grep");
|
||||
expect(normalizeToolName("Read")).toBe("read");
|
||||
expect(normalizeToolName("CaseAdd")).toBe("CaseAdd");
|
||||
expect(normalizeToolName("Constructor")).toBe("Constructor");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user