mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
fix(plugins): skip unreadable tool descriptor cache entries
This commit is contained in:
@@ -16,10 +16,38 @@ vi.mock("../config/runtime-snapshot.js", () => ({
|
||||
|
||||
import {
|
||||
buildPluginToolDescriptorCacheKey,
|
||||
capturePluginToolDescriptor,
|
||||
createPluginToolDescriptorConfigCacheKeyMemo,
|
||||
resetPluginToolDescriptorCache,
|
||||
} from "./tool-descriptor-cache.js";
|
||||
|
||||
describe("plugin tool descriptor capture", () => {
|
||||
it("skips unreadable plugin tool descriptor fields", () => {
|
||||
const tool = {
|
||||
name: "unstable_tool",
|
||||
description: "unstable tool",
|
||||
parameters: { type: "object", properties: {} },
|
||||
async execute() {
|
||||
return { content: [{ type: "text", text: "ok" }] };
|
||||
},
|
||||
};
|
||||
Object.defineProperty(tool, "parameters", {
|
||||
enumerable: true,
|
||||
get() {
|
||||
throw new Error("fuzzplugin parameters getter exploded");
|
||||
},
|
||||
});
|
||||
|
||||
expect(
|
||||
capturePluginToolDescriptor({
|
||||
pluginId: "fuzzplugin",
|
||||
tool: tool as never,
|
||||
optional: false,
|
||||
}),
|
||||
).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("plugin tool descriptor cache keys", () => {
|
||||
afterEach(() => {
|
||||
hoisted.resolveRuntimeConfigCacheKey.mockClear();
|
||||
|
||||
@@ -145,21 +145,27 @@ export function capturePluginToolDescriptor(params: {
|
||||
pluginId: string;
|
||||
tool: AnyAgentTool;
|
||||
optional: boolean;
|
||||
}): CachedPluginToolDescriptor {
|
||||
const label = (params.tool as { label?: unknown }).label;
|
||||
const title = typeof label === "string" && label.trim() ? label.trim() : undefined;
|
||||
return {
|
||||
...(params.tool.displaySummary ? { displaySummary: params.tool.displaySummary } : {}),
|
||||
optional: params.optional,
|
||||
descriptor: {
|
||||
name: params.tool.name,
|
||||
...(title ? { title } : {}),
|
||||
description: params.tool.description,
|
||||
inputSchema: asJsonObject(params.tool.parameters),
|
||||
owner: { kind: "plugin", pluginId: params.pluginId },
|
||||
executor: { kind: "plugin", pluginId: params.pluginId, toolName: params.tool.name },
|
||||
},
|
||||
};
|
||||
}): CachedPluginToolDescriptor | undefined {
|
||||
try {
|
||||
const label = (params.tool as { label?: unknown }).label;
|
||||
const title = typeof label === "string" && label.trim() ? label.trim() : undefined;
|
||||
const displaySummary = params.tool.displaySummary;
|
||||
const name = params.tool.name;
|
||||
return {
|
||||
...(displaySummary ? { displaySummary } : {}),
|
||||
optional: params.optional,
|
||||
descriptor: {
|
||||
name,
|
||||
...(title ? { title } : {}),
|
||||
description: params.tool.description,
|
||||
inputSchema: asJsonObject(params.tool.parameters),
|
||||
owner: { kind: "plugin", pluginId: params.pluginId },
|
||||
executor: { kind: "plugin", pluginId: params.pluginId, toolName: name },
|
||||
},
|
||||
};
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function readCachedPluginToolDescriptors(
|
||||
|
||||
@@ -163,6 +163,22 @@ function createMalformedTool(name: string) {
|
||||
};
|
||||
}
|
||||
|
||||
function createToolWithUnstableDescriptorSchema(name: string) {
|
||||
let parameterReads = 0;
|
||||
const tool = makeTool(name);
|
||||
Object.defineProperty(tool, "parameters", {
|
||||
enumerable: true,
|
||||
get() {
|
||||
parameterReads += 1;
|
||||
if (parameterReads > 1) {
|
||||
throw new Error("fuzzplugin parameters getter exploded");
|
||||
}
|
||||
return { type: "object", properties: {} };
|
||||
},
|
||||
});
|
||||
return tool;
|
||||
}
|
||||
|
||||
function installConsoleMethodSpy(method: "log" | "warn") {
|
||||
const spy = vi.fn();
|
||||
loggingState.rawConsole = {
|
||||
@@ -2061,6 +2077,28 @@ describe("resolvePluginTools optional tools", () => {
|
||||
expect(factory).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("keeps live tools when descriptor cache capture hits an unstable schema getter", () => {
|
||||
const factory = vi.fn(() =>
|
||||
createToolWithUnstableDescriptorSchema("unstable_descriptor_tool"),
|
||||
);
|
||||
setRegistry([
|
||||
{
|
||||
pluginId: "cache-fuzz",
|
||||
optional: false,
|
||||
source: "/tmp/cache-fuzz.js",
|
||||
names: ["unstable_descriptor_tool"],
|
||||
factory,
|
||||
},
|
||||
]);
|
||||
|
||||
const first = resolvePluginTools(createResolveToolsParams());
|
||||
const second = resolvePluginTools(createResolveToolsParams());
|
||||
|
||||
expectResolvedToolNames(first, ["unstable_descriptor_tool"]);
|
||||
expectResolvedToolNames(second, ["unstable_descriptor_tool"]);
|
||||
expect(factory).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("executes cached healthy tools when a runtime sibling is malformed", async () => {
|
||||
const factory = vi.fn(() => [
|
||||
createMalformedTool("fuzz_move_angles"),
|
||||
|
||||
@@ -1333,14 +1333,15 @@ export function resolvePluginTools(params: {
|
||||
});
|
||||
if (manifestPlugin) {
|
||||
const capturedDescriptors = capturedDescriptorsByPluginId.get(entry.pluginId) ?? [];
|
||||
capturedDescriptors.push(
|
||||
capturePluginToolDescriptor({
|
||||
pluginId: entry.pluginId,
|
||||
tool,
|
||||
optional,
|
||||
}),
|
||||
);
|
||||
capturedDescriptorsByPluginId.set(entry.pluginId, capturedDescriptors);
|
||||
const capturedDescriptor = capturePluginToolDescriptor({
|
||||
pluginId: entry.pluginId,
|
||||
tool,
|
||||
optional,
|
||||
});
|
||||
if (capturedDescriptor) {
|
||||
capturedDescriptors.push(capturedDescriptor);
|
||||
capturedDescriptorsByPluginId.set(entry.pluginId, capturedDescriptors);
|
||||
}
|
||||
}
|
||||
tools.push(tool);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user