























@@ -185,32 +185,42 @@ vi.mock("../plugins/manifest-registry.js", () => {
185185schemaCacheKey: "test:brave",
186186configSchema: buildSchema(),
187187},
188- ...[
189-"firecrawl",
190-"google",
191-"minimax",
192-"moonshot",
193-"perplexity",
194-"searxng",
195-"tavily",
196-"xai",
197-].map((id) => ({
198- id,
199-origin: "bundled",
188+ ...mockWebSearchProviders
189+.filter((provider) => provider.pluginId !== "brave")
190+.map((provider) => ({
191+id: provider.pluginId,
192+origin: "bundled",
193+channels: [],
194+providers: [],
195+contracts: {
196+webSearchProviders: [provider.id],
197+},
198+cliBackends: [],
199+skills: [],
200+hooks: [],
201+rootDir: `/tmp/plugins/${provider.pluginId}`,
202+source: "test",
203+manifestPath: `/tmp/plugins/${provider.pluginId}/openclaw.plugin.json`,
204+schemaCacheKey: `test:${provider.pluginId}`,
205+configSchema: buildSchema(),
206+})),
207+{
208+id: "acme-search",
209+origin: "installed",
200210channels: [],
201211providers: [],
202212contracts: {
203-webSearchProviders: [id],
213+webSearchProviders: ["acme-search"],
204214},
205215cliBackends: [],
206216skills: [],
207217hooks: [],
208-rootDir: `/tmp/plugins/${id}`,
218+rootDir: "/tmp/plugins/acme-search",
209219source: "test",
210-manifestPath: `/tmp/plugins/${id}/openclaw.plugin.json`,
211-schemaCacheKey: `test:${id}`,
220+manifestPath: "/tmp/plugins/acme-search/openclaw.plugin.json",
221+schemaCacheKey: "test:acme-search",
212222configSchema: buildSchema(),
213-})),
223+},
214224],
215225diagnostics: [],
216226}),
@@ -413,6 +423,74 @@ describe("web search provider config", () => {
413423414424expect(res.ok).toBe(true);
415425});
426+427+it("accepts provider ids registered by installed plugin manifests", () => {
428+const res = validateConfigObjectWithPlugins(
429+buildWebSearchProviderConfig({
430+provider: "acme-search",
431+}),
432+);
433+434+expect(res.ok).toBe(true);
435+});
436+437+it("rejects unknown provider ids without plugin evidence", () => {
438+const res = validateConfigObjectWithPlugins({
439+tools: {
440+web: {
441+search: {
442+provider: "brvae",
443+},
444+},
445+},
446+});
447+448+expect(res.ok).toBe(false);
449+if (res.ok) {
450+return;
451+}
452+expect(res.issues).toContainEqual(
453+expect.objectContaining({
454+path: "tools.web.search.provider",
455+message: "unknown web_search provider: brvae",
456+allowedValues: expect.arrayContaining(["acme-search", "brave", "gemini"]),
457+}),
458+);
459+});
460+461+it("warns for unknown provider ids when stale plugin config is present", () => {
462+const res = validateConfigObjectWithPlugins({
463+tools: {
464+web: {
465+search: {
466+provider: "missing-third-party",
467+},
468+},
469+},
470+plugins: {
471+entries: {
472+"missing-third-party": {
473+config: {
474+webSearch: {},
475+},
476+},
477+},
478+},
479+});
480+481+expect(res.ok).toBe(true);
482+if (!res.ok) {
483+return;
484+}
485+expect(res.warnings).toEqual(
486+expect.arrayContaining([
487+expect.objectContaining({
488+path: "tools.web.search.provider",
489+message: expect.stringContaining("unknown web_search provider: missing-third-party"),
490+}),
491+]),
492+);
493+});
416494});
417495418496describe("web search provider auto-detection", () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。