Detach memory and update config
Removed the memory_subdir attribute from AgentConfig and related settings, transitioning to a project-based memory isolation approach. Updated the memory plugin configuration to default to an empty string for agent_memory_subdir. Enhanced the get_context_memory_subdir function to support project-specific memory directories. Removed the project-edit-memory component and adjusted the UI to reflect these changes. Added a new memory configuration file for better management of memory settings.
Alessandro committed
Feb 21, 2026 at 10:41 UTC
505128c384c86f06809fb5fcde0e412f4cef1a94
14 files changed
+54
-158
agent.py
-1
@@ -303,7 +303,6 @@ class AgentConfig:
303
browser_model: models.ModelConfig
304
mcp_servers: str
305
profile: str = ""
306
- memory_subdir: str = ""
306
knowledge_subdirs: list[str] = field(default_factory=lambda: ["default", "custom"])
307
browser_http_headers: dict[str, str] = field(
308
default_factory=dict
initialize.py
-1
@@ -78,7 +78,6 @@ def initialize_agent(override_settings: dict | None = None):
78
embeddings_model=embedding_llm,
79
browser_model=browser_llm,
80
profile=current_settings["agent_profile"],
81
- memory_subdir=current_settings["agent_memory_subdir"],
81
knowledge_subdirs=[current_settings["agent_knowledge_subdir"], "default"],
82
mcp_servers=current_settings["mcp_servers"],
83
browser_http_headers=current_settings["browser_http_headers"],
plugins/memory/config.default.json
+1
-1
@@ -13,5 +13,5 @@
13
"memory_memorize_enabled": true,
14
"memory_memorize_consolidation": true,
15
"memory_memorize_replace_threshold": 0.9,
16
- "agent_memory_subdir": "default"
16
+ "agent_memory_subdir": ""
17
}
plugins/memory/config.json
new
+18
@@ -0,0 +1,18 @@
1
+{
2
+ "project_memory_isolation": true,
3
+ "memory_recall_enabled": true,
4
+ "memory_recall_delayed": false,
5
+ "memory_recall_interval": 3,
6
+ "memory_recall_history_len": 10000,
7
+ "memory_recall_memories_max_search": 12,
8
+ "memory_recall_solutions_max_search": 8,
9
+ "memory_recall_memories_max_result": 5,
10
+ "memory_recall_solutions_max_result": 3,
11
+ "memory_recall_similarity_threshold": 0.7,
12
+ "memory_recall_query_prep": false,
13
+ "memory_recall_post_filter": false,
14
+ "memory_memorize_enabled": true,
15
+ "memory_memorize_consolidation": true,
16
+ "memory_memorize_replace_threshold": 0.9,
17
+ "agent_memory_subdir": "default"
18
+}
plugins/memory/helpers/memory.py
+11
-12
@@ -23,7 +23,7 @@ import os, json
23
import numpy as np
24
25
from python.helpers.print_style import PrintStyle
26
-from python.helpers import files, plugins
26
+from python.helpers import files, plugins, projects
27
from langchain_core.documents import Document
28
from . import knowledge_import
29
from python.helpers.log import Log, LogItem
@@ -520,17 +520,16 @@ def get_agent_memory_subdir(agent: Agent) -> str:
520
521
522
def get_context_memory_subdir(context: AgentContext) -> str:
523
- # if project is active, use project memory subdir
524
- from python.helpers.projects import (
525
- get_context_memory_subdir as get_project_memory_subdir,
526
- )
527
-
528
- memory_subdir = get_project_memory_subdir(context)
529
- if memory_subdir:
530
- return memory_subdir
531
-
532
- # no project, regular memory subdir
533
- return plugins.get_plugin_config("memory").get("agent_memory_subdir", "default")
523
+ config = plugins.get_plugin_config("memory")
524
+
525
+ # Check if project isolation is enabled and we are in a project
526
+ if config.get("project_memory_isolation", True):
527
+ project_name = projects.get_context_project_name(context)
528
+ if project_name:
529
+ return "projects/" + project_name
530
+
531
+ # Fallback to configured subdir or default
532
+ return config.get("agent_memory_subdir", "") or "default"
533
534
535
def get_existing_memory_subdirs() -> list[str]:
plugins/memory/webui/config.html
+17
-3
@@ -13,16 +13,30 @@
13
context awareness.
14
</div>
15
16
+ <div class="field" x-show="$store.pluginSettings.projectName">
17
+ <div class="field-label">
18
+ <div class="field-title">Project Isolation</div>
19
+ <div class="field-description">
20
+ If enabled, this project uses its own memory folder (projects/NAME). If disabled, it shares the global memory (default or custom subdir).
21
+ </div>
22
+ </div>
23
+ <div class="field-control">
24
+ <label class="toggle">
25
+ <input type="checkbox" x-model="$store.pluginSettings.settings.project_memory_isolation" />
26
+ <span class="toggler"></span>
27
+ </label>
28
+ </div>
29
+ </div>
30
+
31
<div class="field">
32
<div class="field-label">
33
<div class="field-title">Memory Subdirectory</div>
34
<div class="field-description">
20
- Subdirectory of /memory folder to use for agent memory storage. Used to separate memory
21
- storage between different instances.
35
+ Subdirectory of /memory folder.
36
</div>
37
</div>
38
<div class="field-control">
25
- <input type="text" x-model="$store.pluginSettings.settings.agent_memory_subdir" />
39
+ <input type="text" x-model="$store.pluginSettings.settings.agent_memory_subdir" placeholder="Auto (Project-isolated)" />
40
</div>
41
</div>
42
python/extensions/agent_init/_15_load_profile_settings.py
-1
@@ -38,7 +38,6 @@ class LoadProfileSettings(Extension):
38
39
for override_key, config_attr in (
40
("agent_profile", "profile"),
41
- ("agent_memory_subdir", "memory_subdir"),
41
("mcp_servers", "mcp_servers"),
42
("browser_http_headers", "browser_http_headers"),
43
):
python/helpers/projects.py
-15
@@ -34,9 +34,6 @@ class BasicProjectData(TypedDict):
34
instructions: str
35
color: str
36
git_url: str
37
- memory: Literal[
38
- "own", "global"
39
- ] # in the future we can add cutom and point to another existing folder
37
file_structure: FileStructureInjectionSettings
38
39
class GitStatusData(TypedDict, total=False):
@@ -158,7 +155,6 @@ def _normalizeBasicData(data: BasicProjectData) -> BasicProjectData:
155
"instructions": data.get("instructions", ""),
156
"color": data.get("color", ""),
157
"git_url": data.get("git_url", ""),
161
- "memory": data.get("memory", "own"),
158
"file_structure": data.get(
159
"file_structure",
160
_default_file_structure_settings(),
@@ -179,7 +175,6 @@ def _normalizeEditData(data: EditProjectData) -> EditProjectData:
175
"instruction_files_count": data.get("instruction_files_count", 0),
176
"knowledge_files_count": data.get("knowledge_files_count", 0),
177
"secrets": data.get("secrets", ""),
182
- "memory": data.get("memory", "own"),
178
"file_structure": data.get(
179
"file_structure",
180
_default_file_structure_settings(),
@@ -463,16 +458,6 @@ def save_project_secrets(name: str, secrets: str):
458
secrets_manager.save_secrets_with_merge(secrets)
459
460
466
-def get_context_memory_subdir(context: "AgentContext") -> str | None:
467
- # if a project is active and has memory isolation set, return the project memory subdir
468
- project_name = get_context_project_name(context)
469
- if project_name:
470
- project_data = load_basic_project_data(project_name)
471
- if project_data["memory"] == "own":
472
- return "projects/" + project_name
473
- return None # no memory override
474
-
475
-
461
def create_project_meta_folders(name: str):
462
# create instructions folder
463
files.create_dir(get_project_meta(name, PROJECT_INSTRUCTIONS_DIR))
python/helpers/settings.py
-31
@@ -92,7 +92,6 @@ class Settings(TypedDict):
92
browser_http_headers: dict[str, Any]
93
94
agent_profile: str
95
- agent_memory_subdir: str
95
agent_knowledge_subdir: str
96
97
workdir_path: str
@@ -103,21 +102,6 @@ class Settings(TypedDict):
102
workdir_max_lines: int
103
workdir_gitignore: str
104
106
- memory_recall_enabled: bool
107
- memory_recall_delayed: bool
108
- memory_recall_interval: int
109
- memory_recall_history_len: int
110
- memory_recall_memories_max_search: int
111
- memory_recall_solutions_max_search: int
112
- memory_recall_memories_max_result: int
113
- memory_recall_solutions_max_result: int
114
- memory_recall_similarity_threshold: float
115
- memory_recall_query_prep: bool
116
- memory_recall_post_filter: bool
117
- memory_memorize_enabled: bool
118
- memory_memorize_consolidation: bool
119
- memory_memorize_replace_threshold: float
120
-
105
api_keys: dict[str, str]
106
107
auth_login: str
@@ -547,26 +531,11 @@ def get_default_settings() -> Settings:
531
browser_model_rl_output=get_default_value("browser_model_rl_output", 0),
532
browser_model_kwargs=get_default_value("browser_model_kwargs", {}),
533
browser_http_headers=get_default_value("browser_http_headers", {}),
550
- memory_recall_enabled=get_default_value("memory_recall_enabled", True),
551
- memory_recall_delayed=get_default_value("memory_recall_delayed", False),
552
- memory_recall_interval=get_default_value("memory_recall_interval", 3),
553
- memory_recall_history_len=get_default_value("memory_recall_history_len", 10000),
554
- memory_recall_memories_max_search=get_default_value("memory_recall_memories_max_search", 12),
555
- memory_recall_solutions_max_search=get_default_value("memory_recall_solutions_max_search", 8),
556
- memory_recall_memories_max_result=get_default_value("memory_recall_memories_max_result", 5),
557
- memory_recall_solutions_max_result=get_default_value("memory_recall_solutions_max_result", 3),
558
- memory_recall_similarity_threshold=get_default_value("memory_recall_similarity_threshold", 0.7),
559
- memory_recall_query_prep=get_default_value("memory_recall_query_prep", False),
560
- memory_recall_post_filter=get_default_value("memory_recall_post_filter", False),
561
- memory_memorize_enabled=get_default_value("memory_memorize_enabled", True),
562
- memory_memorize_consolidation=get_default_value("memory_memorize_consolidation", True),
563
- memory_memorize_replace_threshold=get_default_value("memory_memorize_replace_threshold", 0.9),
534
api_keys={},
535
auth_login="",
536
auth_password="",
537
root_password="",
538
agent_profile=get_default_value("agent_profile", "agent0"),
569
- agent_memory_subdir=get_default_value("agent_memory_subdir", "default"),
539
agent_knowledge_subdir=get_default_value("agent_knowledge_subdir", "custom"),
540
workdir_path=get_default_value("workdir_path", files.get_abs_path_dockerized("usr/workdir")),
541
workdir_show=get_default_value("workdir_show", True),
webui/components/plugins/list/pluginListStore.js
+1
@@ -14,6 +14,7 @@ const model = {
14
activeTab: "custom",
15
16
async init() {
17
+ this.loading = false;
18
await this.setTab('custom');
19
if (this.plugins.length === 0) {
20
await this.setTab('builtin');
webui/components/plugins/plugin-settings-store.js
+5
@@ -301,6 +301,11 @@ const model = {
301
this.loadedProjectName = "";
302
this.loadedAgentProfile = "";
303
this.error = null;
304
+ this.isLoading = false;
305
+ this.isSaving = false;
306
+ this.isListingConfigs = false;
307
+ this.configsError = null;
308
+ this.configs = [];
309
},
310
311
// Reactive URL for the plugin's settings component (used with x-html injection)
webui/components/projects/project-edit-memory.html
deleted
-81
@@ -1,81 +0,0 @@
1
-<html>
2
-
3
-<head>
4
- <title>Create a new project</title>
5
- <script type="module">
6
- import { store } from "/components/projects/projects-store.js";
7
- </script>
8
-</head>
9
-
10
-<body>
11
- <div x-data>
12
- <template x-if="$store.projects && $store.projects.selectedProject">
13
- <div>
14
-
15
- <div class="projects-setting-row" style="padding: 1rem 0;">
16
- <div class="projects-setting-text">
17
- <label class="projects-form-label">Project-specific memory</label>
18
- <span class="projects-form-description">When turned on, the agent's memory while working on this
19
- project will be isolated from the global memory. The memory will be stored in <strong><span
20
- x-text="$store.projects.getSelectedAbsPath('.a0proj','memory')"></span></strong>.
21
- <br>
22
- Project-specific knowledge files can be used only when project-specific memory is turned on.</span>
23
- </div>
24
- <div class="projects-setting-control">
25
- <label class="toggle">
26
- <input type="checkbox" x-model="$store.projects.selectedProject._ownMemory">
27
- <span class="toggler"></span>
28
- </label>
29
- </div>
30
- </div>
31
-
32
- <div class="projects-form-group" x-show="$store.projects.selectedProject._ownMemory">
33
- <label class="projects-form-label">Knowledge files</label>
34
- <div class="projects-input-with-button-wrapper">
35
- <span class="projects-form-description">Additional knowledge files in <strong><span
36
- x-text="$store.projects.getSelectedAbsPath('.a0proj','knowledge')"></span></strong>.
37
- Knowledge files are imported into memory and are used when relevant based on the context of the conversation.</span>
38
- <span class="projects-form-description">Currently there are <strong><span
39
- x-text="$store.projects.selectedProject.knowledge_files_count || 0"></span></strong>
40
- knowledge files.</span>
41
- <button class="button icon-button" x-on:click="$store.projects.browseKnowledgeFiles()">
42
- <span class="icon material-symbols-outlined">folder</span>
43
- <span>Browse</span>
44
- </button>
45
- </div>
46
- </div>
47
-
48
- <style>
49
- .projects-setting-row {
50
- display: flex;
51
- align-items: center;
52
- justify-content: space-between;
53
- gap: 2rem;
54
- }
55
-
56
- .projects-setting-text {
57
- flex-grow: 1;
58
- flex-shrink: 1;
59
- min-width: 0;
60
- }
61
-
62
- .projects-setting-control {
63
- flex-grow: 0;
64
- flex-shrink: 0;
65
- }
66
-
67
- .projects-form-label {
68
- display: block;
69
- }
70
- </style>
71
-
72
- </div>
73
-
74
-
75
- </template>
76
- </div>
77
-</body>
78
-<style>
79
-</style>
80
-
81
-</html>
\ No newline at end of file
webui/components/projects/project-edit.html
-8
@@ -42,14 +42,6 @@
42
</x-component>
43
</div>
44
45
- <div class="project-detail">
46
- <div class="project-detail-header">
47
- <span class="projects-project-card-title">Memory</span>
48
- </div>
49
- <x-component path="projects/project-edit-memory.html">
50
- </x-component>
51
- </div>
52
-
45
<div class="project-detail">
46
<div class="project-detail-header">
47
<span class="projects-project-card-title">File structure</span>
webui/components/projects/projects-store.js
+1
-4
@@ -297,7 +297,7 @@ const model = {
297
const response = await api.callJsonApi("projects", {
298
action: "delete",
299
name: name,
300
- });
300
+ });
301
if (response.ok) {
302
notifications.toastFrontendSuccess(
303
"Project deleted successfully",
@@ -350,7 +350,6 @@ const model = {
350
// prepare data
351
const data = {
352
...this.selectedProject,
353
- memory: this.selectedProject._ownMemory ? "own" : "global",
353
};
354
// remove internal fields
355
for (const kvp of Object.entries(data))
@@ -402,7 +401,6 @@ const model = {
401
_meta: {
402
creating: true,
403
},
405
- _ownMemory: true,
404
_cloning: false,
405
name: ``,
406
title: `Project #${this.projectList.length + 1}`,
@@ -425,7 +423,6 @@ const model = {
423
creating: false,
424
},
425
...projectData,
428
- _ownMemory: projectData.memory == "own",
426
};
427
},
428