fix(tools): keep web_search top-level under xdev

web_search is a discoverable built-in, so with tools.xdev defaulting to
true createTools mounted it under xd:// and dropped it from the top-level
toolset. Models that call web_search directly got "Tool web_search not
found" on default configs. Pin it in XDEV_KEEP_TOP_LEVEL so it stays a
direct-callable tool while other discoverable tools keep mounting.

Fixes #5973
This commit is contained in:
roboomp
2026-07-18 09:17:20 +00:00
parent 3fdd85ab6c
commit e99d565e21
3 changed files with 34 additions and 4 deletions
+4
View File
@@ -2,6 +2,10 @@
## [Unreleased]
### Fixed
- Fixed `web_search` being unreachable under default config: with `tools.xdev: true`, the discoverable `web_search` tool was mounted under `xd://` and dropped from the top-level toolset, so models calling it directly got "Tool web_search not found". It is now pinned top-level via `XDEV_KEEP_TOP_LEVEL` while other discoverable tools keep mounting under `xd://` ([#5973](https://github.com/can1357/oh-my-pi/issues/5973)).
## [17.0.4] - 2026-07-18
### Fixed
+11 -4
View File
@@ -38,11 +38,18 @@ import { ToolError } from "./tool-errors";
/**
* Discoverable built-ins that must stay top-level even when xdev mounting is
* active: `todo` feeds the todo prelude/prewalk machinery, `ask` is the
* model's user-interaction affordance, and `grep` is the redirect target of
* the bash interceptor rules — each loses its harness integration if hidden
* behind dispatch.
* model's user-interaction affordance, `grep` is the redirect target of the
* bash interceptor rules, and `web_search` is invoked directly by most models
* (which have no notion of the `xd://` protocol) so hiding it behind dispatch
* makes it unreachable in practice (issue #5973) — each loses its harness
* integration or usability if hidden behind dispatch.
*/
export const XDEV_KEEP_TOP_LEVEL: Record<string, true> = { todo: true, ask: true, grep: true };
export const XDEV_KEEP_TOP_LEVEL: Record<string, true> = {
todo: true,
ask: true,
grep: true,
web_search: true,
};
/**
* Tools that carry the `xd://` transport itself and therefore can never be
@@ -206,3 +206,22 @@ describe("read and write route xd:// device URLs", () => {
}
});
});
describe("web_search stays top-level under xdev", () => {
it("keeps web_search a direct tool and off the xd:// registry with default config", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "write-xdev-websearch-"));
try {
const session = xdevSession(tempDir);
// Default config: tools.xdev is on.
expect(session.settings.get("tools.xdev")).toBe(true);
const tools = await createTools(session);
// Regression for #5973: models call web_search directly, so it must
// remain a top-level function and never mount behind the xd:// device.
expect(tools.some(entry => entry.name === "web_search")).toBe(true);
const mounted = session.xdevRegistry ? [...session.xdevRegistry.list()].map(t => t.name) : [];
expect(mounted).not.toContain("web_search");
} finally {
await removeWithRetries(tempDir);
}
});
});