xiaozuoassistant 0.1.51 → 0.1.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config.json +4 -2
- package/dist/client/assets/{browser-ponyfill-Bz0RS90l.js → browser-ponyfill-CdFOpSMG.js} +1 -1
- package/dist/client/assets/index-ZHPWrPl3.css +1 -0
- package/dist/client/assets/index-hkDzKLlN.js +163 -0
- package/dist/client/index.html +2 -2
- package/dist/client/locales/en/translation.json +13 -1
- package/dist/client/locales/zh/translation.json +13 -1
- package/dist/server/config/loader.js +10 -4
- package/dist/server/core/agents/runtime.js +5 -1
- package/dist/server/core/brain.js +5 -2
- package/dist/server/core/memories/manager.js +10 -5
- package/dist/server/core/memories/short-term.js +63 -2
- package/dist/server/core/scheduler.js +4 -2
- package/dist/server/index.js +28 -3
- package/package.json +1 -1
- package/public/locales/en/translation.json +13 -1
- package/public/locales/zh/translation.json +13 -1
- package/dist/client/assets/index-BwZQ_7Ty.js +0 -158
- package/dist/client/assets/index-C72E_nIr.css +0 -1
package/dist/client/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🍇</text></svg>" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>xiaozuoAssistant</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-hkDzKLlN.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-ZHPWrPl3.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|
|
@@ -27,8 +27,17 @@
|
|
|
27
27
|
"aliasHint": "Alias is shown in the list and header. Empty falls back to session ID.",
|
|
28
28
|
"workspaceLabel": "Workspace",
|
|
29
29
|
"workspaceHint": "File tools default to this workspace and are restricted to it.",
|
|
30
|
+
"contextLabel": "Context (optional)",
|
|
31
|
+
"contextPlaceholder": "E.g. project background, terminology, current conclusions, long-lived context...",
|
|
32
|
+
"contextHint": "This is persisted and can be used as session context in complex tasks.",
|
|
30
33
|
"selectWorkspace": "Select workspace",
|
|
31
|
-
"selectCurrentFolder": "Select current folder"
|
|
34
|
+
"selectCurrentFolder": "Select current folder",
|
|
35
|
+
"persistNow": "Persist now",
|
|
36
|
+
"persisting": "Persisting...",
|
|
37
|
+
"persistSuccess": "Persisted",
|
|
38
|
+
"delete": "Delete session",
|
|
39
|
+
"deleting": "Deleting...",
|
|
40
|
+
"deleteConfirm": "Are you sure you want to delete this session?"
|
|
32
41
|
},
|
|
33
42
|
"settings": {
|
|
34
43
|
"title": "Settings",
|
|
@@ -51,6 +60,9 @@
|
|
|
51
60
|
"scheduler": {
|
|
52
61
|
"memoryMaintenance": "Memory Maintenance",
|
|
53
62
|
"description": "Configure when the system should compress old memories and delete expired sessions.",
|
|
63
|
+
"sessionRetentionDays": "Session retention",
|
|
64
|
+
"sessionRetentionHint": "Sessions inactive for longer than this will be deleted during maintenance.",
|
|
65
|
+
"days": "days",
|
|
54
66
|
"cronSchedule": "Cron Schedule",
|
|
55
67
|
"cronHint": "Format: Minute Hour Day Month DayOfWeek (e.g., \"0 0 * * *\" for daily at midnight)",
|
|
56
68
|
"manualTrigger": "Manual Trigger",
|
|
@@ -27,8 +27,17 @@
|
|
|
27
27
|
"aliasHint": "列表与聊天头部将优先展示别名。留空将回退显示会话 ID。",
|
|
28
28
|
"workspaceLabel": "Workspace(工作目录)",
|
|
29
29
|
"workspaceHint": "文件相关工具默认以该目录为工作区,并限制访问范围。",
|
|
30
|
+
"contextLabel": "上下文数据(可选)",
|
|
31
|
+
"contextPlaceholder": "例如:项目背景、术语约定、当前阶段结论、需要长期保持的上下文...",
|
|
32
|
+
"contextHint": "该内容会持久化保存,并在复杂任务中作为会话上下文使用。",
|
|
30
33
|
"selectWorkspace": "选择工作目录",
|
|
31
|
-
"selectCurrentFolder": "选择当前目录"
|
|
34
|
+
"selectCurrentFolder": "选择当前目录",
|
|
35
|
+
"persistNow": "立即持久化",
|
|
36
|
+
"persisting": "持久化中...",
|
|
37
|
+
"persistSuccess": "已持久化",
|
|
38
|
+
"delete": "删除会话",
|
|
39
|
+
"deleting": "删除中...",
|
|
40
|
+
"deleteConfirm": "确定要删除这个会话吗?"
|
|
32
41
|
},
|
|
33
42
|
"settings": {
|
|
34
43
|
"title": "设置",
|
|
@@ -51,6 +60,9 @@
|
|
|
51
60
|
"scheduler": {
|
|
52
61
|
"memoryMaintenance": "记忆维护",
|
|
53
62
|
"description": "配置系统何时压缩旧记忆并删除过期会话。",
|
|
63
|
+
"sessionRetentionDays": "会话保留天数",
|
|
64
|
+
"sessionRetentionHint": "超过该天数未活跃的会话会在维护任务中被自动删除。",
|
|
65
|
+
"days": "天",
|
|
54
66
|
"cronSchedule": "Cron 表达式",
|
|
55
67
|
"cronHint": "格式: 分 时 日 月 周 (例如 \"0 0 * * *\" 表示每天午夜)",
|
|
56
68
|
"manualTrigger": "手动触发",
|
|
@@ -10,7 +10,9 @@ try {
|
|
|
10
10
|
if (!loadedConfig.systemPrompt)
|
|
11
11
|
loadedConfig.systemPrompt = SYSTEM_PROMPT;
|
|
12
12
|
if (!loadedConfig.scheduler)
|
|
13
|
-
loadedConfig.scheduler = { memoryMaintenanceCron: '0 0 * * *' };
|
|
13
|
+
loadedConfig.scheduler = { memoryMaintenanceCron: '0 0 * * *', sessionRetentionDays: 5 };
|
|
14
|
+
if (loadedConfig.scheduler.sessionRetentionDays === undefined)
|
|
15
|
+
loadedConfig.scheduler.sessionRetentionDays = 5;
|
|
14
16
|
if (!loadedConfig.workspace)
|
|
15
17
|
loadedConfig.workspace = process.cwd();
|
|
16
18
|
// Ensure LLM config exists
|
|
@@ -22,7 +24,8 @@ try {
|
|
|
22
24
|
model: 'qwen-plus',
|
|
23
25
|
temperature: 0.7,
|
|
24
26
|
requestTimeoutMs: 600000,
|
|
25
|
-
maxRetries: 2
|
|
27
|
+
maxRetries: 2,
|
|
28
|
+
maxToolIterations: 200
|
|
26
29
|
};
|
|
27
30
|
}
|
|
28
31
|
else {
|
|
@@ -32,6 +35,8 @@ try {
|
|
|
32
35
|
loadedConfig.llm.requestTimeoutMs = 600000;
|
|
33
36
|
if (loadedConfig.llm.maxRetries === undefined)
|
|
34
37
|
loadedConfig.llm.maxRetries = 2;
|
|
38
|
+
if (loadedConfig.llm.maxToolIterations === undefined)
|
|
39
|
+
loadedConfig.llm.maxToolIterations = 200;
|
|
35
40
|
}
|
|
36
41
|
// Override with env vars if present (optional, but good for security)
|
|
37
42
|
if (process.env.XIAOZUO_LLM_API_KEY)
|
|
@@ -51,10 +56,11 @@ catch (error) {
|
|
|
51
56
|
model: 'qwen-plus',
|
|
52
57
|
temperature: 0.7,
|
|
53
58
|
requestTimeoutMs: 600000,
|
|
54
|
-
maxRetries: 2
|
|
59
|
+
maxRetries: 2,
|
|
60
|
+
maxToolIterations: 200
|
|
55
61
|
},
|
|
56
62
|
logging: { level: 'info' },
|
|
57
|
-
scheduler: { memoryMaintenanceCron: '0 0 * * *' },
|
|
63
|
+
scheduler: { memoryMaintenanceCron: '0 0 * * *', sessionRetentionDays: 5 },
|
|
58
64
|
workspace: process.cwd(),
|
|
59
65
|
systemPrompt: SYSTEM_PROMPT
|
|
60
66
|
};
|
|
@@ -32,7 +32,7 @@ export class AgentRuntime {
|
|
|
32
32
|
console.log(`[Agent:${this.name}] Calling LLM...`);
|
|
33
33
|
let response = await this.callLLM(messages);
|
|
34
34
|
let iterations = 0;
|
|
35
|
-
const MAX_ITERATIONS =
|
|
35
|
+
const MAX_ITERATIONS = config.llm.maxToolIterations ?? 200;
|
|
36
36
|
while (response.choices[0].message.tool_calls && iterations < MAX_ITERATIONS) {
|
|
37
37
|
iterations++;
|
|
38
38
|
const toolCalls = response.choices[0].message.tool_calls;
|
|
@@ -68,6 +68,10 @@ export class AgentRuntime {
|
|
|
68
68
|
}
|
|
69
69
|
response = await this.callLLM(messages);
|
|
70
70
|
}
|
|
71
|
+
if (response.choices[0].message.tool_calls && iterations >= MAX_ITERATIONS) {
|
|
72
|
+
const content = response.choices[0].message.content || 'No response generated.';
|
|
73
|
+
return `${content}\n\n(已到达回合上限,建议回复“继续”以接着执行;可在 config.json 设置 llm.maxToolIterations,当前=${MAX_ITERATIONS})`;
|
|
74
|
+
}
|
|
71
75
|
return response.choices[0].message.content || 'No response generated.';
|
|
72
76
|
}
|
|
73
77
|
catch (error) {
|
|
@@ -45,7 +45,7 @@ export class Brain {
|
|
|
45
45
|
if (process.env.DEBUG)
|
|
46
46
|
console.log('[Brain] LLM Response (snippet):', contentSnippet);
|
|
47
47
|
let iterations = 0;
|
|
48
|
-
const MAX_ITERATIONS =
|
|
48
|
+
const MAX_ITERATIONS = config.llm.maxToolIterations ?? 200;
|
|
49
49
|
while (response.choices[0].message.tool_calls && iterations < MAX_ITERATIONS) {
|
|
50
50
|
iterations++;
|
|
51
51
|
const toolCalls = response.choices[0].message.tool_calls;
|
|
@@ -92,10 +92,13 @@ export class Brain {
|
|
|
92
92
|
if (process.env.DEBUG)
|
|
93
93
|
console.log('[Brain] LLM Response (after tool, snippet):', nextContentSnippet);
|
|
94
94
|
}
|
|
95
|
+
const hitLimit = Boolean(response.choices[0].message.tool_calls) && iterations >= MAX_ITERATIONS;
|
|
95
96
|
const finalContent = response.choices[0].message.content || 'I could not generate a response.';
|
|
96
97
|
if (process.env.DEBUG)
|
|
97
98
|
console.log('[Brain] Final Response (snippet):', finalContent.substring(0, 100) + '...');
|
|
98
|
-
|
|
99
|
+
if (!hitLimit)
|
|
100
|
+
return finalContent;
|
|
101
|
+
return `${finalContent}\n\n(已到达回合上限,建议回复“继续”以接着执行;可在 config.json 设置 llm.maxToolIterations,当前=${MAX_ITERATIONS})`;
|
|
99
102
|
}
|
|
100
103
|
catch (error) {
|
|
101
104
|
console.error('[Brain] Error in processing:', error);
|
|
@@ -27,6 +27,9 @@ export class MemoryManager {
|
|
|
27
27
|
async updateSessionMeta(id, patch) {
|
|
28
28
|
return this.shortTerm.updateSessionMeta(id, patch);
|
|
29
29
|
}
|
|
30
|
+
async persistSession(id) {
|
|
31
|
+
return this.shortTerm.persistSession(id);
|
|
32
|
+
}
|
|
30
33
|
async deleteSession(id) {
|
|
31
34
|
return this.shortTerm.deleteSession(id);
|
|
32
35
|
}
|
|
@@ -80,16 +83,18 @@ ${recentMessages}
|
|
|
80
83
|
}
|
|
81
84
|
// --- Maintenance ---
|
|
82
85
|
// Managed by Scheduler
|
|
83
|
-
async runMaintenance(
|
|
84
|
-
|
|
86
|
+
async runMaintenance(input) {
|
|
87
|
+
const sessionMaxAgeDays = input?.sessionMaxAgeDays ?? 5;
|
|
88
|
+
const vectorMaxAgeDays = input?.vectorMaxAgeDays ?? 15;
|
|
89
|
+
console.log(`[MemoryManager] Running maintenance (Sessions: ${sessionMaxAgeDays} days, Vector: ${vectorMaxAgeDays} days)...`);
|
|
85
90
|
try {
|
|
86
91
|
// 1. Clean up old sessions
|
|
87
|
-
const deletedSessions = await this.shortTerm.cleanupOldSessions(
|
|
92
|
+
const deletedSessions = await this.shortTerm.cleanupOldSessions(sessionMaxAgeDays);
|
|
88
93
|
if (deletedSessions > 0) {
|
|
89
94
|
console.log(`[MemoryManager] Deleted ${deletedSessions} old sessions.`);
|
|
90
95
|
}
|
|
91
96
|
// 2. Summarize and Prune Vector Memories
|
|
92
|
-
const oldMemories = await this.vector.getMemoriesOlderThan(
|
|
97
|
+
const oldMemories = await this.vector.getMemoriesOlderThan(vectorMaxAgeDays);
|
|
93
98
|
if (oldMemories.length > 0) {
|
|
94
99
|
console.log(`[MemoryManager] Found ${oldMemories.length} old memories to summarize.`);
|
|
95
100
|
// Batch process
|
|
@@ -112,7 +117,7 @@ ${recentMessages}
|
|
|
112
117
|
}
|
|
113
118
|
}
|
|
114
119
|
// Prune after summarization
|
|
115
|
-
await this.vector.pruneOldMemories(
|
|
120
|
+
await this.vector.pruneOldMemories(vectorMaxAgeDays);
|
|
116
121
|
}
|
|
117
122
|
else {
|
|
118
123
|
// No old memories
|
|
@@ -50,6 +50,9 @@ export class ShortTermMemory {
|
|
|
50
50
|
getMessagesFile(sessionId) {
|
|
51
51
|
return path.join(this.getSessionDir(sessionId), 'messages.json');
|
|
52
52
|
}
|
|
53
|
+
getContextFile(sessionId) {
|
|
54
|
+
return path.join(this.getSessionDir(sessionId), 'context.txt');
|
|
55
|
+
}
|
|
53
56
|
getRunsFile(sessionId) {
|
|
54
57
|
return path.join(this.getSessionDir(sessionId), 'runs.json');
|
|
55
58
|
}
|
|
@@ -165,10 +168,58 @@ export class ShortTermMemory {
|
|
|
165
168
|
updatedAt: Date.now()
|
|
166
169
|
};
|
|
167
170
|
await fs.ensureDir(next.workspace);
|
|
168
|
-
|
|
171
|
+
if (patch.context !== undefined) {
|
|
172
|
+
const ctxFile = this.getContextFile(id);
|
|
173
|
+
const val = String(patch.context ?? '').trim();
|
|
174
|
+
if (!val) {
|
|
175
|
+
try {
|
|
176
|
+
await fs.remove(ctxFile);
|
|
177
|
+
}
|
|
178
|
+
catch { /* ignore */ }
|
|
179
|
+
delete next.context;
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
await fs.ensureDir(this.getSessionDir(id));
|
|
183
|
+
await fs.writeFile(ctxFile, val, 'utf-8');
|
|
184
|
+
next.context = val;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const metaForIndex = { ...next };
|
|
188
|
+
delete metaForIndex.context;
|
|
189
|
+
metas[idx] = metaForIndex;
|
|
190
|
+
metas.sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
|
|
191
|
+
await this.saveIndex(metas);
|
|
192
|
+
this.metaCache.set(id, next);
|
|
193
|
+
return next;
|
|
194
|
+
}
|
|
195
|
+
async persistSession(id) {
|
|
196
|
+
await this.ensureReady();
|
|
197
|
+
const current = await this.getSessionMeta(id);
|
|
198
|
+
if (!current)
|
|
199
|
+
throw new Error('Session not found');
|
|
200
|
+
const now = Date.now();
|
|
201
|
+
const metas = await this.loadIndex();
|
|
202
|
+
const idx = metas.findIndex(m => m.id === id);
|
|
203
|
+
if (idx < 0)
|
|
204
|
+
throw new Error('Session not found');
|
|
205
|
+
let context = '';
|
|
206
|
+
try {
|
|
207
|
+
const ctxFile = this.getContextFile(id);
|
|
208
|
+
if (await fs.pathExists(ctxFile)) {
|
|
209
|
+
context = String(await fs.readFile(ctxFile, 'utf-8') || '').trim();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
// ignore
|
|
214
|
+
}
|
|
215
|
+
const next = { ...current, context: context || undefined, updatedAt: now, lastActiveAt: now };
|
|
216
|
+
metas[idx] = { ...metas[idx], updatedAt: now, lastActiveAt: now };
|
|
169
217
|
metas.sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
|
|
170
218
|
await this.saveIndex(metas);
|
|
171
219
|
this.metaCache.set(id, next);
|
|
220
|
+
await fs.ensureDir(this.getSessionDir(id));
|
|
221
|
+
await fs.ensureFile(this.getMessagesFile(id));
|
|
222
|
+
await fs.ensureFile(this.getRunsFile(id));
|
|
172
223
|
return next;
|
|
173
224
|
}
|
|
174
225
|
async getSession(id) {
|
|
@@ -186,7 +237,17 @@ export class ShortTermMemory {
|
|
|
186
237
|
await fs.writeJson(msgFile, [], { spaces: 2 });
|
|
187
238
|
messages = [];
|
|
188
239
|
}
|
|
189
|
-
|
|
240
|
+
let context = '';
|
|
241
|
+
try {
|
|
242
|
+
const ctxFile = this.getContextFile(id);
|
|
243
|
+
if (await fs.pathExists(ctxFile)) {
|
|
244
|
+
context = String(await fs.readFile(ctxFile, 'utf-8') || '').trim();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
// ignore
|
|
249
|
+
}
|
|
250
|
+
return { meta: { ...meta, context: context || undefined }, messages };
|
|
190
251
|
}
|
|
191
252
|
async createRun(sessionId, userContent, runId) {
|
|
192
253
|
await this.ensureReady();
|
|
@@ -15,10 +15,12 @@ export const initScheduler = () => {
|
|
|
15
15
|
// Or we can assume restart means config changed, so maybe we don't need to run immediately?
|
|
16
16
|
// Let's keep it simple: only schedule the cron job. Immediate run can be manual via API.
|
|
17
17
|
cronTask = cron.schedule(cronSchedule, () => {
|
|
18
|
-
|
|
18
|
+
const sessionMaxAgeDays = config.scheduler?.sessionRetentionDays ?? 5;
|
|
19
|
+
memoryManager.runMaintenance({ sessionMaxAgeDays, vectorMaxAgeDays: 15 });
|
|
19
20
|
});
|
|
20
21
|
};
|
|
21
22
|
export const runMaintenanceNow = () => {
|
|
22
23
|
console.log('[Scheduler] Manually triggering maintenance...');
|
|
23
|
-
|
|
24
|
+
const sessionMaxAgeDays = config.scheduler?.sessionRetentionDays ?? 5;
|
|
25
|
+
memoryManager.runMaintenance({ sessionMaxAgeDays, vectorMaxAgeDays: 15 });
|
|
24
26
|
};
|
package/dist/server/index.js
CHANGED
|
@@ -108,8 +108,23 @@ app.post('/api/sessions', async (req, res) => {
|
|
|
108
108
|
// 更新会话元信息
|
|
109
109
|
app.patch('/api/sessions/:id', async (req, res) => {
|
|
110
110
|
try {
|
|
111
|
-
const { alias, workspace } = req.body || {};
|
|
112
|
-
const meta = await memory.updateSessionMeta(req.params.id, { alias, workspace });
|
|
111
|
+
const { alias, workspace, context } = req.body || {};
|
|
112
|
+
const meta = await memory.updateSessionMeta(req.params.id, { alias, workspace, context });
|
|
113
|
+
res.json(meta);
|
|
114
|
+
}
|
|
115
|
+
catch (e) {
|
|
116
|
+
const msg = String(e?.message || 'Unknown error');
|
|
117
|
+
if (msg.toLowerCase().includes('not found')) {
|
|
118
|
+
res.status(404).json({ error: 'Session not found' });
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
res.status(500).json({ error: msg });
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
// 手动持久化会话(刷新 updatedAt,并确保会话数据文件存在)
|
|
125
|
+
app.post('/api/sessions/:id/persist', async (req, res) => {
|
|
126
|
+
try {
|
|
127
|
+
const meta = await memory.persistSession(req.params.id);
|
|
113
128
|
res.json(meta);
|
|
114
129
|
}
|
|
115
130
|
catch (e) {
|
|
@@ -167,13 +182,14 @@ app.get('/api/config', async (req, res) => {
|
|
|
167
182
|
temperature: config.llm.temperature,
|
|
168
183
|
systemPrompt: config.systemPrompt,
|
|
169
184
|
memoryMaintenanceCron: config.scheduler?.memoryMaintenanceCron,
|
|
185
|
+
sessionRetentionDays: config.scheduler?.sessionRetentionDays,
|
|
170
186
|
workspace: config.workspace
|
|
171
187
|
});
|
|
172
188
|
});
|
|
173
189
|
// 更新配置并写入 config.json
|
|
174
190
|
app.post('/api/config', async (req, res) => {
|
|
175
191
|
try {
|
|
176
|
-
const { apiKey, baseURL, model, temperature, systemPrompt, memoryMaintenanceCron, workspace } = req.body;
|
|
192
|
+
const { apiKey, baseURL, model, temperature, systemPrompt, memoryMaintenanceCron, sessionRetentionDays, workspace } = req.body;
|
|
177
193
|
// 更新配置对象
|
|
178
194
|
const newConfig = { ...config };
|
|
179
195
|
if (apiKey !== undefined)
|
|
@@ -218,6 +234,15 @@ app.post('/api/config', async (req, res) => {
|
|
|
218
234
|
restartScheduler = true;
|
|
219
235
|
}
|
|
220
236
|
}
|
|
237
|
+
if (sessionRetentionDays !== undefined) {
|
|
238
|
+
if (!newConfig.scheduler)
|
|
239
|
+
newConfig.scheduler = {};
|
|
240
|
+
const days = Number(sessionRetentionDays);
|
|
241
|
+
if (Number.isFinite(days) && days > 0 && newConfig.scheduler.sessionRetentionDays !== days) {
|
|
242
|
+
newConfig.scheduler.sessionRetentionDays = days;
|
|
243
|
+
restartScheduler = true;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
221
246
|
// 保存到文件
|
|
222
247
|
saveConfig(newConfig);
|
|
223
248
|
// 如果 LLM 配置变了,更新 Brain
|
package/package.json
CHANGED
|
@@ -27,8 +27,17 @@
|
|
|
27
27
|
"aliasHint": "Alias is shown in the list and header. Empty falls back to session ID.",
|
|
28
28
|
"workspaceLabel": "Workspace",
|
|
29
29
|
"workspaceHint": "File tools default to this workspace and are restricted to it.",
|
|
30
|
+
"contextLabel": "Context (optional)",
|
|
31
|
+
"contextPlaceholder": "E.g. project background, terminology, current conclusions, long-lived context...",
|
|
32
|
+
"contextHint": "This is persisted and can be used as session context in complex tasks.",
|
|
30
33
|
"selectWorkspace": "Select workspace",
|
|
31
|
-
"selectCurrentFolder": "Select current folder"
|
|
34
|
+
"selectCurrentFolder": "Select current folder",
|
|
35
|
+
"persistNow": "Persist now",
|
|
36
|
+
"persisting": "Persisting...",
|
|
37
|
+
"persistSuccess": "Persisted",
|
|
38
|
+
"delete": "Delete session",
|
|
39
|
+
"deleting": "Deleting...",
|
|
40
|
+
"deleteConfirm": "Are you sure you want to delete this session?"
|
|
32
41
|
},
|
|
33
42
|
"settings": {
|
|
34
43
|
"title": "Settings",
|
|
@@ -51,6 +60,9 @@
|
|
|
51
60
|
"scheduler": {
|
|
52
61
|
"memoryMaintenance": "Memory Maintenance",
|
|
53
62
|
"description": "Configure when the system should compress old memories and delete expired sessions.",
|
|
63
|
+
"sessionRetentionDays": "Session retention",
|
|
64
|
+
"sessionRetentionHint": "Sessions inactive for longer than this will be deleted during maintenance.",
|
|
65
|
+
"days": "days",
|
|
54
66
|
"cronSchedule": "Cron Schedule",
|
|
55
67
|
"cronHint": "Format: Minute Hour Day Month DayOfWeek (e.g., \"0 0 * * *\" for daily at midnight)",
|
|
56
68
|
"manualTrigger": "Manual Trigger",
|
|
@@ -27,8 +27,17 @@
|
|
|
27
27
|
"aliasHint": "列表与聊天头部将优先展示别名。留空将回退显示会话 ID。",
|
|
28
28
|
"workspaceLabel": "Workspace(工作目录)",
|
|
29
29
|
"workspaceHint": "文件相关工具默认以该目录为工作区,并限制访问范围。",
|
|
30
|
+
"contextLabel": "上下文数据(可选)",
|
|
31
|
+
"contextPlaceholder": "例如:项目背景、术语约定、当前阶段结论、需要长期保持的上下文...",
|
|
32
|
+
"contextHint": "该内容会持久化保存,并在复杂任务中作为会话上下文使用。",
|
|
30
33
|
"selectWorkspace": "选择工作目录",
|
|
31
|
-
"selectCurrentFolder": "选择当前目录"
|
|
34
|
+
"selectCurrentFolder": "选择当前目录",
|
|
35
|
+
"persistNow": "立即持久化",
|
|
36
|
+
"persisting": "持久化中...",
|
|
37
|
+
"persistSuccess": "已持久化",
|
|
38
|
+
"delete": "删除会话",
|
|
39
|
+
"deleting": "删除中...",
|
|
40
|
+
"deleteConfirm": "确定要删除这个会话吗?"
|
|
32
41
|
},
|
|
33
42
|
"settings": {
|
|
34
43
|
"title": "设置",
|
|
@@ -51,6 +60,9 @@
|
|
|
51
60
|
"scheduler": {
|
|
52
61
|
"memoryMaintenance": "记忆维护",
|
|
53
62
|
"description": "配置系统何时压缩旧记忆并删除过期会话。",
|
|
63
|
+
"sessionRetentionDays": "会话保留天数",
|
|
64
|
+
"sessionRetentionHint": "超过该天数未活跃的会话会在维护任务中被自动删除。",
|
|
65
|
+
"days": "天",
|
|
54
66
|
"cronSchedule": "Cron 表达式",
|
|
55
67
|
"cronHint": "格式: 分 时 日 月 周 (例如 \"0 0 * * *\" 表示每天午夜)",
|
|
56
68
|
"manualTrigger": "手动触发",
|