Skip to content

Commit af2b0a6

Browse files
alexzhu0vincentkoc
andauthored
Keep agent web_search on runtime provider resolution (#88684)
Merged via squash. Prepared head SHA: bf13efd Co-authored-by: alexzhu0 <[email protected]> Co-authored-by: vincentkoc <[email protected]> Reviewed-by: @vincentkoc
1 parent 2a484a3 commit af2b0a6

3 files changed

Lines changed: 20 additions & 46 deletions

File tree

src/agents/tools/web-search.late-bind.test.ts

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,20 +35,10 @@ type RunWebSearchParams = {
3535
};
3636
};
3737

38-
type ProviderResolutionParams = {
39-
value?: string;
40-
};
41-
4238
function firstRunWebSearchParams(): RunWebSearchParams | undefined {
4339
return mocks.runWebSearch.mock.calls[0]?.[0] as RunWebSearchParams | undefined;
4440
}
4541

46-
function firstProviderResolutionParams(): ProviderResolutionParams | undefined {
47-
return mocks.resolveManifestContractOwnerPluginId.mock.calls[0]?.[0] as
48-
| ProviderResolutionParams
49-
| undefined;
50-
}
51-
5242
describe("web_search late-bound runtime fallback", () => {
5343
beforeEach(() => {
5444
mocks.runWebSearch.mockReset();
@@ -106,7 +96,7 @@ describe("web_search late-bound runtime fallback", () => {
10696

10797
await tool?.execute("call-search", { query: "openclaw" }, undefined);
10898

109-
expect(firstProviderResolutionParams()?.value).toBe("brave");
99+
expect(mocks.resolveManifestContractOwnerPluginId).not.toHaveBeenCalled();
110100
expect(firstRunWebSearchParams()?.preferRuntimeProviders).toBe(true);
111101
});
112102

@@ -122,7 +112,7 @@ describe("web_search late-bound runtime fallback", () => {
122112
expect(firstRunWebSearchParams()?.preferRuntimeProviders).toBe(true);
123113
});
124114

125-
it("does not prefer runtime providers when the configured provider is a bundled manifest owner", async () => {
115+
it("keeps runtime provider discovery enabled when configured search provider has a manifest owner", async () => {
126116
mocks.resolveManifestContractOwnerPluginId.mockReturnValue("openclaw-bundled-brave");
127117
const config = {
128118
tools: { web: { search: { provider: "brave" } } },
@@ -134,7 +124,8 @@ describe("web_search late-bound runtime fallback", () => {
134124

135125
await tool?.execute("call-search", { query: "openclaw" }, undefined);
136126

137-
expect(firstRunWebSearchParams()?.preferRuntimeProviders).toBe(false);
127+
expect(mocks.resolveManifestContractOwnerPluginId).not.toHaveBeenCalled();
128+
expect(firstRunWebSearchParams()?.preferRuntimeProviders).toBe(true);
138129
});
139130

140131
it("prefers active runtime metadata over options.runtimeWebSearch when present", async () => {

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

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,8 @@ describe("web tool runtime context", () => {
7878

7979
expect(resolved.config).toBe(runtimeConfig);
8080
expect(resolved.runtimeWebSearch?.selectedProvider).toBe("perplexity");
81-
const ownerLookup = latestOwnerLookupParams();
82-
expect(ownerLookup.contract).toBe("webSearchProviders");
83-
expect(ownerLookup.value).toBe("perplexity");
84-
expect(ownerLookup).not.toHaveProperty("origin");
85-
expect(ownerLookup.config).toBe(runtimeConfig);
81+
expect(resolved.preferRuntimeProviders).toBe(true);
82+
expect(mocks.resolveManifestContractOwnerPluginId).not.toHaveBeenCalled();
8683
});
8784

8885
it("falls back to captured search config and runtime metadata when active globals are missing", async () => {
@@ -104,28 +101,20 @@ describe("web tool runtime context", () => {
104101

105102
expect(resolved.config).toBe(capturedConfig);
106103
expect(resolved.runtimeWebSearch?.selectedProvider).toBe("brave");
107-
const ownerLookup = latestOwnerLookupParams();
108-
expect(ownerLookup.contract).toBe("webSearchProviders");
109-
expect(ownerLookup.value).toBe("brave");
110-
expect(ownerLookup).not.toHaveProperty("origin");
111-
expect(ownerLookup.config).toBe(capturedConfig);
104+
expect(resolved.preferRuntimeProviders).toBe(true);
105+
expect(mocks.resolveManifestContractOwnerPluginId).not.toHaveBeenCalled();
112106
});
113107

114-
it("uses configured provider ids when runtime metadata is absent", () => {
115-
resolveWebSearchToolRuntimeContext({
108+
it("keeps search runtime discovery enabled when runtime metadata is absent", () => {
109+
const resolved = resolveWebSearchToolRuntimeContext({
116110
config: { tools: { web: { search: { provider: "Brave" } } } },
117111
});
118112

119-
const ownerLookup = latestOwnerLookupParams();
120-
expect(ownerLookup.contract).toBe("webSearchProviders");
121-
expect(ownerLookup.value).toBe("brave");
122-
expect(ownerLookup).not.toHaveProperty("origin");
123-
expect(ownerLookup.config).toEqual({
124-
tools: { web: { search: { provider: "Brave" } } },
125-
});
113+
expect(resolved.preferRuntimeProviders).toBe(true);
114+
expect(mocks.resolveManifestContractOwnerPluginId).not.toHaveBeenCalled();
126115
});
127116

128-
it("treats resolved global provider owners as explicit selections", async () => {
117+
it("keeps search runtime discovery enabled for manifest-owned configured providers", async () => {
129118
mocks.resolveManifestContractOwnerPluginId.mockReturnValue("brave");
130119
const { resolveWebSearchToolRuntimeContext: resolveWebSearchToolRuntimeContextLocal } =
131120
await import("./web-tool-runtime-context.js");
@@ -134,10 +123,8 @@ describe("web tool runtime context", () => {
134123
config: { tools: { web: { search: { provider: "brave" } } } },
135124
});
136125

137-
expect(resolved.preferRuntimeProviders).toBe(false);
138-
expect(mocks.resolveManifestContractOwnerPluginId.mock.calls.at(-1)?.[0]).not.toHaveProperty(
139-
"origin",
140-
);
126+
expect(resolved.preferRuntimeProviders).toBe(true);
127+
expect(mocks.resolveManifestContractOwnerPluginId).not.toHaveBeenCalled();
141128
});
142129

143130
it("keeps runtime providers disabled for bundled fetch owners", async () => {

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

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ type WebProviderKind = "fetch" | "search";
1616

1717
type WebProviderRuntimeMetadata = RuntimeWebFetchMetadata | RuntimeWebSearchMetadata;
1818

19-
type WebProviderContract = "webFetchProviders" | "webSearchProviders";
20-
2119
type ResolvedWebToolRuntimeContext<TMetadata extends WebProviderRuntimeMetadata> = {
2220
config?: OpenClawConfig;
2321
preferRuntimeProviders: boolean;
@@ -36,23 +34,21 @@ function resolveRuntimeWebProviderId(metadata: WebProviderRuntimeMetadata | unde
3634
return metadata?.selectedProvider ?? metadata?.providerConfigured ?? "";
3735
}
3836

39-
function resolveWebProviderContract(kind: WebProviderKind): WebProviderContract {
40-
return kind === "fetch" ? "webFetchProviders" : "webSearchProviders";
41-
}
42-
4337
function shouldPreferRuntimeProviders(params: {
4438
config?: OpenClawConfig;
4539
kind: WebProviderKind;
4640
providerSelectionId: string;
4741
}): boolean {
48-
if (!params.providerSelectionId) {
42+
// Agent-side web_search must use the live runtime registry; runWebSearch
43+
// applies manifest ownership only as a load-scope hint after that.
44+
if (!params.providerSelectionId || params.kind === "search") {
4945
return true;
5046
}
5147
// Built-in providers are handled by core; plugin-owned selections should route through plugins.
5248
return !resolveManifestContractOwnerPluginId({
53-
contract: resolveWebProviderContract(params.kind),
49+
contract: "webFetchProviders",
5450
value: params.providerSelectionId,
55-
...(params.kind === "fetch" ? { origin: "bundled" as const } : {}),
51+
origin: "bundled",
5652
config: params.config,
5753
});
5854
}

0 commit comments

Comments
 (0)