diff --git a/surfsense_backend/app/agents/new_chat/middleware/memory_injection.py b/surfsense_backend/app/agents/new_chat/middleware/memory_injection.py
index e39a78633..782c98ce3 100644
--- a/surfsense_backend/app/agents/new_chat/middleware/memory_injection.py
+++ b/surfsense_backend/app/agents/new_chat/middleware/memory_injection.py
@@ -19,8 +19,8 @@ from langgraph.runtime import Runtime
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
-from app.db import ChatVisibility, SearchSpace, User, shielded_async_session
from app.agents.new_chat.tools.update_memory import MEMORY_HARD_LIMIT
+from app.db import ChatVisibility, SearchSpace, User, shielded_async_session
logger = logging.getLogger(__name__)
@@ -96,10 +96,9 @@ class MemoryInjectionMiddleware(AgentMiddleware): # type: ignore[type-arg]
) -> tuple[str | None, bool]:
"""Return (memory_content, is_persisted).
- When the user has saved memory in the database, ``is_persisted`` is
- ``True``. When we fall back to a seed (first-name only), it is
- ``False`` — the system prompt instructs the LLM to call
- ``update_memory`` once to persist it.
+ When the user has no saved memory but has a display name, a seed
+ document is created and **persisted to the database immediately**
+ so the LLM doesn't need to make a tool call to save it.
"""
try:
result = await session.execute(
@@ -118,7 +117,15 @@ class MemoryInjectionMiddleware(AgentMiddleware): # type: ignore[type-arg]
if display_name:
first_name = display_name.split()[0]
- return f"## About the user\n- Name: {first_name}", False
+ seed = f"## About the user\n- Name: {first_name}"
+ await session.execute(
+ User.__table__.update()
+ .where(User.id == self.user_id)
+ .values(memory_md=seed)
+ )
+ await session.commit()
+ logger.info("Auto-persisted memory seed for user %s", self.user_id)
+ return seed, True
return None, True
except Exception:
diff --git a/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/MemoryContent.tsx b/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/MemoryContent.tsx
index 007f45feb..cee450788 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/MemoryContent.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/MemoryContent.tsx
@@ -134,10 +134,10 @@ export function MemoryContent() {
variant="outline"
onClick={handleSave}
disabled={saving || !hasChanges || isOverLimit}
- className="gap-2 bg-white text-black hover:bg-neutral-100 dark:bg-white dark:text-black dark:hover:bg-neutral-200"
- >
- {saving && }
- Save
+ className="relative gap-2 bg-white text-black hover:bg-neutral-100 dark:bg-white dark:text-black dark:hover:bg-neutral-200 items-center justify-center"
+ >
+ Save
+ {saving && }
diff --git a/surfsense_web/components/settings/team-memory-manager.tsx b/surfsense_web/components/settings/team-memory-manager.tsx
index 8fc33efcf..4162b272c 100644
--- a/surfsense_web/components/settings/team-memory-manager.tsx
+++ b/surfsense_web/components/settings/team-memory-manager.tsx
@@ -140,10 +140,10 @@ export function TeamMemoryManager({ searchSpaceId }: TeamMemoryManagerProps) {
variant="outline"
onClick={handleSave}
disabled={saving || !hasChanges || isOverLimit}
- className="gap-2 bg-white text-black hover:bg-neutral-100 dark:bg-white dark:text-black dark:hover:bg-neutral-200"
- >
- {saving && }
- Save
+ className="relative gap-2 bg-white text-black hover:bg-neutral-100 dark:bg-white dark:text-black dark:hover:bg-neutral-200 items-center justify-center"
+ >
+ Save
+ {saving && }