mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:20:43 +00:00
fix(web-search): restore SecretRef runtime compatibility for bundled providers (#68424)
Adds missing compatibility runtime path metadata for bundled SecretRef-capable web-search providers and keeps the manifest registry covered by a regression test.\n\nThanks @afurm!
This commit is contained in:
@@ -15,6 +15,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Fixes
|
||||
|
||||
- Telegram/status reactions: honor `messages.removeAckAfterReply` when lifecycle status reactions are enabled, clearing or restoring the reaction after success/error using the configured hold timings. (#68067) Thanks @poiskgit.
|
||||
- Web search/plugins: resolve plugin-scoped SecretRef API keys for bundled Exa, Firecrawl, Gemini, Kimi, Perplexity, Tavily, and Grok web-search providers when they are selected through the shared web-search config. (#68424) Thanks @afurm.
|
||||
- Telegram/polling: raise the default polling watchdog threshold from 90s to 120s and add configurable `channels.telegram.pollingStallThresholdMs` (also per-account) so long-running Telegram work gets more room before polling is treated as stalled. (#57737) Thanks @Vitalcheffe.
|
||||
- Telegram/polling: bound the persisted-offset confirmation `getUpdates` probe with a client-side timeout so a zombie socket cannot hang polling recovery before the runner watchdog starts. (#50368) Thanks @boticlaw.
|
||||
- Agents/Pi runner: retry silent `stopReason=error` turns with no output when no side effects ran, so non-frontier providers that briefly return empty error turns get another chance instead of ending the session early. (#68310) Thanks @Chased1k.
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
"contracts": {
|
||||
"webSearchProviders": ["exa"]
|
||||
},
|
||||
"configContracts": {
|
||||
"compatibilityRuntimePaths": ["tools.web.search.apiKey"]
|
||||
},
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
"webSearchProviders": ["firecrawl"],
|
||||
"tools": ["firecrawl_search", "firecrawl_scrape"]
|
||||
},
|
||||
"configContracts": {
|
||||
"compatibilityRuntimePaths": ["tools.web.search.apiKey"]
|
||||
},
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
@@ -53,6 +53,9 @@
|
||||
"videoGenerationProviders": ["google"],
|
||||
"webSearchProviders": ["gemini"]
|
||||
},
|
||||
"configContracts": {
|
||||
"compatibilityRuntimePaths": ["tools.web.search.apiKey"]
|
||||
},
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
@@ -52,6 +52,9 @@
|
||||
"mediaUnderstandingProviders": ["moonshot"],
|
||||
"webSearchProviders": ["kimi"]
|
||||
},
|
||||
"configContracts": {
|
||||
"compatibilityRuntimePaths": ["tools.web.search.apiKey"]
|
||||
},
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
"contracts": {
|
||||
"webSearchProviders": ["perplexity"]
|
||||
},
|
||||
"configContracts": {
|
||||
"compatibilityRuntimePaths": ["tools.web.search.apiKey"]
|
||||
},
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
"webSearchProviders": ["tavily"],
|
||||
"tools": ["tavily_search", "tavily_extract"]
|
||||
},
|
||||
"configContracts": {
|
||||
"compatibilityRuntimePaths": ["tools.web.search.apiKey"]
|
||||
},
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
@@ -87,6 +87,9 @@
|
||||
"videoGenerationProviders": ["xai"],
|
||||
"tools": ["code_execution", "x_search"]
|
||||
},
|
||||
"configContracts": {
|
||||
"compatibilityRuntimePaths": ["tools.web.search.apiKey"]
|
||||
},
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
loadPluginManifestRegistry,
|
||||
resolveManifestContractOwnerPluginId,
|
||||
resolveManifestContractPluginIds,
|
||||
resolveManifestContractPluginIdsByCompatibilityRuntimePath,
|
||||
} from "./manifest-registry.js";
|
||||
import {
|
||||
hasBundledWebFetchProviderPublicArtifact,
|
||||
@@ -9,6 +11,29 @@ import {
|
||||
resolveBundledExplicitWebSearchProvidersFromPublicArtifacts,
|
||||
} from "./web-provider-public-artifacts.explicit.js";
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
}
|
||||
|
||||
function supportsSecretRefWebSearchApiKey(
|
||||
plugin: ReturnType<typeof loadPluginManifestRegistry>["plugins"][number],
|
||||
): boolean {
|
||||
const configProperties = isRecord(plugin.configSchema?.["properties"])
|
||||
? plugin.configSchema["properties"]
|
||||
: undefined;
|
||||
const webSearch = configProperties?.["webSearch"];
|
||||
if (!isRecord(webSearch)) {
|
||||
return false;
|
||||
}
|
||||
const properties = isRecord(webSearch["properties"]) ? webSearch["properties"] : undefined;
|
||||
const apiKey = properties?.["apiKey"];
|
||||
if (!isRecord(apiKey)) {
|
||||
return false;
|
||||
}
|
||||
const typeValue = apiKey["type"];
|
||||
return Array.isArray(typeValue) && typeValue.includes("object");
|
||||
}
|
||||
|
||||
describe("web provider public artifacts", () => {
|
||||
it("has a public artifact for every bundled web search provider declared in manifests", () => {
|
||||
const pluginIds = resolveManifestContractPluginIds({
|
||||
@@ -44,6 +69,28 @@ describe("web provider public artifacts", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("registers compatibility runtime paths for bundled SecretRef-capable web search providers", () => {
|
||||
const registry = loadPluginManifestRegistry({ cache: false });
|
||||
const expectedPluginIds = registry.plugins
|
||||
.filter(
|
||||
(plugin) =>
|
||||
plugin.origin === "bundled" &&
|
||||
(plugin.contracts?.webSearchProviders?.length ?? 0) > 0 &&
|
||||
supportsSecretRefWebSearchApiKey(plugin),
|
||||
)
|
||||
.map((plugin) => plugin.id)
|
||||
.toSorted((left, right) => left.localeCompare(right));
|
||||
|
||||
expect(expectedPluginIds).not.toHaveLength(0);
|
||||
expect(
|
||||
resolveManifestContractPluginIdsByCompatibilityRuntimePath({
|
||||
contract: "webSearchProviders",
|
||||
path: "tools.web.search.apiKey",
|
||||
origin: "bundled",
|
||||
}),
|
||||
).toEqual(expectedPluginIds);
|
||||
});
|
||||
|
||||
it("has a public artifact for every bundled web fetch provider declared in manifests", () => {
|
||||
const pluginIds = resolveManifestContractPluginIds({
|
||||
contract: "webFetchProviders",
|
||||
|
||||
Reference in New Issue
Block a user