evolclaw 2.0.1 → 2.0.2
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/data/evolclaw.sample.json +31 -26
- package/dist/channels/wechat.js +451 -0
- package/dist/cli.js +196 -146
- package/dist/config.js +32 -17
- package/dist/core/agent-runner.js +27 -41
- package/dist/core/command-handler.js +72 -54
- package/dist/core/message-processor.js +36 -10
- package/dist/core/message-queue.js +9 -3
- package/dist/core/session-manager.js +81 -238
- package/dist/index.js +189 -115
- package/dist/utils/init-feishu.js +261 -0
- package/dist/utils/init-wechat.js +170 -0
- package/dist/utils/init.js +98 -69
- package/dist/utils/stream-flusher.js +3 -2
- package/package.json +9 -7
|
@@ -23,6 +23,56 @@ export class SessionManager {
|
|
|
23
23
|
const encodedPath = this.getProjectDirName(projectPath);
|
|
24
24
|
return path.join(homeDir, '.claude', 'projects', encodedPath, `${sessionId}.jsonl`);
|
|
25
25
|
}
|
|
26
|
+
rowToSession(row) {
|
|
27
|
+
return {
|
|
28
|
+
id: row.id,
|
|
29
|
+
channel: row.channel,
|
|
30
|
+
channelId: row.channel_id,
|
|
31
|
+
projectPath: row.project_path,
|
|
32
|
+
claudeSessionId: row.claude_session_id,
|
|
33
|
+
name: row.name,
|
|
34
|
+
isActive: row.is_active === 1,
|
|
35
|
+
createdAt: row.created_at,
|
|
36
|
+
updatedAt: row.updated_at
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
deactivateAll(channel, channelId) {
|
|
40
|
+
this.db.prepare(`
|
|
41
|
+
UPDATE sessions SET is_active = 0, updated_at = ?
|
|
42
|
+
WHERE channel = ? AND channel_id = ? AND is_active = 1
|
|
43
|
+
`).run(Date.now(), channel, channelId);
|
|
44
|
+
}
|
|
45
|
+
validateSessionFile(row) {
|
|
46
|
+
const claudeSessionId = row.claude_session_id;
|
|
47
|
+
if (!claudeSessionId)
|
|
48
|
+
return undefined;
|
|
49
|
+
const sessionFile = this.getSessionFilePath(row.project_path, claudeSessionId);
|
|
50
|
+
if (fs.existsSync(sessionFile))
|
|
51
|
+
return claudeSessionId;
|
|
52
|
+
logger.warn(`Session file not found: ${sessionFile}, clearing session ID`);
|
|
53
|
+
this.db.prepare(`UPDATE sessions SET claude_session_id = NULL WHERE id = ?`).run(row.id);
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
insertSession(session) {
|
|
57
|
+
this.db.prepare(`
|
|
58
|
+
INSERT OR IGNORE INTO sessions (id, channel, channel_id, project_path, claude_session_id, name, is_active, created_at, updated_at)
|
|
59
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
60
|
+
`).run(session.id, session.channel, session.channelId, session.projectPath, session.claudeSessionId ?? null, session.name ?? null, session.isActive ? 1 : 0, session.createdAt, session.updatedAt);
|
|
61
|
+
}
|
|
62
|
+
extractUserMessageText(messageContent) {
|
|
63
|
+
if (typeof messageContent === 'string') {
|
|
64
|
+
const text = messageContent.trim();
|
|
65
|
+
return text.substring(0, 50) + (text.length > 50 ? '...' : '');
|
|
66
|
+
}
|
|
67
|
+
else if (Array.isArray(messageContent)) {
|
|
68
|
+
const textContent = messageContent.find((c) => c.type === 'text');
|
|
69
|
+
if (textContent?.text) {
|
|
70
|
+
const text = textContent.text.trim();
|
|
71
|
+
return text.substring(0, 50) + (text.length > 50 ? '...' : '');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
26
76
|
initDatabase() {
|
|
27
77
|
const tableInfo = this.db.prepare('PRAGMA table_info(sessions)').all();
|
|
28
78
|
const hasIsActive = tableInfo.some((col) => col.name === 'is_active');
|
|
@@ -144,26 +194,8 @@ export class SessionManager {
|
|
|
144
194
|
WHERE channel = ? AND channel_id = ? AND is_active = 1
|
|
145
195
|
`).get(channel, channelId);
|
|
146
196
|
if (active) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if (validSessionId) {
|
|
150
|
-
const sessionFile = this.getSessionFilePath(active.project_path, validSessionId);
|
|
151
|
-
if (!fs.existsSync(sessionFile)) {
|
|
152
|
-
logger.warn(`Session file not found: ${sessionFile}`);
|
|
153
|
-
validSessionId = null;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
return {
|
|
157
|
-
id: active.id,
|
|
158
|
-
channel: active.channel,
|
|
159
|
-
channelId: active.channel_id,
|
|
160
|
-
projectPath: active.project_path,
|
|
161
|
-
claudeSessionId: validSessionId,
|
|
162
|
-
name: active.name,
|
|
163
|
-
isActive: active.is_active === 1,
|
|
164
|
-
createdAt: active.created_at,
|
|
165
|
-
updatedAt: active.updated_at
|
|
166
|
-
};
|
|
197
|
+
const validSessionId = this.validateSessionFile(active);
|
|
198
|
+
return { ...this.rowToSession(active), claudeSessionId: validSessionId };
|
|
167
199
|
}
|
|
168
200
|
// 2. 没有活跃会话,查找该聊天在默认项目的会话
|
|
169
201
|
const existing = this.db.prepare(`
|
|
@@ -172,32 +204,13 @@ export class SessionManager {
|
|
|
172
204
|
ORDER BY updated_at DESC LIMIT 1
|
|
173
205
|
`).get(channel, channelId, defaultProjectPath);
|
|
174
206
|
if (existing) {
|
|
175
|
-
|
|
176
|
-
let validSessionId = existing.claude_session_id;
|
|
177
|
-
if (validSessionId) {
|
|
178
|
-
const sessionFile = this.getSessionFilePath(existing.project_path, validSessionId);
|
|
179
|
-
if (!fs.existsSync(sessionFile)) {
|
|
180
|
-
logger.warn(`Session file not found: ${sessionFile}, clearing session ID`);
|
|
181
|
-
validSessionId = null;
|
|
182
|
-
this.db.prepare(`UPDATE sessions SET claude_session_id = NULL WHERE id = ?`).run(existing.id);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
207
|
+
const validSessionId = this.validateSessionFile(existing);
|
|
185
208
|
// 激活该会话
|
|
186
209
|
this.db.prepare(`
|
|
187
210
|
UPDATE sessions SET is_active = 1, updated_at = ?
|
|
188
211
|
WHERE id = ?
|
|
189
212
|
`).run(Date.now(), existing.id);
|
|
190
|
-
return {
|
|
191
|
-
id: existing.id,
|
|
192
|
-
channel: existing.channel,
|
|
193
|
-
channelId: existing.channel_id,
|
|
194
|
-
projectPath: existing.project_path,
|
|
195
|
-
claudeSessionId: validSessionId,
|
|
196
|
-
name: existing.name,
|
|
197
|
-
isActive: true,
|
|
198
|
-
createdAt: existing.created_at,
|
|
199
|
-
updatedAt: existing.updated_at
|
|
200
|
-
};
|
|
213
|
+
return { ...this.rowToSession(existing), claudeSessionId: validSessionId, isActive: true };
|
|
201
214
|
}
|
|
202
215
|
// 3. 创建新会话(默认为活跃)
|
|
203
216
|
const session = {
|
|
@@ -217,23 +230,13 @@ export class SessionManager {
|
|
|
217
230
|
`).run(session.id, session.channel, session.channelId, session.projectPath, session.claudeSessionId ?? null, session.name ?? null, 1, session.createdAt, session.updatedAt);
|
|
218
231
|
// 如果插入被忽略(已存在),重新查询
|
|
219
232
|
if (result.changes === 0) {
|
|
220
|
-
const
|
|
233
|
+
const recheck = this.db.prepare(`
|
|
221
234
|
SELECT * FROM sessions
|
|
222
235
|
WHERE channel = ? AND channel_id = ? AND project_path = ?
|
|
223
236
|
`).get(channel, channelId, defaultProjectPath);
|
|
224
|
-
if (
|
|
225
|
-
this.db.prepare(`UPDATE sessions SET is_active = 1, updated_at = ? WHERE id = ?`).run(Date.now(),
|
|
226
|
-
return {
|
|
227
|
-
id: existing.id,
|
|
228
|
-
channel: existing.channel,
|
|
229
|
-
channelId: existing.channel_id,
|
|
230
|
-
projectPath: existing.project_path,
|
|
231
|
-
claudeSessionId: existing.claude_session_id,
|
|
232
|
-
name: existing.name,
|
|
233
|
-
isActive: true,
|
|
234
|
-
createdAt: existing.created_at,
|
|
235
|
-
updatedAt: Date.now()
|
|
236
|
-
};
|
|
237
|
+
if (recheck) {
|
|
238
|
+
this.db.prepare(`UPDATE sessions SET is_active = 1, updated_at = ? WHERE id = ?`).run(Date.now(), recheck.id);
|
|
239
|
+
return { ...this.rowToSession(recheck), isActive: true, updatedAt: Date.now() };
|
|
237
240
|
}
|
|
238
241
|
}
|
|
239
242
|
return session;
|
|
@@ -252,10 +255,7 @@ export class SessionManager {
|
|
|
252
255
|
}
|
|
253
256
|
async switchProject(channel, channelId, newProjectPath) {
|
|
254
257
|
// 1. 取消当前活跃会话
|
|
255
|
-
this.
|
|
256
|
-
UPDATE sessions SET is_active = 0, updated_at = ?
|
|
257
|
-
WHERE channel = ? AND channel_id = ? AND is_active = 1
|
|
258
|
-
`).run(Date.now(), channel, channelId);
|
|
258
|
+
this.deactivateAll(channel, channelId);
|
|
259
259
|
// 2. 查找目标项目的会话
|
|
260
260
|
const target = this.db.prepare(`
|
|
261
261
|
SELECT * FROM sessions
|
|
@@ -263,32 +263,13 @@ export class SessionManager {
|
|
|
263
263
|
ORDER BY updated_at DESC LIMIT 1
|
|
264
264
|
`).get(channel, channelId, newProjectPath);
|
|
265
265
|
if (target) {
|
|
266
|
-
|
|
267
|
-
let validSessionId = target.claude_session_id;
|
|
268
|
-
if (validSessionId) {
|
|
269
|
-
const sessionFile = this.getSessionFilePath(newProjectPath, validSessionId);
|
|
270
|
-
if (!fs.existsSync(sessionFile)) {
|
|
271
|
-
logger.warn(`Session file not found: ${sessionFile}, clearing session ID`);
|
|
272
|
-
validSessionId = null;
|
|
273
|
-
this.db.prepare(`UPDATE sessions SET claude_session_id = NULL WHERE id = ?`).run(target.id);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
266
|
+
const validSessionId = this.validateSessionFile(target);
|
|
276
267
|
// 激活已有会话
|
|
277
268
|
this.db.prepare(`
|
|
278
269
|
UPDATE sessions SET is_active = 1, updated_at = ?
|
|
279
270
|
WHERE id = ?
|
|
280
271
|
`).run(Date.now(), target.id);
|
|
281
|
-
return {
|
|
282
|
-
id: target.id,
|
|
283
|
-
channel: target.channel,
|
|
284
|
-
channelId: target.channel_id,
|
|
285
|
-
projectPath: target.project_path,
|
|
286
|
-
claudeSessionId: validSessionId,
|
|
287
|
-
name: target.name,
|
|
288
|
-
isActive: true,
|
|
289
|
-
createdAt: target.created_at,
|
|
290
|
-
updatedAt: target.updated_at
|
|
291
|
-
};
|
|
272
|
+
return { ...this.rowToSession(target), claudeSessionId: validSessionId, isActive: true };
|
|
292
273
|
}
|
|
293
274
|
// 3. 创建新会话
|
|
294
275
|
const session = {
|
|
@@ -301,16 +282,9 @@ export class SessionManager {
|
|
|
301
282
|
createdAt: Date.now(),
|
|
302
283
|
updatedAt: Date.now()
|
|
303
284
|
};
|
|
304
|
-
this.
|
|
305
|
-
INSERT OR IGNORE INTO sessions (id, channel, channel_id, project_path, claude_session_id, name, is_active, created_at, updated_at)
|
|
306
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
307
|
-
`).run(session.id, session.channel, session.channelId, session.projectPath, null, session.name ?? null, 1, session.createdAt, session.updatedAt);
|
|
285
|
+
this.insertSession(session);
|
|
308
286
|
return session;
|
|
309
287
|
}
|
|
310
|
-
async updateProjectPath(channel, channelId, projectPath) {
|
|
311
|
-
this.db.prepare('UPDATE sessions SET project_path = ?, updated_at = ? WHERE channel = ? AND channel_id = ?')
|
|
312
|
-
.run(projectPath, Date.now(), channel, channelId);
|
|
313
|
-
}
|
|
314
288
|
async updateClaudeSessionId(channel, channelId, claudeSessionId) {
|
|
315
289
|
// 只更新当前活跃会话的 Claude Session ID
|
|
316
290
|
this.db.prepare(`
|
|
@@ -336,35 +310,14 @@ export class SessionManager {
|
|
|
336
310
|
WHERE channel = ? AND channel_id = ? AND is_active = 1
|
|
337
311
|
`).run(Date.now(), channel, channelId);
|
|
338
312
|
}
|
|
339
|
-
async
|
|
340
|
-
// 向后兼容的别名
|
|
341
|
-
await this.clearActiveSession(channel, channelId);
|
|
342
|
-
}
|
|
343
|
-
async getSession(channel, channelId) {
|
|
344
|
-
// 获取活跃会话
|
|
313
|
+
async getActiveSession(channel, channelId) {
|
|
345
314
|
const row = this.db.prepare(`
|
|
346
315
|
SELECT * FROM sessions
|
|
347
316
|
WHERE channel = ? AND channel_id = ? AND is_active = 1
|
|
348
317
|
`).get(channel, channelId);
|
|
349
318
|
if (!row)
|
|
350
319
|
return undefined;
|
|
351
|
-
return
|
|
352
|
-
id: row.id,
|
|
353
|
-
channel: row.channel,
|
|
354
|
-
channelId: row.channel_id,
|
|
355
|
-
projectPath: row.project_path,
|
|
356
|
-
claudeSessionId: row.claude_session_id,
|
|
357
|
-
name: row.name,
|
|
358
|
-
isActive: row.is_active === 1,
|
|
359
|
-
createdAt: row.created_at,
|
|
360
|
-
updatedAt: row.updated_at
|
|
361
|
-
};
|
|
362
|
-
}
|
|
363
|
-
/**
|
|
364
|
-
* 获取活跃会话(getSession 的别名,语义更清晰)
|
|
365
|
-
*/
|
|
366
|
-
async getActiveSession(channel, channelId) {
|
|
367
|
-
return this.getSession(channel, channelId);
|
|
320
|
+
return this.rowToSession(row);
|
|
368
321
|
}
|
|
369
322
|
async listSessions(channel, channelId) {
|
|
370
323
|
// 列出该聊天的所有会话
|
|
@@ -373,17 +326,7 @@ export class SessionManager {
|
|
|
373
326
|
WHERE channel = ? AND channel_id = ?
|
|
374
327
|
ORDER BY updated_at DESC
|
|
375
328
|
`).all(channel, channelId);
|
|
376
|
-
return rows.map(row => (
|
|
377
|
-
id: row.id,
|
|
378
|
-
channel: row.channel,
|
|
379
|
-
channelId: row.channel_id,
|
|
380
|
-
projectPath: row.project_path,
|
|
381
|
-
claudeSessionId: row.claude_session_id,
|
|
382
|
-
name: row.name,
|
|
383
|
-
isActive: row.is_active === 1,
|
|
384
|
-
createdAt: row.created_at,
|
|
385
|
-
updatedAt: row.updated_at
|
|
386
|
-
}));
|
|
329
|
+
return rows.map(row => this.rowToSession(row));
|
|
387
330
|
}
|
|
388
331
|
async getSessionByProjectPath(channel, channelId, projectPath) {
|
|
389
332
|
const row = this.db.prepare(`
|
|
@@ -392,17 +335,7 @@ export class SessionManager {
|
|
|
392
335
|
`).get(channel, channelId, projectPath);
|
|
393
336
|
if (!row)
|
|
394
337
|
return undefined;
|
|
395
|
-
return
|
|
396
|
-
id: row.id,
|
|
397
|
-
channel: row.channel,
|
|
398
|
-
channelId: row.channel_id,
|
|
399
|
-
projectPath: row.project_path,
|
|
400
|
-
claudeSessionId: row.claude_session_id,
|
|
401
|
-
name: row.name,
|
|
402
|
-
isActive: row.is_active === 1,
|
|
403
|
-
createdAt: row.created_at,
|
|
404
|
-
updatedAt: row.updated_at
|
|
405
|
-
};
|
|
338
|
+
return this.rowToSession(row);
|
|
406
339
|
}
|
|
407
340
|
async getSessionByName(channel, channelId, name) {
|
|
408
341
|
const row = this.db.prepare(`
|
|
@@ -411,17 +344,7 @@ export class SessionManager {
|
|
|
411
344
|
`).get(channel, channelId, name);
|
|
412
345
|
if (!row)
|
|
413
346
|
return undefined;
|
|
414
|
-
return
|
|
415
|
-
id: row.id,
|
|
416
|
-
channel: row.channel,
|
|
417
|
-
channelId: row.channel_id,
|
|
418
|
-
projectPath: row.project_path,
|
|
419
|
-
claudeSessionId: row.claude_session_id,
|
|
420
|
-
name: row.name,
|
|
421
|
-
isActive: row.is_active === 1,
|
|
422
|
-
createdAt: row.created_at,
|
|
423
|
-
updatedAt: row.updated_at
|
|
424
|
-
};
|
|
347
|
+
return this.rowToSession(row);
|
|
425
348
|
}
|
|
426
349
|
async switchToSession(channel, channelId, targetSessionId) {
|
|
427
350
|
// 验证目标会话存在
|
|
@@ -431,26 +354,13 @@ export class SessionManager {
|
|
|
431
354
|
if (!target)
|
|
432
355
|
return null;
|
|
433
356
|
// 取消当前活跃会话
|
|
434
|
-
this.
|
|
435
|
-
UPDATE sessions SET is_active = 0, updated_at = ?
|
|
436
|
-
WHERE channel = ? AND channel_id = ? AND is_active = 1
|
|
437
|
-
`).run(Date.now(), channel, channelId);
|
|
357
|
+
this.deactivateAll(channel, channelId);
|
|
438
358
|
// 激活目标会话
|
|
439
359
|
this.db.prepare(`
|
|
440
360
|
UPDATE sessions SET is_active = 1, updated_at = ?
|
|
441
361
|
WHERE id = ?
|
|
442
362
|
`).run(Date.now(), targetSessionId);
|
|
443
|
-
return {
|
|
444
|
-
id: target.id,
|
|
445
|
-
channel: target.channel,
|
|
446
|
-
channelId: target.channel_id,
|
|
447
|
-
projectPath: target.project_path,
|
|
448
|
-
claudeSessionId: target.claude_session_id,
|
|
449
|
-
name: target.name,
|
|
450
|
-
isActive: true,
|
|
451
|
-
createdAt: target.created_at,
|
|
452
|
-
updatedAt: Date.now()
|
|
453
|
-
};
|
|
363
|
+
return { ...this.rowToSession(target), isActive: true, updatedAt: Date.now() };
|
|
454
364
|
}
|
|
455
365
|
async renameSession(sessionId, newName) {
|
|
456
366
|
const result = this.db.prepare(`
|
|
@@ -460,10 +370,7 @@ export class SessionManager {
|
|
|
460
370
|
}
|
|
461
371
|
async createNewSession(channel, channelId, projectPath, name) {
|
|
462
372
|
// 取消当前活跃会话
|
|
463
|
-
this.
|
|
464
|
-
UPDATE sessions SET is_active = 0, updated_at = ?
|
|
465
|
-
WHERE channel = ? AND channel_id = ? AND is_active = 1
|
|
466
|
-
`).run(Date.now(), channel, channelId);
|
|
373
|
+
this.deactivateAll(channel, channelId);
|
|
467
374
|
// 创建新会话
|
|
468
375
|
const session = {
|
|
469
376
|
id: `${channel}-${channelId}-${Date.now()}`,
|
|
@@ -475,10 +382,7 @@ export class SessionManager {
|
|
|
475
382
|
createdAt: Date.now(),
|
|
476
383
|
updatedAt: Date.now()
|
|
477
384
|
};
|
|
478
|
-
this.
|
|
479
|
-
INSERT INTO sessions (id, channel, channel_id, project_path, claude_session_id, name, is_active, created_at, updated_at)
|
|
480
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
481
|
-
`).run(session.id, session.channel, session.channelId, session.projectPath, null, session.name ?? null, 1, session.createdAt, session.updatedAt);
|
|
385
|
+
this.insertSession(session);
|
|
482
386
|
return session;
|
|
483
387
|
}
|
|
484
388
|
/**
|
|
@@ -486,10 +390,7 @@ export class SessionManager {
|
|
|
486
390
|
*/
|
|
487
391
|
async createForkedSession(sourceSession, forkedClaudeSessionId, name) {
|
|
488
392
|
// 取消当前活跃会话
|
|
489
|
-
this.
|
|
490
|
-
UPDATE sessions SET is_active = 0, updated_at = ?
|
|
491
|
-
WHERE channel = ? AND channel_id = ? AND is_active = 1
|
|
492
|
-
`).run(Date.now(), sourceSession.channel, sourceSession.channelId);
|
|
393
|
+
this.deactivateAll(sourceSession.channel, sourceSession.channelId);
|
|
493
394
|
const session = {
|
|
494
395
|
id: `${sourceSession.channel}-${sourceSession.channelId}-${Date.now()}`,
|
|
495
396
|
channel: sourceSession.channel,
|
|
@@ -501,10 +402,7 @@ export class SessionManager {
|
|
|
501
402
|
createdAt: Date.now(),
|
|
502
403
|
updatedAt: Date.now()
|
|
503
404
|
};
|
|
504
|
-
this.
|
|
505
|
-
INSERT INTO sessions (id, channel, channel_id, project_path, claude_session_id, name, is_active, created_at, updated_at)
|
|
506
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
507
|
-
`).run(session.id, session.channel, session.channelId, session.projectPath, session.claudeSessionId ?? null, session.name ?? null, 1, session.createdAt, session.updatedAt);
|
|
405
|
+
this.insertSession(session);
|
|
508
406
|
return session;
|
|
509
407
|
}
|
|
510
408
|
async scanCliSessions(projectPath) {
|
|
@@ -539,21 +437,10 @@ export class SessionManager {
|
|
|
539
437
|
const lines = content.split('\n').filter(l => l.trim());
|
|
540
438
|
for (const line of lines) {
|
|
541
439
|
const event = JSON.parse(line);
|
|
542
|
-
// 格式: {type: "user", message: {role: "user", content: ...}}
|
|
543
440
|
if (event.type === 'user' && event.message?.role === 'user') {
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
const text = messageContent.trim();
|
|
548
|
-
return text.substring(0, 50) + (text.length > 50 ? '...' : '');
|
|
549
|
-
}
|
|
550
|
-
else if (Array.isArray(messageContent)) {
|
|
551
|
-
const textContent = messageContent.find((c) => c.type === 'text');
|
|
552
|
-
if (textContent?.text) {
|
|
553
|
-
const text = textContent.text.trim();
|
|
554
|
-
return text.substring(0, 50) + (text.length > 50 ? '...' : '');
|
|
555
|
-
}
|
|
556
|
-
}
|
|
441
|
+
const text = this.extractUserMessageText(event.message.content);
|
|
442
|
+
if (text)
|
|
443
|
+
return text;
|
|
557
444
|
}
|
|
558
445
|
}
|
|
559
446
|
}
|
|
@@ -573,18 +460,7 @@ export class SessionManager {
|
|
|
573
460
|
for (const line of lines) {
|
|
574
461
|
const event = JSON.parse(line);
|
|
575
462
|
if (event.type === 'user' && event.message?.role === 'user') {
|
|
576
|
-
|
|
577
|
-
if (typeof messageContent === 'string') {
|
|
578
|
-
const text = messageContent.trim();
|
|
579
|
-
lastMessage = text.substring(0, 50) + (text.length > 50 ? '...' : '');
|
|
580
|
-
}
|
|
581
|
-
else if (Array.isArray(messageContent)) {
|
|
582
|
-
const textContent = messageContent.find((c) => c.type === 'text');
|
|
583
|
-
if (textContent?.text) {
|
|
584
|
-
const text = textContent.text.trim();
|
|
585
|
-
lastMessage = text.substring(0, 50) + (text.length > 50 ? '...' : '');
|
|
586
|
-
}
|
|
587
|
-
}
|
|
463
|
+
lastMessage = this.extractUserMessageText(event.message.content) ?? lastMessage;
|
|
588
464
|
}
|
|
589
465
|
}
|
|
590
466
|
return lastMessage;
|
|
@@ -626,12 +502,6 @@ export class SessionManager {
|
|
|
626
502
|
return { turns: 0 };
|
|
627
503
|
}
|
|
628
504
|
}
|
|
629
|
-
/**
|
|
630
|
-
* 统计会话回合数(用户消息数)— 兼容旧调用
|
|
631
|
-
*/
|
|
632
|
-
countSessionTurns(projectPath, claudeSessionId) {
|
|
633
|
-
return this.getSessionFileInfo(projectPath, claudeSessionId).turns;
|
|
634
|
-
}
|
|
635
505
|
async getSessionByUuidPrefix(channel, channelId, uuidPrefix) {
|
|
636
506
|
const rows = this.db.prepare(`
|
|
637
507
|
SELECT * FROM sessions
|
|
@@ -642,18 +512,7 @@ export class SessionManager {
|
|
|
642
512
|
if (rows.length > 1) {
|
|
643
513
|
logger.warn(`Multiple sessions found with UUID prefix: ${uuidPrefix}`);
|
|
644
514
|
}
|
|
645
|
-
|
|
646
|
-
return {
|
|
647
|
-
id: row.id,
|
|
648
|
-
channel: row.channel,
|
|
649
|
-
channelId: row.channel_id,
|
|
650
|
-
projectPath: row.project_path,
|
|
651
|
-
claudeSessionId: row.claude_session_id,
|
|
652
|
-
name: row.name,
|
|
653
|
-
isActive: row.is_active === 1,
|
|
654
|
-
createdAt: row.created_at,
|
|
655
|
-
updatedAt: row.updated_at
|
|
656
|
-
};
|
|
515
|
+
return this.rowToSession(rows[0]);
|
|
657
516
|
}
|
|
658
517
|
async importCliSession(channel, channelId, projectPath, claudeSessionId) {
|
|
659
518
|
// 检查是否已存在相同项目路径的会话
|
|
@@ -671,23 +530,10 @@ export class SessionManager {
|
|
|
671
530
|
UPDATE sessions SET claude_session_id = ?, is_active = 1, updated_at = ?
|
|
672
531
|
WHERE id = ?
|
|
673
532
|
`).run(claudeSessionId, Date.now(), existingByPath.id);
|
|
674
|
-
return {
|
|
675
|
-
id: existingByPath.id,
|
|
676
|
-
channel: existingByPath.channel,
|
|
677
|
-
channelId: existingByPath.channel_id,
|
|
678
|
-
projectPath: existingByPath.project_path,
|
|
679
|
-
claudeSessionId,
|
|
680
|
-
name: existingByPath.name,
|
|
681
|
-
isActive: true,
|
|
682
|
-
createdAt: existingByPath.created_at,
|
|
683
|
-
updatedAt: Date.now()
|
|
684
|
-
};
|
|
533
|
+
return { ...this.rowToSession(existingByPath), claudeSessionId, isActive: true, updatedAt: Date.now() };
|
|
685
534
|
}
|
|
686
535
|
// 取消当前活跃会话
|
|
687
|
-
this.
|
|
688
|
-
UPDATE sessions SET is_active = 0, updated_at = ?
|
|
689
|
-
WHERE channel = ? AND channel_id = ? AND is_active = 1
|
|
690
|
-
`).run(Date.now(), channel, channelId);
|
|
536
|
+
this.deactivateAll(channel, channelId);
|
|
691
537
|
// 创建会话记录
|
|
692
538
|
const session = {
|
|
693
539
|
id: `${channel}-${channelId}-${Date.now()}`,
|
|
@@ -700,10 +546,7 @@ export class SessionManager {
|
|
|
700
546
|
createdAt: Date.now(),
|
|
701
547
|
updatedAt: Date.now()
|
|
702
548
|
};
|
|
703
|
-
this.
|
|
704
|
-
INSERT OR IGNORE INTO sessions (id, channel, channel_id, project_path, claude_session_id, name, is_active, created_at, updated_at)
|
|
705
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
706
|
-
`).run(session.id, session.channel, session.channelId, session.projectPath, session.claudeSessionId ?? null, session.name ?? null, 1, session.createdAt, session.updatedAt);
|
|
549
|
+
this.insertSession(session);
|
|
707
550
|
return session;
|
|
708
551
|
}
|
|
709
552
|
// ==================== 健康状态管理 ====================
|