diff --git a/api/server/controllers/agents/client.js b/api/server/controllers/agents/client.js index 7b050256f0..951c10742d 100644 --- a/api/server/controllers/agents/client.js +++ b/api/server/controllers/agents/client.js @@ -272,10 +272,17 @@ class AgentClient extends BaseClient { })) : []), ]; + const sharedRunAttachmentIds = new Set(); if (this.options.attachments) { const attachments = await this.options.attachments; const latestMessage = orderedMessages[orderedMessages.length - 1]; + for (const file of attachments) { + if (file?.file_id) { + sharedRunAttachmentIds.add(file.file_id); + } + } + if (this.message_file_map) { this.message_file_map[latestMessage.messageId] = attachments; } else { @@ -417,7 +424,9 @@ class AgentClient extends BaseClient { const agentScopedContextEntries = await Promise.all( allAgents.map(async ({ agentId }) => { - const attachments = getAgentContextAttachments(agentId); + const attachments = getAgentContextAttachments(agentId).filter( + (file) => !file?.file_id || !sharedRunAttachmentIds.has(file.file_id), + ); if (!attachments || attachments.length === 0) { return [agentId, '']; } diff --git a/api/server/controllers/agents/client.test.js b/api/server/controllers/agents/client.test.js index 4aea6beeee..873cba9c58 100644 --- a/api/server/controllers/agents/client.test.js +++ b/api/server/controllers/agents/client.test.js @@ -1551,6 +1551,33 @@ describe('AgentClient - titleConvo', () => { expect(handoffAgent.additional_instructions).not.toContain('Primary private context'); }); + it('does not duplicate a file that is both request context and scoped context', async () => { + const sharedFile = makeTextFile('shared-file', 'shared.txt', 'Shared duplicate context'); + + client.options.attachments = [sharedFile]; + client.options.agentContextAttachmentsByAgentId = new Map([['primary-agent', [sharedFile]]]); + client.agentConfigs = new Map(); + + await client.buildMessages( + [ + { + messageId: 'msg-1', + parentMessageId: null, + sender: 'User', + text: 'Use the available context.', + isCreatedByUser: true, + }, + ], + 'msg-1', + {}, + ); + + const occurrences = ( + mockAgent.additional_instructions.match(/Shared duplicate context/g) ?? [] + ).length; + expect(occurrences).toBe(1); + }); + it('keeps direct chats with context-doc agents working without request attachments', async () => { const primaryContext = makeTextFile( 'primary-context',