xiaozuoassistant 0.2.38 → 0.2.40

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.
@@ -90,6 +90,26 @@ export class FeishuChannel extends BaseChannel {
90
90
  // Use chat_id for group chats, open_id/user_id for p2p?
91
91
  // Actually message.chat_id is universal for where the message comes from.
92
92
  const sessionId = `feishu:${botName}:${event.chat_id}`;
93
+ // 支持飞书端发送 /archive 触发手动归档
94
+ if (content.trim().toLowerCase() === '/archive') {
95
+ console.log(`[Feishu-${botName}] Manual archive command triggered for sessionId: ${sessionId}`);
96
+ this.emitMessage(sessionId, '正在为您整理并归档当前会话的长期记忆,请稍候...');
97
+ // We can't directly call memoryManager here as it breaks architecture,
98
+ // emit a special command or let Brain handle it.
99
+ // The simplest way is to emit the command, and let the backend API handle it via event or intercept it.
100
+ // For MVP: We intercept it here and make a local HTTP request to the archive endpoint
101
+ try {
102
+ const axios = require('axios');
103
+ const configLoader = require('../config/loader.js').config;
104
+ const port = configLoader.server?.port || 3001;
105
+ await axios.post(`http://127.0.0.1:${port}/api/sessions/${encodeURIComponent(sessionId)}/archive`);
106
+ this.emitMessage(sessionId, '✅ 归档完成!我已经记住了这个会话的关键信息。');
107
+ }
108
+ catch (e) {
109
+ this.emitMessage(sessionId, `❌ 归档失败: ${e.message}`);
110
+ }
111
+ return;
112
+ }
93
113
  console.log(`[Feishu-${botName}] Emitting message with sessionId: ${sessionId}`);
94
114
  this.emitMessage(sessionId, content);
95
115
  }
@@ -16,6 +16,25 @@ export class MemoryManager {
16
16
  return MemoryManager.instance;
17
17
  }
18
18
  // --- Layer 1: Short-term (Session) ---
19
+ getFeishuAppId(sessionId) {
20
+ if (sessionId.startsWith('feishu:')) {
21
+ const parts = sessionId.split(':');
22
+ if (parts.length >= 2) {
23
+ const botName = parts[1];
24
+ const feishuConfig = config.channels?.feishu;
25
+ if (Array.isArray(feishuConfig)) {
26
+ const bot = feishuConfig.find(b => b.name === botName);
27
+ if (bot)
28
+ return bot.appId;
29
+ }
30
+ else if (feishuConfig) {
31
+ if (botName === 'default')
32
+ return feishuConfig.appId;
33
+ }
34
+ }
35
+ }
36
+ return undefined;
37
+ }
19
38
  async createSession(input) {
20
39
  return this.shortTerm.createSession(input);
21
40
  }
@@ -42,7 +61,8 @@ export class MemoryManager {
42
61
  // to avoid cluttering.
43
62
  if (message.content.length > 10) { // Simple filter
44
63
  const userId = config.userId || 'default';
45
- this.vector.addMemory(message.content, 'recent', { sessionId, role: message.role, userId })
64
+ const appId = this.getFeishuAppId(sessionId);
65
+ this.vector.addMemory(message.content, 'recent', { sessionId, role: message.role, userId, appId })
46
66
  .catch(err => console.error('Background vector add failed:', err));
47
67
  }
48
68
  // 3. Extract Facts (Structured) - This usually requires an LLM call to extract
@@ -74,9 +94,19 @@ export class MemoryManager {
74
94
  const history = await this.getHistory(sessionId);
75
95
  const recentMessages = history.slice(-10).map(m => `${m.role}: ${m.content}`).join('\n');
76
96
  // 2. Search Vector Memory (Recent & Long-term)
77
- const vectorResults = await this.vector.search(query, undefined, 10);
78
- const vectorFiltered = vectorResults.filter(r => (r.metadata?.userId || 'default') === uid).slice(0, 3);
79
- const vectorContext = vectorFiltered.map(r => `[Memory]: ${r.text}`).join('\n');
97
+ // We search more broadly to ensure we find both User-specific and Bot-specific memories
98
+ const vectorResults = await this.vector.search(query, undefined, 20);
99
+ const botAppId = this.getFeishuAppId(sessionId);
100
+ // Filter results: Include memories belonging to the user OR the current bot
101
+ const vectorFiltered = vectorResults.filter(r => {
102
+ const isUserMemory = (r.metadata?.userId || 'default') === uid;
103
+ const isBotMemory = botAppId && r.metadata?.appId === botAppId;
104
+ return isUserMemory || isBotMemory;
105
+ }).slice(0, 5); // Take top 5 relevant chunks
106
+ const vectorContext = vectorFiltered.map(r => {
107
+ const source = (botAppId && r.metadata?.appId === botAppId) ? `[Bot Knowledge]` : `[User Memory]`;
108
+ return `${source}: ${r.text}`;
109
+ }).join('\n');
80
110
  // 3. Get User Profile (Structured)
81
111
  const profile = await this.structured.getUserProfile(uid);
82
112
  const profileContext = Object.entries(profile).map(([k, v]) => `[User Info] ${k}: ${v}`).join('\n');
@@ -135,6 +165,7 @@ ${recentMessages}
135
165
  const content = chunk.map(m => `${m.role}: ${m.content}`).join('\n');
136
166
  // 1. Memory Archiving (Project or General)
137
167
  const projectIds = session.projectIds || (session.projectId ? [session.projectId] : []);
168
+ const appId = this.getFeishuAppId(session.id);
138
169
  if (projectIds.length > 0) {
139
170
  // Project Context
140
171
  const projectInfo = await brain.extractKeyInformation(content, 'project');
@@ -143,7 +174,8 @@ ${recentMessages}
143
174
  await this.vector.addMemory(projectInfo, 'project_archive', {
144
175
  sessionId: session.id,
145
176
  projectId: pid,
146
- archivedAt: now
177
+ archivedAt: now,
178
+ appId
147
179
  });
148
180
  console.log(`[MemoryManager] Archived project info for Project ${pid} (Chunk ${i / CHUNK_SIZE + 1})`);
149
181
  }
@@ -156,7 +188,8 @@ ${recentMessages}
156
188
  await this.vector.addMemory(generalInfo, 'long_term', {
157
189
  sessionId: session.id,
158
190
  archivedAt: now,
159
- source: 'auto_archive'
191
+ source: 'auto_archive',
192
+ appId
160
193
  });
161
194
  console.log(`[MemoryManager] Archived general info for Session ${session.id} (Chunk ${i / CHUNK_SIZE + 1})`);
162
195
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xiaozuoassistant",
3
- "version": "0.2.38",
3
+ "version": "0.2.40",
4
4
  "description": "A local-first personal AI assistant with multi-channel support and enhanced memory.",
5
5
  "author": "mantle.lau",
6
6
  "license": "MIT",