mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
fix(agents): preserve unreadable wrapped tool schemas
This commit is contained in:
57
src/agents/sessions/tools/tool-definition-wrapper.test.ts
Normal file
57
src/agents/sessions/tools/tool-definition-wrapper.test.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { AgentTool } from "../../runtime/index.js";
|
||||
import type { ToolDefinition } from "../extensions/types.js";
|
||||
import {
|
||||
createToolDefinitionFromAgentTool,
|
||||
wrapToolDefinition,
|
||||
} from "./tool-definition-wrapper.js";
|
||||
|
||||
describe("tool definition wrapper", () => {
|
||||
it("preserves an unreadable extension tool definition schema without crashing the wrapper", async () => {
|
||||
const execute = vi.fn(async () => ({ content: [] }));
|
||||
const prepareArguments = vi.fn((params: unknown) => params);
|
||||
const definition = {
|
||||
name: "hostile_definition",
|
||||
label: "Hostile Definition",
|
||||
description: "throws while reading parameters",
|
||||
prepareArguments,
|
||||
execute,
|
||||
} as unknown as ToolDefinition;
|
||||
Object.defineProperty(definition, "parameters", {
|
||||
get() {
|
||||
throw new Error("definition parameters exploded");
|
||||
},
|
||||
});
|
||||
|
||||
const tool = wrapToolDefinition(definition, () => ({}) as never);
|
||||
|
||||
expect(() => tool.parameters).toThrow("definition parameters exploded");
|
||||
expect(tool.prepareArguments).toBe(prepareArguments);
|
||||
await tool.execute("call-1", {}, undefined, undefined);
|
||||
expect(execute).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("preserves an unreadable agent tool schema without crashing definition synthesis", async () => {
|
||||
const execute = vi.fn(async () => ({ content: [] }));
|
||||
const prepareArguments = vi.fn((params: unknown) => params);
|
||||
const tool = {
|
||||
name: "hostile_agent_tool",
|
||||
label: "Hostile Agent Tool",
|
||||
description: "throws while reading parameters",
|
||||
prepareArguments,
|
||||
execute,
|
||||
} as unknown as AgentTool;
|
||||
Object.defineProperty(tool, "parameters", {
|
||||
get() {
|
||||
throw new Error("agent parameters exploded");
|
||||
},
|
||||
});
|
||||
|
||||
const definition = createToolDefinitionFromAgentTool(tool);
|
||||
|
||||
expect(() => definition.parameters).toThrow("agent parameters exploded");
|
||||
expect(definition.prepareArguments).toBe(prepareArguments);
|
||||
await definition.execute("call-1", {}, undefined, undefined, {} as never);
|
||||
expect(execute).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -2,6 +2,19 @@ import type { TSchema } from "typebox";
|
||||
import type { AgentTool } from "../../runtime/index.js";
|
||||
import type { ExtensionContext, ToolDefinition } from "../extensions/types.js";
|
||||
|
||||
function defineForwardedParameters<TParams extends TSchema>(
|
||||
target: { parameters?: TParams },
|
||||
source: { parameters: TParams },
|
||||
): void {
|
||||
Object.defineProperty(target, "parameters", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get() {
|
||||
return source.parameters;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** Wrap a ToolDefinition into an AgentTool for the core runtime. */
|
||||
export function wrapToolDefinition<
|
||||
TParams extends TSchema = TSchema,
|
||||
@@ -11,16 +24,17 @@ export function wrapToolDefinition<
|
||||
definition: ToolDefinition<TParams, TDetails, TState>,
|
||||
ctxFactory?: () => ExtensionContext,
|
||||
): AgentTool<TParams, TDetails> {
|
||||
return {
|
||||
const tool = {
|
||||
name: definition.name,
|
||||
label: definition.label,
|
||||
description: definition.description,
|
||||
parameters: definition.parameters,
|
||||
prepareArguments: definition.prepareArguments,
|
||||
executionMode: definition.executionMode,
|
||||
execute: (toolCallId, params, signal, onUpdate) =>
|
||||
definition.execute(toolCallId, params, signal, onUpdate, ctxFactory?.() as ExtensionContext),
|
||||
};
|
||||
} as AgentTool<TParams, TDetails>;
|
||||
defineForwardedParameters(tool, definition);
|
||||
return tool;
|
||||
}
|
||||
|
||||
/** Wrap multiple ToolDefinitions into AgentTools for the core runtime. */
|
||||
@@ -38,14 +52,15 @@ export function wrapToolDefinitions(
|
||||
* provides plain AgentTool overrides that do not include prompt metadata or renderers.
|
||||
*/
|
||||
export function createToolDefinitionFromAgentTool(tool: AgentTool): ToolDefinition {
|
||||
return {
|
||||
const definition = {
|
||||
name: tool.name,
|
||||
label: tool.label,
|
||||
description: tool.description,
|
||||
parameters: tool.parameters,
|
||||
prepareArguments: tool.prepareArguments,
|
||||
executionMode: tool.executionMode,
|
||||
execute: async (toolCallId, params, signal, onUpdate) =>
|
||||
tool.execute(toolCallId, params, signal, onUpdate),
|
||||
};
|
||||
} as ToolDefinition;
|
||||
defineForwardedParameters(definition, tool);
|
||||
return definition;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user