diff --git a/src/services/agent.js b/src/services/agent.js index 63a4a71..8643cff 100644 --- a/src/services/agent.js +++ b/src/services/agent.js @@ -10,7 +10,7 @@ const MandateService = require("./mandate"); const logger = require("../utils/logger"); class AgentService { - constructor(database, config = {}) { + constructor(database, config = {}, a2aService = null) { this.db = database; this.config = { defaultSpendingLimit: config.defaultSpendingLimit || 1000, @@ -18,6 +18,7 @@ class AgentService { config.defaultAuthorizedCounterparties || [], }; this.mandateService = new MandateService(config.mandateConfig); + this.a2aService = a2aService; } /** @@ -164,7 +165,7 @@ class AgentService { } /** - * Perform an Agent-to-Agent (A2A) transfer (conceptual) + * Perform an Agent-to-Agent (A2A) transfer * @param {Object} params - Transfer parameters * @param {string} params.fromAgentId - Source agent ID * @param {string} params.toAgentId - Destination agent ID @@ -173,9 +174,16 @@ class AgentService { * @returns {Promise} Transfer result */ async performA2ATransfer({ fromAgentId, toAgentId, amount, currency }) { - // This is a conceptual implementation. - // In a real system, this would involve interaction with the WalletService, - // and policy checks for both agents. + if (this.a2aService) { + return await this.a2aService.executeTransfer({ + fromAgentId, + toAgentId, + amount, + ucpPayload: { currency }, + }); + } + + // Fallback for cases where a2aService is not provided const fromAgent = await this.getAgent(fromAgentId); await this.getAgent(toAgentId); // Verify toAgent exists diff --git a/tests/unit/agent.spec.js b/tests/unit/agent.spec.js index 49a8df6..f263044 100644 --- a/tests/unit/agent.spec.js +++ b/tests/unit/agent.spec.js @@ -3,6 +3,7 @@ const AgentService = require('../../src/services/agent'); describe('AgentService', () => { let agentService; let mockDb; + let mockA2AService; beforeEach(() => { mockDb = { @@ -11,7 +12,10 @@ describe('AgentService', () => { findAllAgents: jest.fn(), updateAgent: jest.fn(), }; - agentService = new AgentService(mockDb); + mockA2AService = { + executeTransfer: jest.fn() + }; + agentService = new AgentService(mockDb, {}, mockA2AService); }); describe('registerAgent', () => { @@ -85,4 +89,67 @@ describe('AgentService', () => { expect(result.success).toBe(true); }); }); + + describe('performA2ATransfer', () => { + it('should delegate to a2aService if provided', async () => { + const transferParams = { + fromAgentId: 'agent1', + toAgentId: 'agent2', + amount: 100, + currency: 'USD' + }; + const expectedResult = { success: true, transferId: 'tx123' }; + mockA2AService.executeTransfer.mockResolvedValue(expectedResult); + + const result = await agentService.performA2ATransfer(transferParams); + + expect(mockA2AService.executeTransfer).toHaveBeenCalledWith({ + fromAgentId: 'agent1', + toAgentId: 'agent2', + amount: 100, + ucpPayload: { currency: 'USD' } + }); + expect(result).toEqual(expectedResult); + }); + + it('should fallback to local logic if a2aService is not provided', async () => { + const localAgentService = new AgentService(mockDb); + const fromAgent = { + id: 'agent1', + config: { limits: { perTransaction: 500 }, authorizedCounterparties: ['agent2'] } + }; + const toAgent = { id: 'agent2' }; + + mockDb.findAgentById.mockResolvedValueOnce(fromAgent); + mockDb.findAgentById.mockResolvedValueOnce(toAgent); + + const result = await localAgentService.performA2ATransfer({ + fromAgentId: 'agent1', + toAgentId: 'agent2', + amount: 100, + currency: 'USD' + }); + + expect(result.success).toBe(true); + expect(result.amount).toBe(100); + }); + + it('should throw error in fallback if amount exceeds limit', async () => { + const localAgentService = new AgentService(mockDb); + const fromAgent = { + id: 'agent1', + config: { limits: { perTransaction: 50 } } + }; + + mockDb.findAgentById.mockResolvedValueOnce(fromAgent); + mockDb.findAgentById.mockResolvedValueOnce({ id: 'agent2' }); + + await expect(localAgentService.performA2ATransfer({ + fromAgentId: 'agent1', + toAgentId: 'agent2', + amount: 100, + currency: 'USD' + })).rejects.toThrow('Zero Trust Validation Failed: Amount 100 exceeds agent per-transaction limit of 50'); + }); + }); });