From b1a56a143b3d2e01c8e819571f254eec8cf12458 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Thu, 9 Jul 2026 11:29:08 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix:=20Correct=20scope=20+=20too?= =?UTF-8?q?l=20visibility=20for=20lazily=20provisioned=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses Codex P1 findings on the lazy provisioning path: - Scope per file like the direct upload path: current-message chat attachments (context=message_attachment) provision to the user's code sandbox / unscoped vector index (entity_id undefined); only agent setup files use entity_id=agentId. Previously every lazily provisioned file was agent-scoped, so a user's chat attachment landed in the agent sandbox and file_search queries (unscoped for fromAgent=false) missed the agent-scoped embedding. - Surface provisioned files to the tool loaded immediately after by adding them to ctx.tool_resources..files, which primeCodeFiles/primeFiles read. Before, a freshly provisioned unified upload was invisible to the first code/file_search call even though provisioning had completed. --- .../services/Endpoints/agents/initialize.js | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/api/server/services/Endpoints/agents/initialize.js b/api/server/services/Endpoints/agents/initialize.js index 39faef6814..a40c0d5e71 100644 --- a/api/server/services/Endpoints/agents/initialize.js +++ b/api/server/services/Endpoints/agents/initialize.js @@ -17,9 +17,11 @@ const { buildAgentContextAttachmentsByAgentId, } = require('@librechat/api'); const { + FileContext, Permissions, ResourceType, EModelEndpoint, + EToolResources, PermissionBits, PermissionTypes, MAX_SUBAGENT_DEPTH, @@ -275,6 +277,29 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => { return; } + /** Current-message chat attachments stay in the user's sandbox / unscoped vector + * index (matching the direct message_file upload path); only agent setup files + * are scoped to the agent. */ + const entityIdForFile = (file) => + file.context === FileContext.message_attachment ? undefined : agentId; + + /** Surface a just-provisioned file to the tool loaded immediately after: the code + * and file_search primers read `tool_resources..files`. */ + if (!ctx.tool_resources) { + ctx.tool_resources = {}; + } + const addProvisionedFile = (file, resourceType) => { + if (!file.file_id) { + return; + } + const resource = ctx.tool_resources[resourceType] ?? {}; + const files = resource.files ? [...resource.files] : []; + if (!files.some((existing) => existing.file_id === file.file_id)) { + files.push(file); + } + ctx.tool_resources[resourceType] = { ...resource, files }; + }; + /** @type {import('@librechat/api').TFileUpdate[]} */ const pendingUpdates = []; @@ -284,9 +309,10 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => { const { codeEnvRef, fileUpdate } = await provisionToCodeEnv({ req, file, - entity_id: agentId, + entity_id: entityIdForFile(file), }); file.metadata = { ...file.metadata, codeEnvRef }; + addProvisionedFile(file, EToolResources.execute_code); pendingUpdates.push(fileUpdate); }), ); @@ -301,9 +327,14 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => { if (needsSearch && provisionState.vectorDBFiles.length > 0) { const results = await Promise.allSettled( provisionState.vectorDBFiles.map(async (file) => { - const result = await provisionToVectorDB({ req, file, entity_id: agentId }); + const result = await provisionToVectorDB({ + req, + file, + entity_id: entityIdForFile(file), + }); if (result.embedded) { file.embedded = true; + addProvisionedFile(file, EToolResources.file_search); if (result.fileUpdate) { pendingUpdates.push(result.fileUpdate); }