Skip to content

Commit 0668f1e

Browse files
committed
fix(web): resolve explicit global search providers
1 parent 94f3eca commit 0668f1e

5 files changed

Lines changed: 52 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ Docs: https://docs.openclaw.ai
3838
- CLI tables: preserve muted/color styling on wrapped continuation lines after multiline cells, keeping `openclaw plugins list` descriptions readable.
3939
- Process execution: collapse case-insensitive duplicate child environment keys on Windows so caller-provided overrides such as `PATH` cannot be shadowed by host `Path`.
4040
- Browser CLI: request the existing `operator.admin` gateway scope explicitly for browser control commands, avoiding unnecessary scope-upgrade approval loops. Fixes #81555. (#81716) Thanks @joshavant.
41+
- Web: honor explicitly configured global `web_search` providers during provider ownership resolution while keeping sandboxed `web_fetch` limited to bundled providers.
4142
- iOS: restore first-use Contacts, Calendar, and Reminders permission prompts and add Privacy & Access status/actions in Settings. Thanks @BunsDev.
4243
- Canvas: return not found for malformed percent-encoded Canvas/A2UI/document asset paths and keep decoded parent traversal blocked before path normalization.
4344
- Telegram: allow trusted local Bot API media files whose filenames start with dots instead of falling back to remote download.

src/agents/tools/web-tool-runtime-context.test.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ describe("web tool runtime context", () => {
7777
const ownerLookup = latestOwnerLookupParams();
7878
expect(ownerLookup.contract).toBe("webSearchProviders");
7979
expect(ownerLookup.value).toBe("perplexity");
80-
expect(ownerLookup.origin).toBe("bundled");
80+
expect(ownerLookup).not.toHaveProperty("origin");
8181
expect(ownerLookup.config).toBe(runtimeConfig);
8282
});
8383

@@ -103,7 +103,7 @@ describe("web tool runtime context", () => {
103103
const ownerLookup = latestOwnerLookupParams();
104104
expect(ownerLookup.contract).toBe("webSearchProviders");
105105
expect(ownerLookup.value).toBe("brave");
106-
expect(ownerLookup.origin).toBe("bundled");
106+
expect(ownerLookup).not.toHaveProperty("origin");
107107
expect(ownerLookup.config).toBe(capturedConfig);
108108
});
109109

@@ -115,12 +115,26 @@ describe("web tool runtime context", () => {
115115
const ownerLookup = latestOwnerLookupParams();
116116
expect(ownerLookup.contract).toBe("webSearchProviders");
117117
expect(ownerLookup.value).toBe("brave");
118-
expect(ownerLookup.origin).toBe("bundled");
118+
expect(ownerLookup).not.toHaveProperty("origin");
119119
expect(ownerLookup.config).toEqual({
120120
tools: { web: { search: { provider: "Brave" } } },
121121
});
122122
});
123123

124+
it("treats resolved global provider owners as explicit selections", async () => {
125+
mocks.resolveManifestContractOwnerPluginId.mockReturnValue("brave");
126+
const { resolveWebSearchToolRuntimeContext } = await import("./web-tool-runtime-context.js");
127+
128+
const resolved = resolveWebSearchToolRuntimeContext({
129+
config: { tools: { web: { search: { provider: "brave" } } } },
130+
});
131+
132+
expect(resolved.preferRuntimeProviders).toBe(false);
133+
expect(mocks.resolveManifestContractOwnerPluginId.mock.calls.at(-1)?.[0]).not.toHaveProperty(
134+
"origin",
135+
);
136+
});
137+
124138
it("keeps runtime providers disabled for bundled fetch owners", async () => {
125139
mocks.resolveManifestContractOwnerPluginId.mockReturnValue("firecrawl");
126140

src/agents/tools/web-tool-runtime-context.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ function shouldPreferRuntimeProviders(params: {
4646
return !resolveManifestContractOwnerPluginId({
4747
contract: resolveWebProviderContract(params.kind),
4848
value: params.providerSelectionId,
49-
origin: "bundled",
49+
...(params.kind === "fetch" ? { origin: "bundled" as const } : {}),
5050
config: params.config,
5151
});
5252
}

src/web-search/runtime.test.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -595,8 +595,8 @@ describe("web search runtime", () => {
595595

596596
const ownerCall = mockCallParam(resolveManifestContractOwnerPluginIdMock);
597597
expect(ownerCall.contract).toBe("webSearchProviders");
598-
expect(ownerCall.origin).toBe("bundled");
599598
expect(ownerCall.value).toBe("duckduckgo");
599+
expect(ownerCall).not.toHaveProperty("origin");
600600
expect(mockCallParam(resolveRuntimeWebSearchProvidersMock).onlyPluginIds).toEqual([
601601
"duckduckgo",
602602
]);
@@ -628,6 +628,38 @@ describe("web search runtime", () => {
628628
expect(mockCallParam(resolveRuntimeWebSearchProvidersMock).onlyPluginIds).toEqual(["google"]);
629629
});
630630

631+
it("scopes configured global web_search providers when runtime providers are not preferred", async () => {
632+
resolveManifestContractOwnerPluginIdMock.mockImplementation(({ value }) =>
633+
value === "custom" ? "custom-search" : undefined,
634+
);
635+
resolvePluginWebSearchProvidersMock.mockReturnValue([createCustomSearchProvider()]);
636+
637+
await expect(
638+
runWebSearch({
639+
config: {
640+
tools: {
641+
web: {
642+
search: {
643+
provider: "custom",
644+
},
645+
},
646+
},
647+
...createCustomSearchConfig("custom-key"),
648+
},
649+
preferRuntimeProviders: false,
650+
args: { query: "configured-custom" },
651+
}),
652+
).resolves.toMatchObject({
653+
provider: "custom",
654+
});
655+
656+
expect(resolvePluginWebSearchProvidersMock).toHaveBeenCalledWith(
657+
expect.objectContaining({
658+
onlyPluginIds: ["custom-search"],
659+
}),
660+
);
661+
});
662+
631663
it("keeps runtime provider loading unscoped when configured provider ownership is unknown", async () => {
632664
resolveManifestContractOwnerPluginIdMock.mockReturnValue(undefined);
633665
resolveRuntimeWebSearchProvidersMock.mockReturnValue([

src/web-search/runtime.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,6 @@ export function resolveWebSearchProviderId(params: {
147147
resolvePluginWebSearchProviders({
148148
config,
149149
bundledAllowlistCompat: true,
150-
origin: "bundled",
151150
}),
152151
);
153152
const raw =
@@ -231,7 +230,6 @@ function resolveExplicitWebSearchProviderPluginIds(params: {
231230
config: params.config,
232231
contract: "webSearchProviders",
233232
value: providerId,
234-
origin: "bundled",
235233
});
236234
return ownerPluginId ? [ownerPluginId] : undefined;
237235
}
@@ -270,7 +268,6 @@ export function resolveWebSearchDefinition(
270268
: resolvePluginWebSearchProviders({
271269
config,
272270
bundledAllowlistCompat: true,
273-
origin: "bundled",
274271
...loadScope,
275272
}),
276273
);
@@ -334,7 +331,6 @@ function resolveWebSearchCandidates(
334331
: resolvePluginWebSearchProviders({
335332
config,
336333
bundledAllowlistCompat: true,
337-
origin: "bundled",
338334
...loadScope,
339335
}),
340336
).filter(Boolean);

0 commit comments

Comments
 (0)