Skip to content

Commit 1555165

Browse files
VACIncvincentkoc
authored andcommitted
fix tavily tool secret refs
1 parent 129b9da commit 1555165

5 files changed

Lines changed: 118 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ Docs: https://docs.openclaw.ai
143143

144144
### Fixes
145145

146+
- Tavily: resolve dedicated `tavily_search` and `tavily_extract` tool credentials from the active runtime config snapshot, so `exec` SecretRef-backed API keys do not reach the tools unresolved. (#78610) Thanks @VACInc.
146147
- Gateway/sessions: clear cached skills snapshots during `/new` and `sessions.reset` so long-lived channel sessions rebuild the visible skill list after skills change. (#78873) Thanks @Evizero.
147148
- fix(auto-reply): gate inline skill tool dispatch [AI]. (#78517) Thanks @pgondhi987.
148149
- Canvas plugin: keep legacy root `canvasHost` configs valid until `openclaw doctor --fix` migrates them into `plugins.entries.canvas.config.host`, move Canvas/A2UI clients to gateway protocol v4 plugin surfaces, and refresh the generated A2UI bundle hash so normal builds stay clean.

extensions/tavily/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { definePluginEntry, type AnyAgentTool } from "openclaw/plugin-sdk/plugin-entry";
1+
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
22
import { createTavilyExtractTool } from "./src/tavily-extract-tool.js";
33
import { createTavilyWebSearchProvider } from "./src/tavily-search-provider.js";
44
import { createTavilySearchTool } from "./src/tavily-search-tool.js";
@@ -9,7 +9,7 @@ export default definePluginEntry({
99
description: "Bundled Tavily search and extract plugin",
1010
register(api) {
1111
api.registerWebSearchProvider(createTavilyWebSearchProvider());
12-
api.registerTool(createTavilySearchTool(api) as AnyAgentTool);
13-
api.registerTool(createTavilyExtractTool(api) as AnyAgentTool);
12+
api.registerTool((ctx) => createTavilySearchTool(api, ctx), { name: "tavily_search" });
13+
api.registerTool((ctx) => createTavilyExtractTool(api, ctx), { name: "tavily_extract" });
1414
},
1515
});

extensions/tavily/src/tavily-extract-tool.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-types";
2+
import type { OpenClawPluginToolContext } from "openclaw/plugin-sdk/plugin-entry";
13
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-runtime";
24
import {
35
jsonResult,
@@ -8,6 +10,18 @@ import { Type } from "typebox";
810
import { runTavilyExtract } from "./tavily-client.js";
911
import { optionalStringEnum } from "./tavily-tool-schema.js";
1012

13+
type TavilyToolConfigContext = Pick<
14+
OpenClawPluginToolContext,
15+
"config" | "runtimeConfig" | "getRuntimeConfig"
16+
>;
17+
18+
function resolveTavilyToolConfig(
19+
api: OpenClawPluginApi,
20+
ctx?: TavilyToolConfigContext,
21+
): OpenClawConfig {
22+
return ctx?.getRuntimeConfig?.() ?? ctx?.runtimeConfig ?? ctx?.config ?? api.config;
23+
}
24+
1125
const TavilyExtractToolSchema = Type.Object(
1226
{
1327
urls: Type.Array(Type.String(), {
@@ -39,7 +53,7 @@ const TavilyExtractToolSchema = Type.Object(
3953
{ additionalProperties: false },
4054
);
4155

42-
export function createTavilyExtractTool(api: OpenClawPluginApi) {
56+
export function createTavilyExtractTool(api: OpenClawPluginApi, ctx?: TavilyToolConfigContext) {
4357
return {
4458
name: "tavily_extract",
4559
label: "Tavily Extract",
@@ -65,7 +79,7 @@ export function createTavilyExtractTool(api: OpenClawPluginApi) {
6579

6680
return jsonResult(
6781
await runTavilyExtract({
68-
cfg: api.config,
82+
cfg: resolveTavilyToolConfig(api, ctx),
6983
urls,
7084
query,
7185
extractDepth,

extensions/tavily/src/tavily-search-tool.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-types";
2+
import type { OpenClawPluginToolContext } from "openclaw/plugin-sdk/plugin-entry";
13
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-runtime";
24
import {
35
jsonResult,
@@ -8,6 +10,18 @@ import { Type } from "typebox";
810
import { runTavilySearch } from "./tavily-client.js";
911
import { optionalStringEnum } from "./tavily-tool-schema.js";
1012

13+
type TavilyToolConfigContext = Pick<
14+
OpenClawPluginToolContext,
15+
"config" | "runtimeConfig" | "getRuntimeConfig"
16+
>;
17+
18+
function resolveTavilyToolConfig(
19+
api: OpenClawPluginApi,
20+
ctx?: TavilyToolConfigContext,
21+
): OpenClawConfig {
22+
return ctx?.getRuntimeConfig?.() ?? ctx?.runtimeConfig ?? ctx?.config ?? api.config;
23+
}
24+
1125
const TavilySearchToolSchema = Type.Object(
1226
{
1327
query: Type.String({ description: "Search query string." }),
@@ -46,7 +60,7 @@ const TavilySearchToolSchema = Type.Object(
4660
{ additionalProperties: false },
4761
);
4862

49-
export function createTavilySearchTool(api: OpenClawPluginApi) {
63+
export function createTavilySearchTool(api: OpenClawPluginApi, ctx?: TavilyToolConfigContext) {
5064
return {
5165
name: "tavily_search",
5266
label: "Tavily Search",
@@ -69,7 +83,7 @@ export function createTavilySearchTool(api: OpenClawPluginApi) {
6983

7084
return jsonResult(
7185
await runTavilySearch({
72-
cfg: api.config,
86+
cfg: resolveTavilyToolConfig(api, ctx),
7387
query,
7488
searchDepth,
7589
topic,

extensions/tavily/src/tavily-tools.test.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-types";
22
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-runtime";
3+
import { createTestPluginApi } from "openclaw/plugin-sdk/plugin-test-api";
34
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
45
import {
56
DEFAULT_TAVILY_BASE_URL,
@@ -33,13 +34,15 @@ describe("tavily tools", () => {
3334
let createTavilySearchTool: typeof import("./tavily-search-tool.js").createTavilySearchTool;
3435
let createTavilyExtractTool: typeof import("./tavily-extract-tool.js").createTavilyExtractTool;
3536
let tavilyClientTesting: typeof import("./tavily-client.js").__testing;
37+
let tavilyPlugin: typeof import("../index.js").default;
3638

3739
beforeAll(async () => {
3840
({ createTavilyWebSearchProvider } = await import("./tavily-search-provider.js"));
3941
({ createTavilySearchTool } = await import("./tavily-search-tool.js"));
4042
({ createTavilyExtractTool } = await import("./tavily-extract-tool.js"));
4143
({ __testing: tavilyClientTesting } =
4244
await vi.importActual<typeof import("./tavily-client.js")>("./tavily-client.js"));
45+
({ default: tavilyPlugin } = await import("../index.js"));
4346
});
4447

4548
beforeEach(() => {
@@ -140,6 +143,85 @@ describe("tavily tools", () => {
140143
});
141144
});
142145

146+
it("late-binds dedicated tools to the resolved runtime config snapshot", async () => {
147+
const rawConfig = {
148+
plugins: {
149+
entries: {
150+
tavily: {
151+
config: {
152+
webSearch: {
153+
apiKey: { source: "exec", provider: "default", id: "printf resolved-key" },
154+
},
155+
},
156+
},
157+
},
158+
},
159+
} as OpenClawConfig;
160+
const runtimeConfig = {
161+
plugins: {
162+
entries: {
163+
tavily: {
164+
config: {
165+
webSearch: {
166+
apiKey: "resolved-key",
167+
},
168+
},
169+
},
170+
},
171+
},
172+
} as OpenClawConfig;
173+
const registeredTools: Array<Parameters<OpenClawPluginApi["registerTool"]>[0]> = [];
174+
const registeredOptions: Array<Parameters<OpenClawPluginApi["registerTool"]>[1]> = [];
175+
const api = createTestPluginApi({
176+
config: rawConfig,
177+
registerTool(tool, opts) {
178+
registeredTools.push(tool);
179+
registeredOptions.push(opts);
180+
},
181+
});
182+
183+
tavilyPlugin.register(api);
184+
const searchFactory = registeredTools.find(
185+
(tool, index) =>
186+
registeredOptions[index]?.name === "tavily_search" && typeof tool === "function",
187+
);
188+
const extractFactory = registeredTools.find(
189+
(tool, index) =>
190+
registeredOptions[index]?.name === "tavily_extract" && typeof tool === "function",
191+
);
192+
if (typeof searchFactory !== "function" || typeof extractFactory !== "function") {
193+
throw new Error("Expected Tavily tools to register as runtime-context factories");
194+
}
195+
196+
const searchTool = searchFactory({
197+
config: rawConfig,
198+
runtimeConfig,
199+
});
200+
const extractTool = extractFactory({
201+
config: rawConfig,
202+
getRuntimeConfig: () => runtimeConfig,
203+
});
204+
if (Array.isArray(searchTool) || !searchTool || Array.isArray(extractTool) || !extractTool) {
205+
throw new Error("Expected single Tavily tool definitions");
206+
}
207+
208+
await searchTool.execute("search-call", { query: "openclaw" });
209+
await extractTool.execute("extract-call", { urls: ["https://example.com"] });
210+
211+
expect(runTavilySearch).toHaveBeenCalledWith(
212+
expect.objectContaining({
213+
cfg: runtimeConfig,
214+
query: "openclaw",
215+
}),
216+
);
217+
expect(runTavilyExtract).toHaveBeenCalledWith(
218+
expect.objectContaining({
219+
cfg: runtimeConfig,
220+
urls: ["https://example.com"],
221+
}),
222+
);
223+
});
224+
143225
it("drops empty domain arrays and forwards query-scoped chunking", async () => {
144226
runTavilySearch.mockImplementationOnce(async (params: Record<string, unknown>) => ({
145227
ok: true,

0 commit comments

Comments
 (0)