diff --git a/apps/server/src/services/agentDocuments/index.ts b/apps/server/src/services/agentDocuments/index.ts index c7cc6d0078..4360bfa82b 100644 --- a/apps/server/src/services/agentDocuments/index.ts +++ b/apps/server/src/services/agentDocuments/index.ts @@ -616,6 +616,8 @@ export class AgentDocumentsService { const filtered = sourceType && sourceType !== 'all' ? docs.filter((d) => d.sourceType === sourceType) : docs; return filtered.map((d) => ({ + ...deriveAgentDocumentFields(d), + description: d.description, documentId: d.documentId, fileType: d.fileType, filename: d.filename, @@ -623,7 +625,9 @@ export class AgentDocumentsService { loadPosition: d.policy?.context?.position, parentId: d.parentId, sourceType: d.sourceType, + templateId: d.templateId, title: d.title, + updatedAt: d.updatedAt, })); } @@ -642,6 +646,8 @@ export class AgentDocumentsService { .filter((doc): doc is AgentDocumentWithRules => Boolean(doc)) .filter((doc) => !sourceType || sourceType === 'all' || doc.sourceType === sourceType) .map((doc) => ({ + ...deriveAgentDocumentFields(doc), + description: doc.description, documentId: doc.documentId, fileType: doc.fileType, filename: doc.filename, @@ -649,7 +655,9 @@ export class AgentDocumentsService { loadPosition: doc.policy?.context?.position, parentId: doc.parentId, sourceType: doc.sourceType, + templateId: doc.templateId, title: doc.title, + updatedAt: doc.updatedAt, })); } diff --git a/src/features/AgentDocumentsExplorer/types.ts b/src/features/AgentDocumentsExplorer/types.ts index 675676efd7..39cf91d5f2 100644 --- a/src/features/AgentDocumentsExplorer/types.ts +++ b/src/features/AgentDocumentsExplorer/types.ts @@ -1,7 +1,7 @@ import type { agentDocumentService } from '@/services/agentDocument'; export type AgentDocumentItem = Awaited< - ReturnType + ReturnType >[number]; export const PENDING_ID_PREFIX = 'pending:'; diff --git a/src/features/AgentDocumentsExplorer/utils/pendingDocument.ts b/src/features/AgentDocumentsExplorer/utils/pendingDocument.ts index 841c0ec2ea..9a14d8d22f 100644 --- a/src/features/AgentDocumentsExplorer/utils/pendingDocument.ts +++ b/src/features/AgentDocumentsExplorer/utils/pendingDocument.ts @@ -24,39 +24,20 @@ export const makePendingDocument = ({ const id = `${PENDING_ID_PREFIX}${nanoid(10)}`; const now = new Date(); return { - accessPublic: 0, - accessSelf: 1, - accessShared: 0, - agentId, category: AGENT_DOCUMENT_CATEGORY, - content: '', - createdAt: now, - deletedAt: null, - deletedByAgentId: null, - deletedByUserId: null, - deleteReason: null, description: null, documentId: id, - editorData: null, filename: title, fileType: isFolder ? CUSTOM_FOLDER_FILE_TYPE : CUSTOM_DOCUMENT_FILE_TYPE, id, isFolder, isSkillBundle: false, isSkillIndex: false, - loadRules: {} as AgentDocumentItem['loadRules'], - metadata: null, + loadPosition: undefined, parentId, - policy: null, - policyLoad: 'always' as AgentDocumentItem['policyLoad'], - policyLoadFormat: 'raw' as AgentDocumentItem['policyLoadFormat'], - policyLoadPosition: '', - policyLoadRule: '', - source: null, sourceType: AGENT_DOCUMENT_SOURCE_TYPE, templateId: null, title, updatedAt: now, - userId: '', }; }; diff --git a/src/routes/(main)/agent/features/Conversation/WorkingSidebar/ResourcesSection/AgentDocumentsGroup.tsx b/src/routes/(main)/agent/features/Conversation/WorkingSidebar/ResourcesSection/AgentDocumentsGroup.tsx index 94ce70f239..2109a665db 100644 --- a/src/routes/(main)/agent/features/Conversation/WorkingSidebar/ResourcesSection/AgentDocumentsGroup.tsx +++ b/src/routes/(main)/agent/features/Conversation/WorkingSidebar/ResourcesSection/AgentDocumentsGroup.tsx @@ -105,7 +105,7 @@ const FILTER_OPTIONS = [ { labelKey: 'workingPanel.resources.filter.web', value: 'web' }, ] as const satisfies readonly { labelKey: string; value: ResourceFilter }[]; -type AgentDocumentListItem = Awaited>[number]; +type AgentDocumentListItem = Awaited>[number]; interface DocumentItemProps { agentId: string; @@ -286,7 +286,7 @@ const AgentDocumentsGroup = memo( isLoading, mutate, } = useClientDataSWR(agentId ? agentDocumentSWRKeys.documentsList(agentId) : null, () => - agentDocumentService.getDocuments({ agentId: agentId! }), + agentDocumentService.listDocuments({ agentId: agentId! }), ); const webData = useMemo( diff --git a/src/services/agentDocument.ts b/src/services/agentDocument.ts index 8fe03f4314..d8a292d851 100644 --- a/src/services/agentDocument.ts +++ b/src/services/agentDocument.ts @@ -292,3 +292,7 @@ export const resolveAgentDocumentsContext = async (params: { }; export const agentDocumentService = new AgentDocumentService(); + +export type AgentDocumentListItem = Awaited< + ReturnType +>[number]; diff --git a/src/store/agent/slices/agent/action.test.ts b/src/store/agent/slices/agent/action.test.ts index ce4dad9cf9..712af2599b 100644 --- a/src/store/agent/slices/agent/action.test.ts +++ b/src/store/agent/slices/agent/action.test.ts @@ -3,7 +3,7 @@ import { act, renderHook, waitFor } from '@testing-library/react'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { agentService } from '@/services/agent'; -import { resolveAgentDocumentsContext } from '@/services/agentDocument'; +import { agentDocumentService } from '@/services/agentDocument'; import { type LobeAgentConfig } from '@/types/agent'; import { withSWR } from '~test-utils'; @@ -26,8 +26,12 @@ vi.mock('@/services/agent', () => ({ })); vi.mock('@/services/agentDocument', () => ({ + agentDocumentService: { + listDocuments: vi.fn(), + }, agentDocumentSWRKeys: { documents: (agentId: string) => ['agent-documents', agentId] as const, + documentsList: (agentId: string) => ['agent-documents-list', agentId] as const, }, resolveAgentDocumentsContext: vi.fn(), })); @@ -96,37 +100,27 @@ describe('AgentSlice Actions', () => { }); describe('useFetchAgentDocuments', () => { - it('should sync fetched agent documents into store cache', async () => { - vi.mocked(resolveAgentDocumentsContext).mockResolvedValue([ + it('should fetch agent documents via listDocuments', async () => { + const docs = [ { - content: 'setup steps', + documentId: 'doc-1', filename: 'setup.md', id: 'doc-1', - loadRules: {}, - policyId: null, - policyLoadFormat: undefined, title: 'Setup', }, - ]); + ]; + vi.mocked(agentDocumentService.listDocuments).mockResolvedValue(docs as any); - const { result } = renderHook(() => useAgentStore(), { wrapper: withSWR }); + const store = renderHook(() => useAgentStore(), { wrapper: withSWR }); - renderHook(() => result.current.useFetchAgentDocuments('agent-1'), { wrapper: withSWR }); + const { result } = renderHook(() => store.result.current.useFetchAgentDocuments('agent-1'), { + wrapper: withSWR, + }); await waitFor(() => { - expect(result.current.agentDocumentsMap['agent-1']).toEqual([ - { - content: 'setup steps', - filename: 'setup.md', - id: 'doc-1', - loadRules: {}, - policyId: null, - policyLoadFormat: undefined, - title: 'Setup', - }, - ]); + expect(result.current.data).toEqual(docs); }); - expect(resolveAgentDocumentsContext).toHaveBeenCalledWith({ agentId: 'agent-1' }); + expect(agentDocumentService.listDocuments).toHaveBeenCalledWith({ agentId: 'agent-1' }); }); }); diff --git a/src/store/agent/slices/agent/action.ts b/src/store/agent/slices/agent/action.ts index 1dd5c7a044..c00a36088f 100644 --- a/src/store/agent/slices/agent/action.ts +++ b/src/store/agent/slices/agent/action.ts @@ -11,7 +11,12 @@ import { MESSAGE_CANCEL_FLAT } from '@/const/message'; import { mutate, useClientDataSWRWithSync } from '@/libs/swr'; import type { AvailableAgentItem, CreateAgentParams, CreateAgentResult } from '@/services/agent'; import { agentService, AVAILABLE_AGENTS_CONTEXT_QUERY_LIMIT } from '@/services/agent'; -import { agentDocumentSWRKeys, resolveAgentDocumentsContext } from '@/services/agentDocument'; +import { + type AgentDocumentListItem, + agentDocumentService, + agentDocumentSWRKeys, + resolveAgentDocumentsContext, +} from '@/services/agentDocument'; import type { StoreSetter } from '@/store/types'; import { getUserStoreState } from '@/store/user'; import { userProfileSelectors } from '@/store/user/selectors'; @@ -371,16 +376,11 @@ export class AgentSliceActionImpl { ); }; - useFetchAgentDocuments = (agentId?: string | null): SWRResponse => { - return useClientDataSWRWithSync( - agentId ? agentDocumentSWRKeys.documents(agentId) : null, - async () => (await resolveAgentDocumentsContext({ agentId: agentId! })) ?? [], + useFetchAgentDocuments = (agentId?: string | null): SWRResponse => { + return useClientDataSWRWithSync( + agentId ? agentDocumentSWRKeys.documentsList(agentId) : null, + async () => agentDocumentService.listDocuments({ agentId: agentId! }), { - onData: (data) => { - if (!agentId) return; - - this.#syncAgentDocuments(agentId, data); - }, revalidateOnFocus: false, }, ); diff --git a/src/store/tool/slices/agentDocumentSkills/action.ts b/src/store/tool/slices/agentDocumentSkills/action.ts index bfdc920028..674ab07b41 100644 --- a/src/store/tool/slices/agentDocumentSkills/action.ts +++ b/src/store/tool/slices/agentDocumentSkills/action.ts @@ -1,7 +1,11 @@ import { buildAgentSkillIdentifier } from '@lobechat/const'; import useSWR, { type SWRResponse } from 'swr'; -import { agentDocumentService, agentDocumentSWRKeys } from '@/services/agentDocument'; +import { + type AgentDocumentListItem, + agentDocumentService, + agentDocumentSWRKeys, +} from '@/services/agentDocument'; import { type StoreSetter } from '@/store/types'; import { setNamespace } from '@/utils/storeDebug'; @@ -12,9 +16,7 @@ const n = setNamespace('agentDocumentSkills'); type Setter = StoreSetter; -const mapDocsToSkills = ( - docs: Awaited>, -): AgentDocumentSkillItem[] => +const mapDocsToSkills = (docs: AgentDocumentListItem[]): AgentDocumentSkillItem[] => docs .filter((doc) => doc.isSkillBundle) .map((doc) => ({ @@ -56,7 +58,7 @@ export class AgentDocumentSkillsActionImpl { } try { - const docs = await agentDocumentService.getDocuments({ agentId }); + const docs = await agentDocumentService.listDocuments({ agentId }); const items = mapDocsToSkills(docs); this.#set( { agentDocumentSkills: items, agentDocumentSkillsAgentId: agentId }, @@ -88,14 +90,15 @@ export class AgentDocumentSkillsActionImpl { /** * SWR-backed hook that fetches the agent's skill bundles and keeps the store * in sync. Shares the same SWR key as the working-sidebar panel so the panel - * fetch and the registry sync collapse into one network request. + * fetch and the registry sync collapse into one network request — both must + * fetch the same slim `listDocuments` payload to keep the cache shape stable. */ useFetchAgentDocumentSkills = ( agentId: string | undefined, - ): SWRResponse>> => - useSWR>>( + ): SWRResponse => + useSWR( agentId ? agentDocumentSWRKeys.documentsList(agentId) : null, - async () => agentDocumentService.getDocuments({ agentId: agentId! }), + async () => agentDocumentService.listDocuments({ agentId: agentId! }), { onSuccess: (docs) => { if (!agentId) return;