mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:50:43 +00:00
fix(agents): keep web_search runtime providers visible
This commit is contained in:
@@ -45,6 +45,7 @@ Docs: https://docs.openclaw.ai
|
||||
- CLI/logs: auto-reconnect `openclaw logs --follow` on transient gateway disconnects (WebSocket close, timeout, connection drop) with bounded exponential backoff (up to 8 retries, capped at 30 s) and stderr retry warnings, while still exiting immediately on non-recoverable auth or configuration errors. Fixes #74782. (#75059) Thanks @shashank-poola.
|
||||
- CLI/logs: announce `--follow` recovery with a `[logs] gateway reconnected` notice once a poll succeeds after a transient outage, and emit JSON `notice` records in `--json` mode for both the retry warning and the reconnect transition, so live monitoring scripts can react to the recovery. Carries forward #75059. (#75372) Thanks @romneyda.
|
||||
- Plugins/onboarding: trust optional official plugin and web-search installs selected from the official catalog so npm security scanning treats them like other source-linked official install paths. Thanks @vincentkoc.
|
||||
- Agents/web_search: keep installed runtime provider discovery enabled when web-search metadata is missing, so externally installed official providers such as Brave remain visible to agent and cron turns instead of falling back to bundled-only lookup. Fixes #76626. Thanks @amknight.
|
||||
- Tests/plugins: expose the Discord npm onboarding Docker lane as a package script and assert planned Docker lanes point at real scripts, so external-channel onboarding coverage can actually run. Thanks @vincentkoc.
|
||||
- Plugins/ClawHub: explain unreleased ClawHub plugin artifacts as a rollout-state fallback to `npm:` installs instead of leaking raw archive metadata fields. Thanks @vincentkoc.
|
||||
- Tests/onboarding: assert packaged channel onboarding leaves `openclaw channels status --json` and plain `openclaw status` showing the configured channel, covering the empty Channels table regression path. Thanks @vincentkoc.
|
||||
|
||||
@@ -98,7 +98,7 @@ export function createWebSearchTool(options?: {
|
||||
? (getActiveSecretsRuntimeSnapshot()?.config ?? options?.config)
|
||||
: options?.config;
|
||||
const preferRuntimeProviders =
|
||||
Boolean(runtimeProviderId) &&
|
||||
!runtimeProviderId ||
|
||||
!resolveManifestContractOwnerPluginId({
|
||||
contract: "webSearchProviders",
|
||||
value: runtimeProviderId,
|
||||
|
||||
@@ -8,12 +8,37 @@ import {
|
||||
import { createWebFetchTool, createWebSearchTool } from "./web-tools.js";
|
||||
|
||||
const runWebSearchCalls = vi.hoisted(
|
||||
() => [] as Array<{ config?: unknown; runtimeWebSearch?: unknown }>,
|
||||
() =>
|
||||
[] as Array<{
|
||||
config?: unknown;
|
||||
preferRuntimeProviders?: boolean;
|
||||
runtimeWebSearch?: unknown;
|
||||
}>,
|
||||
);
|
||||
const activeSecretsRuntimeSnapshot = vi.hoisted(() => ({
|
||||
current: null as null | { config: unknown },
|
||||
}));
|
||||
|
||||
function readConfiguredSearchProvider(config: unknown): string | undefined {
|
||||
if (!config || typeof config !== "object") {
|
||||
return undefined;
|
||||
}
|
||||
const tools = (config as { tools?: unknown }).tools;
|
||||
if (!tools || typeof tools !== "object") {
|
||||
return undefined;
|
||||
}
|
||||
const web = (tools as { web?: unknown }).web;
|
||||
if (!web || typeof web !== "object") {
|
||||
return undefined;
|
||||
}
|
||||
const search = (web as { search?: unknown }).search;
|
||||
if (!search || typeof search !== "object") {
|
||||
return undefined;
|
||||
}
|
||||
const provider = (search as { provider?: unknown }).provider;
|
||||
return typeof provider === "string" ? provider : undefined;
|
||||
}
|
||||
|
||||
vi.mock("../../secrets/runtime.js", () => ({
|
||||
getActiveSecretsRuntimeSnapshot: () => activeSecretsRuntimeSnapshot.current,
|
||||
}));
|
||||
@@ -30,7 +55,8 @@ vi.mock("../../web-search/runtime.js", async () => {
|
||||
options?.runtimeWebSearch?.selectedProvider ??
|
||||
options?.runtimeWebSearch?.providerConfigured ??
|
||||
getActiveRuntimeWebToolsMetadata()?.search?.selectedProvider ??
|
||||
getActiveRuntimeWebToolsMetadata()?.search?.providerConfigured;
|
||||
getActiveRuntimeWebToolsMetadata()?.search?.providerConfigured ??
|
||||
readConfiguredSearchProvider(options?.config);
|
||||
const registration = getActivePluginRegistry()?.webSearchProviders.find(
|
||||
(entry) => entry.provider.id === providerId,
|
||||
);
|
||||
@@ -54,10 +80,12 @@ vi.mock("../../web-search/runtime.js", async () => {
|
||||
runWebSearch: async (options: {
|
||||
config?: unknown;
|
||||
args: Record<string, unknown>;
|
||||
preferRuntimeProviders?: boolean;
|
||||
runtimeWebSearch?: unknown;
|
||||
}) => {
|
||||
runWebSearchCalls.push({
|
||||
config: options.config,
|
||||
preferRuntimeProviders: options.preferRuntimeProviders,
|
||||
runtimeWebSearch: options.runtimeWebSearch,
|
||||
});
|
||||
const resolved = resolveRuntimeDefinition(options as never);
|
||||
@@ -142,6 +170,52 @@ describe("web tools defaults", () => {
|
||||
expect(result?.details).toMatchObject({ ok: true });
|
||||
});
|
||||
|
||||
it("keeps runtime provider discovery enabled when runtime web_search metadata is missing", async () => {
|
||||
const registry = createEmptyPluginRegistry();
|
||||
registry.webSearchProviders.push({
|
||||
pluginId: "custom-search",
|
||||
pluginName: "Custom Search",
|
||||
source: "test",
|
||||
provider: {
|
||||
id: "custom",
|
||||
label: "Custom Search",
|
||||
hint: "Custom runtime provider",
|
||||
envVars: ["CUSTOM_SEARCH_API_KEY"],
|
||||
placeholder: "custom-...",
|
||||
signupUrl: "https://example.com/signup",
|
||||
autoDetectOrder: 1,
|
||||
credentialPath: "plugins.entries.custom-search.config.webSearch.apiKey",
|
||||
getCredentialValue: () => "configured",
|
||||
setCredentialValue: () => {},
|
||||
createTool: () => ({
|
||||
description: "custom runtime tool",
|
||||
parameters: {},
|
||||
execute: async () => ({ provider: "custom" }),
|
||||
}),
|
||||
},
|
||||
});
|
||||
setActivePluginRegistry(registry);
|
||||
|
||||
const tool = createWebSearchTool({
|
||||
config: {
|
||||
tools: {
|
||||
web: {
|
||||
search: {
|
||||
provider: "custom",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
sandboxed: true,
|
||||
});
|
||||
|
||||
const result = await tool?.execute?.("call-runtime-provider-without-metadata", {});
|
||||
|
||||
expect(result?.details).toMatchObject({ provider: "custom" });
|
||||
expect(runWebSearchCalls).toHaveLength(1);
|
||||
expect(runWebSearchCalls[0]?.preferRuntimeProviders).toBe(true);
|
||||
});
|
||||
|
||||
it("late-binds managed web_search execution to the current runtime snapshot", async () => {
|
||||
const registry = createEmptyPluginRegistry();
|
||||
registry.webSearchProviders.push(
|
||||
|
||||
Reference in New Issue
Block a user