diff --git a/packages/core/src/agents/browser/browserAgentFactory.test.ts b/packages/core/src/agents/browser/browserAgentFactory.test.ts index 9bbf61cdc7..22960e1d01 100644 --- a/packages/core/src/agents/browser/browserAgentFactory.test.ts +++ b/packages/core/src/agents/browser/browserAgentFactory.test.ts @@ -474,6 +474,29 @@ describe('browserAgentFactory', () => { ); }); + it('should not register rule if it already exists with modes in different order', async () => { + const existingRule = { + toolName: 'mcp_browser_agent_fill', + decision: PolicyDecision.ASK_USER, + priority: 999, + // Reverse order of MODES_BY_PERMISSIVENESS + modes: [...MODES_BY_PERMISSIVENESS].reverse(), + source: 'BrowserAgent (Sensitive Actions)', + mcpName: BROWSER_AGENT_NAME, + }; + + mockPolicyEngine.getRules.mockReturnValue([existingRule]); + + await createBrowserAgentDefinition(mockConfig, mockMessageBus); + + // Should NOT add 'fill' rule again because it's already there (even if modes order differs) + expect(mockPolicyEngine.addRule).not.toHaveBeenCalledWith( + expect.objectContaining({ + toolName: 'mcp_browser_agent_fill', + }), + ); + }); + it('should register ALLOW rules for read-only tools', async () => { mockBrowserManager.getDiscoveredTools.mockResolvedValue([ { diff --git a/packages/core/src/agents/browser/browserAgentFactory.ts b/packages/core/src/agents/browser/browserAgentFactory.ts index a3365a028c..94779fcbac 100644 --- a/packages/core/src/agents/browser/browserAgentFactory.ts +++ b/packages/core/src/agents/browser/browserAgentFactory.ts @@ -176,7 +176,8 @@ export async function createBrowserAgentDefinition( rule1.decision === rule2.decision && rule1.priority === rule2.priority && rule1.mcpName === rule2.mcpName && - JSON.stringify(rule1.modes) === JSON.stringify(rule2.modes) + rule1.modes.length === rule2.modes.length && + rule1.modes.every((m) => rule2.modes.includes(m)) ); }