@@ -343,6 +343,35 @@ async def attach_source(
343343 return agent_state
344344
345345
346+ @router .patch ("/{agent_id}/folders/attach/{folder_id}" , response_model = AgentState , operation_id = "attach_folder_to_agent" )
347+ async def attach_folder_to_agent (
348+ agent_id : str ,
349+ folder_id : str ,
350+ server : "SyncServer" = Depends (get_letta_server ),
351+ actor_id : str | None = Header (None , alias = "user_id" ),
352+ ):
353+ """
354+ Attach a folder to an agent.
355+ """
356+ actor = await server .user_manager .get_actor_or_default_async (actor_id = actor_id )
357+ agent_state = await server .agent_manager .attach_source_async (agent_id = agent_id , source_id = folder_id , actor = actor )
358+
359+ # Check if the agent is missing any files tools
360+ agent_state = await server .agent_manager .attach_missing_files_tools_async (agent_state = agent_state , actor = actor )
361+
362+ files = await server .file_manager .list_files (folder_id , actor , include_content = True )
363+ if files :
364+ await server .agent_manager .insert_files_into_context_window (agent_state = agent_state , file_metadata_with_content = files , actor = actor )
365+
366+ if agent_state .enable_sleeptime :
367+ source = await server .source_manager .get_source_by_id (source_id = folder_id )
368+ safe_create_task (
369+ server .sleeptime_document_ingest_async (agent_state , source , actor ), logger = logger , label = "sleeptime_document_ingest_async"
370+ )
371+
372+ return agent_state
373+
374+
346375@router .patch ("/{agent_id}/sources/detach/{source_id}" , response_model = AgentState , operation_id = "detach_source_from_agent" )
347376async def detach_source (
348377 agent_id : str ,
@@ -373,6 +402,36 @@ async def detach_source(
373402 return agent_state
374403
375404
405+ @router .patch ("/{agent_id}/folders/detach/{folder_id}" , response_model = AgentState , operation_id = "detach_folder_from_agent" )
406+ async def detach_folder_from_agent (
407+ agent_id : str ,
408+ folder_id : str ,
409+ server : "SyncServer" = Depends (get_letta_server ),
410+ actor_id : str | None = Header (None , alias = "user_id" ),
411+ ):
412+ """
413+ Detach a folder from an agent.
414+ """
415+ actor = await server .user_manager .get_actor_or_default_async (actor_id = actor_id )
416+ agent_state = await server .agent_manager .detach_source_async (agent_id = agent_id , source_id = folder_id , actor = actor )
417+
418+ if not agent_state .sources :
419+ agent_state = await server .agent_manager .detach_all_files_tools_async (agent_state = agent_state , actor = actor )
420+
421+ files = await server .file_manager .list_files (folder_id , actor )
422+ file_ids = [f .id for f in files ]
423+ await server .remove_files_from_context_window (agent_state = agent_state , file_ids = file_ids , actor = actor )
424+
425+ if agent_state .enable_sleeptime :
426+ try :
427+ source = await server .source_manager .get_source_by_id (source_id = folder_id )
428+ block = await server .agent_manager .get_block_with_label_async (agent_id = agent_state .id , block_label = source .name , actor = actor )
429+ await server .block_manager .delete_block_async (block .id , actor )
430+ except :
431+ pass
432+ return agent_state
433+
434+
376435@router .patch ("/{agent_id}/files/close-all" , response_model = List [str ], operation_id = "close_all_open_files" )
377436async def close_all_open_files (
378437 agent_id : str ,
@@ -516,6 +575,19 @@ async def list_agent_sources(
516575 return await server .agent_manager .list_attached_sources_async (agent_id = agent_id , actor = actor )
517576
518577
578+ @router .get ("/{agent_id}/folders" , response_model = list [Source ], operation_id = "list_agent_folders" )
579+ async def list_agent_folders (
580+ agent_id : str ,
581+ server : "SyncServer" = Depends (get_letta_server ),
582+ actor_id : str | None = Header (None , alias = "user_id" ), # Extract user_id from header, default to None if not present
583+ ):
584+ """
585+ Get the folders associated with an agent.
586+ """
587+ actor = await server .user_manager .get_actor_or_default_async (actor_id = actor_id )
588+ return await server .agent_manager .list_attached_sources_async (agent_id = agent_id , actor = actor )
589+
590+
519591# TODO: remove? can also get with agent blocks
520592@router .get ("/{agent_id}/core-memory" , response_model = Memory , operation_id = "retrieve_agent_memory" )
521593async def retrieve_agent_memory (
0 commit comments