refactor: centralize message-provider tool filtering

This commit is contained in:
Peter Steinberger
2026-02-26 04:22:44 +01:00
parent 7af6849c2f
commit e35fe7888b
3 changed files with 45 additions and 18 deletions

View File

@@ -319,19 +319,6 @@ describe("createOpenClawCodingTools", () => {
expect(names.has("telegram")).toBe(false);
expect(names.has("whatsapp")).toBe(false);
});
it.each(["voice", "VOICE", " Voice "])(
"does not expose tts tool for normalized voice message provider: %s",
(messageProvider) => {
const tools = createOpenClawCodingTools({ messageProvider });
const names = new Set(tools.map((tool) => tool.name));
expect(names.has("tts")).toBe(false);
},
);
it("keeps tts tool for non-voice providers", () => {
const tools = createOpenClawCodingTools({ messageProvider: "discord" });
const names = new Set(tools.map((tool) => tool.name));
expect(names.has("tts")).toBe(true);
});
it("filters session tools for sub-agent sessions by default", () => {
const tools = createOpenClawCodingTools({
sessionKey: "agent:main:subagent:test",

View File

@@ -0,0 +1,19 @@
import { describe, expect, it } from "vitest";
import { createOpenClawCodingTools } from "./pi-tools.js";
describe("createOpenClawCodingTools message provider policy", () => {
it.each(["voice", "VOICE", " Voice "])(
"does not expose tts tool for normalized voice provider: %s",
(messageProvider) => {
const tools = createOpenClawCodingTools({ messageProvider });
const names = new Set(tools.map((tool) => tool.name));
expect(names.has("tts")).toBe(false);
},
);
it("keeps tts tool for non-voice providers", () => {
const tools = createOpenClawCodingTools({ messageProvider: "discord" });
const names = new Set(tools.map((tool) => tool.name));
expect(names.has("tts")).toBe(true);
});
});

View File

@@ -67,6 +67,31 @@ function isOpenAIProvider(provider?: string) {
return normalized === "openai" || normalized === "openai-codex";
}
const TOOL_DENY_BY_MESSAGE_PROVIDER: Readonly<Record<string, readonly string[]>> = {
voice: ["tts"],
};
function normalizeMessageProvider(messageProvider?: string): string | undefined {
const normalized = messageProvider?.trim().toLowerCase();
return normalized && normalized.length > 0 ? normalized : undefined;
}
function applyMessageProviderToolPolicy(
tools: AnyAgentTool[],
messageProvider?: string,
): AnyAgentTool[] {
const normalizedProvider = normalizeMessageProvider(messageProvider);
if (!normalizedProvider) {
return tools;
}
const deniedTools = TOOL_DENY_BY_MESSAGE_PROVIDER[normalizedProvider];
if (!deniedTools || deniedTools.length === 0) {
return tools;
}
const deniedSet = new Set(deniedTools);
return tools.filter((tool) => !deniedSet.has(tool.name));
}
function isApplyPatchAllowedForModel(params: {
modelProvider?: string;
modelId?: string;
@@ -217,8 +242,6 @@ export function createOpenClawCodingTools(options?: {
/** Whether the sender is an owner (required for owner-only tools). */
senderIsOwner?: boolean;
}): AnyAgentTool[] {
const rawMessageProvider = options?.messageProvider?.trim().toLowerCase();
const isVoiceMessageProvider = rawMessageProvider === "voice";
const execToolName = "exec";
const sandbox = options?.sandbox?.enabled ? options.sandbox : undefined;
const {
@@ -482,9 +505,7 @@ export function createOpenClawCodingTools(options?: {
senderIsOwner: options?.senderIsOwner,
}),
];
const toolsForMessageProvider = isVoiceMessageProvider
? tools.filter((tool) => tool.name !== "tts")
: tools;
const toolsForMessageProvider = applyMessageProviderToolPolicy(tools, options?.messageProvider);
// Security: treat unknown/undefined as unauthorized (opt-in, not opt-out)
const senderIsOwner = options?.senderIsOwner === true;
const toolsByAuthorization = applyOwnerOnlyToolPolicy(toolsForMessageProvider, senderIsOwner);