@@ -14,49 +14,24 @@ const TOOL_ALLOW_BY_MESSAGE_PROVIDER: Readonly<Record<string, readonly string[]>
|
14 | 14 | node: ["canvas", "image", "pdf", "tts", "web_fetch", "web_search"], |
15 | 15 | }; |
16 | 16 | |
17 | | -/** Filters tool names by the active message-provider allow/deny policy. */ |
18 | | -export function filterToolNamesByMessageProvider( |
19 | | -toolNames: readonly string[], |
| 17 | +/** Applies message-provider filtering while preserving duplicate tool entries. */ |
| 18 | +export function filterToolsByMessageProvider<TTool extends { name: string }>( |
| 19 | +tools: readonly TTool[], |
20 | 20 | messageProvider?: string, |
21 | | -): string[] { |
| 21 | +): TTool[] { |
22 | 22 | const normalizedProvider = normalizeOptionalLowercaseString(messageProvider); |
23 | 23 | if (!normalizedProvider) { |
24 | | -return [...toolNames]; |
| 24 | +return [...tools]; |
25 | 25 | } |
26 | 26 | const allowedTools = TOOL_ALLOW_BY_MESSAGE_PROVIDER[normalizedProvider]; |
27 | 27 | if (allowedTools && allowedTools.length > 0) { |
28 | 28 | const allowedSet = new Set(allowedTools); |
29 | | -return toolNames.filter((toolName) => allowedSet.has(toolName)); |
| 29 | +return tools.filter((tool) => allowedSet.has(tool.name)); |
30 | 30 | } |
31 | 31 | const deniedTools = TOOL_DENY_BY_MESSAGE_PROVIDER[normalizedProvider]; |
32 | 32 | if (!deniedTools || deniedTools.length === 0) { |
33 | | -return [...toolNames]; |
| 33 | +return [...tools]; |
34 | 34 | } |
35 | 35 | const deniedSet = new Set(deniedTools); |
36 | | -return toolNames.filter((toolName) => !deniedSet.has(toolName)); |
37 | | -} |
38 | | - |
39 | | -/** Applies message-provider filtering while preserving duplicate tool entries. */ |
40 | | -export function filterToolsByMessageProvider<TTool extends { name: string }>( |
41 | | -tools: readonly TTool[], |
42 | | -messageProvider?: string, |
43 | | -): TTool[] { |
44 | | -const filteredToolNames = filterToolNamesByMessageProvider( |
45 | | -tools.map((tool) => tool.name), |
46 | | -messageProvider, |
47 | | -); |
48 | | -const remainingCounts = new Map<string, number>(); |
49 | | -for (const toolName of filteredToolNames) { |
50 | | -remainingCounts.set(toolName, (remainingCounts.get(toolName) ?? 0) + 1); |
51 | | -} |
52 | | -return tools.filter((tool) => { |
53 | | -// Counted matching preserves the original order and duplicate instances |
54 | | -// after name-level policy filtering. |
55 | | -const remaining = remainingCounts.get(tool.name) ?? 0; |
56 | | -if (remaining <= 0) { |
57 | | -return false; |
58 | | -} |
59 | | -remainingCounts.set(tool.name, remaining - 1); |
60 | | -return true; |
61 | | -}); |
| 36 | +return tools.filter((tool) => !deniedSet.has(tool.name)); |
62 | 37 | } |