🔒 feat: View/Delete Shared Agent Files (#8419)

* 🔧 fix: Add localized message for delete operation not allowed

* refactor: improve file deletion operations ux

* feat: agent-based file access control and enhance file retrieval logic

* feat: implement agent-specific file retrieval

* feat: enhance agent file retrieval logic for authors and shared access

* ci: include userId and agentId in mockGetFiles call for OCR file retrieval
This commit is contained in:
Danny Avila 2025-07-12 01:52:46 -04:00 committed by GitHub
parent 6aa4bb5a4a
commit f1b29ffb45
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 1216 additions and 35 deletions

View file

@ -71,7 +71,12 @@ describe('primeResources', () => {
tool_resources,
});
expect(mockGetFiles).toHaveBeenCalledWith({ file_id: { $in: ['ocr-file-1'] } }, {}, {});
expect(mockGetFiles).toHaveBeenCalledWith(
{ file_id: { $in: ['ocr-file-1'] } },
{},
{},
{ userId: undefined, agentId: undefined },
);
expect(result.attachments).toEqual(mockOcrFiles);
expect(result.tool_resources).toEqual(tool_resources);
});

View file

@ -10,12 +10,14 @@ import type { Request as ServerRequest } from 'express';
* @param filter - MongoDB filter query for files
* @param _sortOptions - Sorting options (currently unused)
* @param selectFields - Field selection options
* @param options - Additional options including userId and agentId for access control
* @returns Promise resolving to array of files
*/
export type TGetFiles = (
filter: FilterQuery<IMongoFile>,
_sortOptions: ProjectionType<IMongoFile> | null | undefined,
selectFields: QueryOptions<IMongoFile> | null | undefined,
options?: { userId?: string; agentId?: string },
) => Promise<Array<TFile>>;
/**
@ -145,12 +147,14 @@ export const primeResources = async ({
requestFileSet,
attachments: _attachments,
tool_resources: _tool_resources,
agentId,
}: {
req: ServerRequest;
requestFileSet: Set<string>;
attachments: Promise<Array<TFile | null>> | undefined;
tool_resources: AgentToolResources | undefined;
getFiles: TGetFiles;
agentId?: string;
}): Promise<{
attachments: Array<TFile | undefined> | undefined;
tool_resources: AgentToolResources | undefined;
@ -205,6 +209,7 @@ export const primeResources = async ({
},
{},
{},
{ userId: req.user?.id, agentId },
);
for (const file of context) {

View file

@ -188,6 +188,12 @@ export const agents = ({ path = '', options }: { path?: string; options?: object
export const revertAgentVersion = (agent_id: string) => `${agents({ path: `${agent_id}/revert` })}`;
export const files = () => '/api/files';
export const fileUpload = () => '/api/files';
export const fileDelete = () => '/api/files';
export const fileDownload = (userId: string, fileId: string) =>
`/api/files/download/${userId}/${fileId}`;
export const fileConfig = () => '/api/files/config';
export const agentFiles = (agentId: string) => `/api/files/agent/${agentId}`;
export const images = () => `${files()}/images`;

View file

@ -318,6 +318,10 @@ export const getFiles = (): Promise<f.TFile[]> => {
return request.get(endpoints.files());
};
export const getAgentFiles = (agentId: string): Promise<f.TFile[]> => {
return request.get(endpoints.agentFiles(agentId));
};
export const getFileConfig = (): Promise<f.FileConfig> => {
return request.get(`${endpoints.files()}/config`);
};

View file

@ -50,6 +50,11 @@ export enum QueryKeys {
memories = 'memories',
}
// Dynamic query keys that require parameters
export const DynamicQueryKeys = {
agentFiles: (agentId: string) => ['agentFiles', agentId] as const,
} as const;
export enum MutationKeys {
fileUpload = 'fileUpload',
fileDelete = 'fileDelete',