@myassis/gateway 1.0.1 → 1.0.3

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.
Files changed (54) hide show
  1. package/dist/api/index.js +69 -59
  2. package/dist/config/index.js +2 -2
  3. package/dist/index.js +85 -11
  4. package/dist/middleware/auth.js +9 -25
  5. package/dist/middleware/errorHandler.js +1 -1
  6. package/dist/routes/agent.js +30 -19
  7. package/dist/routes/auth.js +35 -26
  8. package/dist/routes/chat.js +1 -1
  9. package/dist/routes/models.js +11 -14
  10. package/dist/routes/service.js +3 -6
  11. package/dist/routes/settings.js +11 -9
  12. package/dist/routes/skillHub.js +10 -6
  13. package/dist/routes/skills.js +13 -10
  14. package/dist/routes/tasks.js +6 -3
  15. package/dist/routes/upload.js +5 -2
  16. package/dist/routes/version.js +4 -4
  17. package/dist/services/LocalTaskService.js +2 -2
  18. package/dist/services/NotificationService.js +4 -4
  19. package/dist/services/ServiceManager.js +12 -12
  20. package/dist/services/TaskSchedulerService.js +21 -12
  21. package/dist/services/TaskService.js +25 -22
  22. package/dist/services/WebSocketService.js +16 -6
  23. package/dist/services/agent/Agent.js +7 -7
  24. package/dist/services/agent/AgentManager.js +26 -18
  25. package/dist/services/agent/AgentStore.js +1 -1
  26. package/dist/services/dataService.js +63 -73
  27. package/dist/services/index.js +9 -9
  28. package/dist/services/llm/LLMClient.js +11 -9
  29. package/dist/services/memory/MemoryManager.js +180 -17
  30. package/dist/services/model/index.js +1 -1
  31. package/dist/services/session/MigrationManager.js +1 -1
  32. package/dist/services/session/Session.js +74 -50
  33. package/dist/services/session/SessionManager.js +14 -7
  34. package/dist/services/session/SessionStore.js +2 -28
  35. package/dist/services/session/index.js +2 -2
  36. package/dist/services/systemPrompt.js +9 -5
  37. package/dist/services/task/PushTokenStore.js +2 -20
  38. package/dist/services/task/TaskStore.js +2 -21
  39. package/dist/services/tools/edit.js +122 -148
  40. package/dist/services/tools/exec.js +1 -1
  41. package/dist/services/tools/fetch.js +1 -1
  42. package/dist/services/tools/file.js +4 -9
  43. package/dist/services/tools/index.js +18 -17
  44. package/dist/services/tools/model.js +9 -7
  45. package/dist/services/tools/sessionsSpawn.js +54 -0
  46. package/dist/services/tools/skill.js +14 -13
  47. package/dist/services/tools/task.js +3 -5
  48. package/dist/stores/authStore.js +52 -66
  49. package/dist/stores/index.js +3 -3
  50. package/dist/stores/persistStore.js +37 -3
  51. package/package.json +7 -3
  52. package/scripts/fix-dist-imports.js +95 -0
  53. package/scripts/fix-imports.js +90 -0
  54. package/scripts/postbuild.js +39 -0
@@ -7,10 +7,10 @@
7
7
  * - /api/v1/data/* - 用户数据路由 (models, skills, tasks, settings)
8
8
  * - /api/v1/skill-hubs/* - 技能库路由
9
9
  */
10
- import { authApi, skillsApi, skillHubApi, modelsApi, settingsApi } from '../api';
11
- import { authStore, memoryStore, persistStore } from '../stores';
12
- import { taskService } from './TaskService';
13
- import { getLogger } from '@pocketclaw/shared';
10
+ import { authApi, skillsApi, skillHubApi, modelsApi, settingsApi } from '../api/index.js';
11
+ import { authStore, memoryStore, persistStore } from '../stores/index.js';
12
+ import { taskService } from './TaskService.js';
13
+ import { getLogger } from '@myassis/shared';
14
14
  const logger = getLogger('DataService');
15
15
  // ============ 认证服务 ============
16
16
  export const authService = {
@@ -20,7 +20,7 @@ export const authService = {
20
20
  if (response.success && response.data) {
21
21
  const expiresIn = response.data.expiresIn || 86400;
22
22
  authStore.save({
23
- token: response.data.accessToken,
23
+ accessToken: response.data.accessToken,
24
24
  refreshToken: response.data.refreshToken,
25
25
  expiresIn,
26
26
  expiresAt: Date.now() + expiresIn * 1000,
@@ -40,7 +40,7 @@ export const authService = {
40
40
  if (response.success && response.data) {
41
41
  const expiresIn = response.data.expiresIn || 86400;
42
42
  authStore.save({
43
- token: response.data.accessToken,
43
+ accessToken: response.data.accessToken,
44
44
  refreshToken: response.data.refreshToken,
45
45
  expiresIn,
46
46
  expiresAt: Date.now() + expiresIn * 1000,
@@ -50,9 +50,9 @@ export const authService = {
50
50
  return response;
51
51
  },
52
52
  // 登出
53
- logout: async () => {
53
+ logout: async (token) => {
54
54
  try {
55
- await authApi.logout();
55
+ await authApi.logout(token);
56
56
  }
57
57
  catch (e) {
58
58
  // 忽略错误
@@ -61,19 +61,15 @@ export const authService = {
61
61
  return { success: true };
62
62
  },
63
63
  // 获取当前用户
64
- me: () => authApi.me(),
64
+ me: (token) => authApi.me(token),
65
65
  // 更新资料
66
- updateProfile: (data) => authApi.updateProfile(data),
66
+ updateProfile: (data, token) => authApi.updateProfile(data, token),
67
67
  // 修改密码
68
- changePassword: (oldPassword, newPassword) => authApi.changePassword({ oldPassword, newPassword }),
68
+ changePassword: (oldPassword, newPassword, token) => authApi.changePassword({ oldPassword, newPassword }, token),
69
69
  // 删除账号
70
- deleteAccount: (password) => authApi.deleteAccount({ password }),
71
- // 获取认证状态
72
- isAuthenticated: () => authStore.isAuthenticated(),
70
+ deleteAccount: (password, token) => authApi.deleteAccount({ password }, token),
73
71
  // 获取当前用户(从内存)
74
- getUser: () => authStore.get()?.user || null,
75
- // 获取 Token
76
- getToken: () => authStore.getToken(),
72
+ getUser: (token) => authStore.get(token)?.user || null,
77
73
  // 刷新 Token
78
74
  refresh: async (refreshToken) => {
79
75
  const response = await authApi.refresh(refreshToken);
@@ -81,21 +77,15 @@ export const authService = {
81
77
  authStore.updateToken(response.accessToken, response.refreshToken);
82
78
  }
83
79
  return response;
84
- },
85
- // 检查认证(供 Desktop 使用)
86
- checkAuth: async () => {
87
- const isAuth = authStore.isAuthenticated();
88
- const user = authStore.get()?.user || null;
89
- return { success: true, authenticated: isAuth, user };
90
80
  }
91
81
  };
92
82
  // ============ 技能服务 (用户已安装的技能) ============
93
83
  export const skillsService = {
94
- list: async () => await skillsApi.list(),
95
- get: (skillId) => skillsApi.get(skillId),
96
- install: async (skillHubId) => {
84
+ list: async (token) => await skillsApi.list(token),
85
+ get: (skillId, token) => skillsApi.get(skillId, token),
86
+ install: async (skillHubId, token) => {
97
87
  // 1. 从技能库获取技能详情
98
- const hubResult = await skillHubApi.get(skillHubId);
88
+ const hubResult = await skillHubApi.get(skillHubId, token);
99
89
  if (!hubResult.success || !hubResult.data) {
100
90
  return { success: false, error: '获取技能详情失败' };
101
91
  }
@@ -110,26 +100,26 @@ export const skillsService = {
110
100
  isActive: true,
111
101
  apiKeyRequired: hub.apiKeyRequired || false,
112
102
  };
113
- const response = await skillsApi.create(skillData);
103
+ const response = await skillsApi.create(skillData, token);
114
104
  if (response.success) {
115
105
  // 3. 增加安装计数
116
- await skillHubApi.install(skillHubId);
106
+ await skillHubApi.install(skillHubId, token);
117
107
  }
118
108
  return response;
119
109
  },
120
- update: (skillId, data) => skillsApi.update(skillId, data),
121
- uninstall: async (skillId) => await skillsApi.uninstall(skillId),
122
- setApiKey: (skillId, apiKey) => {
110
+ update: (skillId, data, token) => skillsApi.update(skillId, data, token),
111
+ uninstall: async (skillId, token) => await skillsApi.uninstall(skillId, token),
112
+ setApiKey: (skillId, apiKey, token) => {
123
113
  persistStore.setSkillApiKey(skillId, apiKey);
124
114
  return { success: true };
125
115
  },
126
- getApiKey: async (skillId) => {
116
+ getApiKey: async (skillId, token) => {
127
117
  const apikey = persistStore.getSkillApiKey(skillId);
128
118
  if (!apikey) {
129
119
  // 判断是否有内置 apikey
130
- const skill = (await skillsService.get(skillId)).data;
120
+ const skill = (await skillsService.get(skillId, token)).data;
131
121
  if (skill.templateId) {
132
- const skillHub = (await skillHubService.get(skill.templateId)).data;
122
+ const skillHub = (await skillHubService.get(skill.templateId, token)).data;
133
123
  if (skillHub?.builtinApikey) {
134
124
  persistStore.setSkillApiKey(skillId, skillHub.builtinApikey);
135
125
  return { success: true, apiKey: skillHub.builtinApikey };
@@ -138,13 +128,13 @@ export const skillsService = {
138
128
  }
139
129
  return { success: true, apiKey: apikey?.apiKey };
140
130
  },
141
- rate: (skillId, score) => skillsApi.rate(skillId, { score }),
142
- parse: (content) => skillsApi.parse({ content }),
143
- create: async (data) => {
144
- const response = await skillsApi.create(data);
131
+ rate: (skillId, score, token) => skillsApi.rate(skillId, { score }, token),
132
+ parse: (content, token) => skillsApi.parse({ content }, token),
133
+ create: async (data, token) => {
134
+ const response = await skillsApi.create(data, token);
145
135
  if (response.success) {
146
136
  // 创建成功后更新技能列表缓存
147
- const listResponse = await skillsApi.list();
137
+ const listResponse = await skillsApi.list(token);
148
138
  if (listResponse.success) {
149
139
  memoryStore.set('skills', listResponse.data);
150
140
  }
@@ -154,28 +144,28 @@ export const skillsService = {
154
144
  };
155
145
  // ============ 技能库服务 (可安装的技能) ============
156
146
  export const skillHubService = {
157
- list: (params) => skillHubApi.list(params),
158
- get: (hubId) => skillHubApi.get(hubId),
159
- categories: () => skillHubApi.categories(),
160
- preinstalled: () => skillHubApi.preinstalled(),
161
- user: () => skillHubApi.user(),
162
- checkUsed: (hubId) => skillHubApi.checkUsed(hubId),
163
- rate: (hubId, rating) => skillHubApi.rate(hubId, rating),
164
- userRating: (hubId) => skillHubApi.userRating(hubId),
165
- install: (hubId) => skillHubApi.install(hubId),
166
- use: (hubId) => skillHubApi.use(hubId),
147
+ list: (token, params) => skillHubApi.list(params, token),
148
+ get: (hubId, token) => skillHubApi.get(hubId, token),
149
+ categories: (token) => skillHubApi.categories(token),
150
+ preinstalled: (token) => skillHubApi.preinstalled(token),
151
+ user: (token) => skillHubApi.user(token),
152
+ checkUsed: (hubId, token) => skillHubApi.checkUsed(hubId, token),
153
+ rate: (hubId, rating, token) => skillHubApi.rate(hubId, rating, token),
154
+ userRating: (hubId, token) => skillHubApi.userRating(hubId, token),
155
+ install: (hubId, token) => skillHubApi.install(hubId, token),
156
+ use: (hubId, token) => skillHubApi.use(hubId, token),
167
157
  };
168
158
  // ============ 模型服务 ============
169
159
  export const modelsService = {
170
- list: () => modelsApi.list(),
171
- get: (modelId) => modelsApi.get(modelId),
172
- create: (data) => modelsApi.create(data),
173
- update: (modelId, data) => modelsApi.update(modelId, data),
174
- delete: (modelId) => modelsApi.delete(modelId),
175
- setPrimary: (modelId) => modelsApi.setPrimary(modelId),
160
+ list: (token) => modelsApi.list(token),
161
+ get: (modelId, token) => modelsApi.get(modelId, token),
162
+ create: (data, token) => modelsApi.create(data, token),
163
+ update: (modelId, data, token) => modelsApi.update(modelId, data, token),
164
+ delete: (modelId, token) => modelsApi.delete(modelId, token),
165
+ setPrimary: (modelId, token) => modelsApi.setPrimary(modelId, token),
176
166
  };
177
167
  export const tasksService = {
178
- list: async (params, userId) => {
168
+ list: async (userId, params) => {
179
169
  const result = await taskService.listTasks({
180
170
  status: params?.status,
181
171
  userId,
@@ -184,13 +174,13 @@ export const tasksService = {
184
174
  });
185
175
  return { success: true, data: result.tasks, total: result.total };
186
176
  },
187
- get: async (taskId) => {
188
- const task = await taskService.getTask(taskId);
177
+ get: async (taskId, token) => {
178
+ const task = await taskService.getTask(taskId, token);
189
179
  if (!task)
190
180
  return { success: false, error: 'Task not found' };
191
181
  return { success: true, data: task };
192
182
  },
193
- create: async (data, userId) => {
183
+ create: async (data, userId, token) => {
194
184
  const uid = userId || data?.userId || 'local';
195
185
  try {
196
186
  const result = await taskService.createTask({
@@ -206,7 +196,7 @@ export const tasksService = {
206
196
  endTime: data.endTime,
207
197
  platformApply: data.platformApply,
208
198
  pushToken: data.pushToken,
209
- });
199
+ }, token);
210
200
  return { success: true, data: { id: result.id, isLocal: result.isLocal } };
211
201
  }
212
202
  catch (error) {
@@ -214,7 +204,7 @@ export const tasksService = {
214
204
  return { success: false, error: error.message };
215
205
  }
216
206
  },
217
- update: async (taskId, data) => {
207
+ update: async (taskId, data, token) => {
218
208
  try {
219
209
  const result = await taskService.updateTask(taskId, {
220
210
  title: data.title,
@@ -229,7 +219,7 @@ export const tasksService = {
229
219
  status: data.status,
230
220
  platformApply: data.platformApply,
231
221
  pushToken: data.pushToken,
232
- });
222
+ }, token);
233
223
  return { success: result };
234
224
  }
235
225
  catch (error) {
@@ -237,9 +227,9 @@ export const tasksService = {
237
227
  return { success: false, error: error.message };
238
228
  }
239
229
  },
240
- delete: async (taskId) => {
230
+ delete: async (taskId, token) => {
241
231
  try {
242
- const result = await taskService.deleteTask(taskId);
232
+ const result = await taskService.deleteTask(taskId, token);
243
233
  return { success: result };
244
234
  }
245
235
  catch (error) {
@@ -247,9 +237,9 @@ export const tasksService = {
247
237
  return { success: false, error: error.message };
248
238
  }
249
239
  },
250
- cancel: async (taskId) => {
240
+ cancel: async (taskId, token) => {
251
241
  try {
252
- const result = await taskService.cancelTask(taskId);
242
+ const result = await taskService.cancelTask(taskId, token);
253
243
  return { success: result };
254
244
  }
255
245
  catch (error) {
@@ -257,9 +247,9 @@ export const tasksService = {
257
247
  return { success: false, error: error.message };
258
248
  }
259
249
  },
260
- updateStatus: async (taskId, status) => {
250
+ updateStatus: async (taskId, status, token) => {
261
251
  try {
262
- const result = await taskService.updateTaskStatus(taskId, status);
252
+ const result = await taskService.updateTaskStatus(taskId, status, token);
263
253
  return { success: result };
264
254
  }
265
255
  catch (error) {
@@ -270,21 +260,21 @@ export const tasksService = {
270
260
  };
271
261
  // ============ 设置服务 ============
272
262
  export const settingsService = {
273
- get: async () => {
263
+ get: async (token) => {
274
264
  const settings = memoryStore.get('settings'); // 预热缓存
275
265
  if (settings) {
276
266
  return { success: true, data: settings };
277
267
  }
278
268
  else {
279
- const response = await settingsApi.get();
269
+ const response = await settingsApi.get(token);
280
270
  if (response.success) {
281
271
  memoryStore.set('settings', response.data); // 更新缓存
282
272
  }
283
273
  return response;
284
274
  }
285
275
  },
286
- update: async (data) => {
287
- const response = await settingsApi.update(data);
276
+ update: async (data, token) => {
277
+ const response = await settingsApi.update(data, token);
288
278
  if (response.success) {
289
279
  memoryStore.set('settings', response.data); // 更新缓存
290
280
  }
@@ -1,15 +1,15 @@
1
1
  // Re-export all services from dataService
2
- export { authService, skillsService, skillHubService, modelsService, settingsService, } from './dataService';
2
+ export { authService, skillsService, skillHubService, modelsService, settingsService, } from './dataService.js';
3
3
  // Re-export tasksService from dataService (server-only tasks)
4
- export { tasksService } from './dataService';
4
+ export { tasksService } from './dataService.js';
5
5
  // Re-export session manager
6
- export { sessionManager } from './session';
6
+ export { getSessionManager } from './session.js';
7
7
  // Re-export task services
8
- export { taskSchedulerService } from './TaskSchedulerService';
9
- export { taskService } from './TaskService';
10
- export { localTaskService } from './LocalTaskService';
8
+ export { taskSchedulerService } from './TaskSchedulerService.js';
9
+ export { taskService } from './TaskService.js';
10
+ export { localTaskService } from './LocalTaskService.js';
11
11
  // Re-export notification and holiday services
12
- export { notificationService } from './NotificationService';
12
+ export { notificationService } from './NotificationService.js';
13
13
  // Re-export task stores
14
- export { TaskStore } from './task/TaskStore';
15
- export { PushTokenStore } from './task/PushTokenStore';
14
+ export { TaskStore } from './task/TaskStore.js';
15
+ export { PushTokenStore } from './task/PushTokenStore.js';
@@ -1,5 +1,5 @@
1
1
  import { persistStore } from "@/stores";
2
- import { getLogger } from '@pocketclaw/shared';
2
+ import { getLogger } from '@myassis/shared';
3
3
  import fs from 'fs/promises';
4
4
  const logger = getLogger('LLMClient');
5
5
  /**
@@ -249,6 +249,7 @@ export class LLMClient {
249
249
  }
250
250
  else {
251
251
  // OpenAI compatible API
252
+ let existAttachments = false;
252
253
  const body = {
253
254
  model: model.modelId,
254
255
  messages: this.messages.map((m) => {
@@ -319,14 +320,8 @@ export class LLMClient {
319
320
  }
320
321
  // 处理用户消息中的附件
321
322
  if (m.role === 'user' && m.attachments && m.attachments.length > 0) {
323
+ existAttachments = true;
322
324
  const contentArray = [];
323
- // 如果有文本内容,先添加文本
324
- if (m.content && m.content.trim()) {
325
- contentArray.push({
326
- type: 'text',
327
- text: m.content,
328
- });
329
- }
330
325
  // 添加附件
331
326
  for (const attachment of m.attachments) {
332
327
  if (attachment.type === 'image') {
@@ -373,6 +368,13 @@ export class LLMClient {
373
368
  }
374
369
  }
375
370
  }
371
+ // 如果有文本内容,添加文本
372
+ if (m.content && m.content.trim()) {
373
+ contentArray.push({
374
+ type: 'text',
375
+ text: m.content,
376
+ });
377
+ }
376
378
  return [{
377
379
  role: m.role,
378
380
  content: contentArray,
@@ -392,7 +394,7 @@ export class LLMClient {
392
394
  }
393
395
  const tokens = JSON.stringify(body).length;
394
396
  logger.debug('tokens', tokens);
395
- if (tokens > 100000) {
397
+ if (tokens > 200000) {
396
398
  logger.warn('message is too large and has writen to the file debug.json');
397
399
  await fs.writeFile('debug.json', JSON.stringify(body.messages), 'utf-8');
398
400
  throw Error('exceed max message tokens');
@@ -1,8 +1,9 @@
1
- import { getLogger } from '@pocketclaw/shared';
1
+ import { getLogger } from '@myassis/shared';
2
2
  const logger = getLogger('MemoryManager');
3
- import { toModel } from '../models';
4
- import { modelsService } from '../dataService';
5
- import { LLMClient } from '../llm/LLMClient';
3
+ import { toModel } from '../models.js';
4
+ import { modelsService } from '../dataService.js';
5
+ import { authStore } from '@/stores';
6
+ import { LLMClient } from '../llm/LLMClient.js';
6
7
  import { appConfig } from '@/config';
7
8
  const DEFAULT_CONFIG = {
8
9
  summaryThreshold: 10,
@@ -16,9 +17,11 @@ export class MemoryManager {
16
17
  session;
17
18
  signal;
18
19
  config = DEFAULT_CONFIG;
19
- constructor(session, signal) {
20
+ childAgent;
21
+ constructor(session, signal, childAgent) {
20
22
  this.session = session;
21
23
  this.signal = signal;
24
+ this.childAgent = childAgent;
22
25
  }
23
26
  toSummaryMessage(summary, createdAt, modelName) {
24
27
  return {
@@ -32,7 +35,11 @@ export class MemoryManager {
32
35
  }
33
36
  async getHistoryMessagesAsync() {
34
37
  // 1. 获取所有消息
35
- const messages = this.session.getMessages();
38
+ let messages = this.session.getMessages();
39
+ if (this.childAgent) {
40
+ messages.splice(-1);
41
+ logger.debug('history', messages);
42
+ }
36
43
  // 2. 消息少于摘要配置的阈值数,直接返回
37
44
  logger.debug('message length:', messages.length);
38
45
  if (messages.length < this.config.summaryThreshold) {
@@ -77,32 +84,120 @@ export class MemoryManager {
77
84
  }
78
85
  }
79
86
  }
87
+ /**
88
+ * 格式化工具调用内容,提取关键信息供摘要模型理解
89
+ */
90
+ formatToolCalls(toolCalls) {
91
+ if (!toolCalls || toolCalls.length === 0)
92
+ return '';
93
+ return toolCalls.map(tc => {
94
+ const parts = [];
95
+ // 外层 toolCall 节点
96
+ if (tc.content)
97
+ parts.push(`思路: ${tc.content}`);
98
+ if (tc.reasoningContent)
99
+ parts.push(`推理: ${tc.reasoningContent}`);
100
+ // 内层工具调用详情
101
+ if (tc.toolCalls && tc.toolCalls.length > 0) {
102
+ const callDetails = tc.toolCalls.map((call) => {
103
+ const name = `${call.toolName}${call.actionName ? '.' + call.actionName : ''}`;
104
+ let detail = name;
105
+ if (call.input) {
106
+ try {
107
+ const input = typeof call.input === 'string' ? JSON.parse(call.input) : call.input;
108
+ // 提取关键输入信息,避免过长
109
+ const inputStr = JSON.stringify(input).slice(0, 500);
110
+ detail += ` 输入: ${inputStr}`;
111
+ }
112
+ catch {
113
+ detail += ` 输入: ${String(call.input).slice(0, 500)}`;
114
+ }
115
+ }
116
+ if (call.output) {
117
+ const outputStr = typeof call.output === 'string' ? call.output : JSON.stringify(call.output);
118
+ detail += ` 结果: ${outputStr.slice(0, 500)}`;
119
+ }
120
+ if (call.status === 'error')
121
+ detail += ' [失败]';
122
+ return detail;
123
+ }).join('; ');
124
+ parts.push(`工具调用: ${callDetails}`);
125
+ }
126
+ return parts.join('\n');
127
+ }).join('\n');
128
+ }
129
+ /**
130
+ * 格式化单条消息,提取有意义的内容
131
+ */
132
+ formatMessage(m) {
133
+ const role = m.role === 'user' ? '用户' : m.role === 'assistant' ? '助手' : '系统';
134
+ let content = m.content || '';
135
+ // 附加工具调用信息
136
+ const toolCallInfo = this.formatToolCalls(m.toolCalls);
137
+ if (toolCallInfo) {
138
+ content += `\n[${toolCallInfo}]`;
139
+ }
140
+ // 截断过长内容
141
+ if (content.length > 2000) {
142
+ content = content.slice(0, 2000) + '...(内容过长已截断)';
143
+ }
144
+ return `${role}: ${content}`;
145
+ }
80
146
  /**
81
147
  * 生成摘要提示词
82
148
  */
83
149
  buildSummaryPrompt(messages, lastSummary) {
84
150
  const conversation = messages
85
151
  .filter((m) => m.role === 'user' || m.role === 'assistant')
86
- .map((m) => `${m.role === 'user' ? '用户' : '助手'}: ${m.content}${m.toolCalls ? JSON.stringify(m.toolCalls) : ''}`)
87
- .join('\n');
88
- return `请分析以下对话内容,生成一个简洁的摘要,保留关键信息、错误信息和上下文。
152
+ .map((m) => this.formatMessage(m))
153
+ .join('\n\n');
154
+ return `你是一个对话摘要专家。请分析以下对话内容,生成一份结构化的摘要。
155
+
156
+ 重要规则:
157
+ 1. 如果对话涉及代码开发/项目工作,必须详细总结项目结构,包括:技术栈、目录结构、关键文件及其职责、模块间关系
158
+ 2. 项目结构描述不限字数,务必完整清晰
159
+ 3. 保留所有关键决策、修改内容和上下文
160
+ 4. 保留错误信息和解决方案,避免重复犯错
161
+ 5. 如果有上一轮摘要,合并其中的关键信息
89
162
 
90
- 对话内容:
163
+ ${lastSummary ? '=== 上一轮摘要 ===\n' + lastSummary + '\n\n' : ''}=== 对话内容 ===
91
164
  ${conversation}
92
165
 
93
- ${lastSummary ? '上一次内容摘要:\n' + lastSummary : ''}
166
+ === 输出格式 ===
167
+ 请按以下格式输出(各部分字数不限,以完整准确为优先):
168
+
169
+ 【摘要】简要概括对话主题和关键进展
170
+
171
+ 【项目结构】(如涉及项目开发)详细描述:
172
+ - 技术栈与框架
173
+ - 目录结构与各目录职责
174
+ - 关键文件及其作用
175
+ - 模块间依赖关系
176
+ - 已完成和待完成的功能
94
177
 
95
- 请用以下格式返回摘要(只返回摘要,不要其他内容):
96
- 【摘要】:简要概括对话主题和关键信息(不超过100字)
97
- 【要点】:列出2-5个关键要点
98
- 【错误】:列出犯过的错误`;
178
+ 【关键决策】列出重要决策及原因
179
+
180
+ 【要点】列出3-8个关键要点
181
+
182
+ 【错误与修复】列出遇到的错误及解决方案,标注"⚠️"避免重复`;
99
183
  }
100
184
  /**
101
- * 生成对话摘要
185
+ * 生成对话摘要(支持分层摘要)
102
186
  */
103
187
  async generateSummaryAsync(messages, lastSummary) {
188
+ const MAX_INPUT_CHARS = 30000; // 单次摘要最大输入字符数
189
+ // 格式化所有消息用于估算长度
190
+ const formattedMessages = messages
191
+ .filter((m) => m.role === 'user' || m.role === 'assistant')
192
+ .map((m) => this.formatMessage(m));
193
+ const totalChars = formattedMessages.join('').length;
194
+ if (totalChars > MAX_INPUT_CHARS && formattedMessages.length > 20) {
195
+ // 分层摘要:分批生成子摘要,再合并
196
+ return await this.hierarchicalSummary(formattedMessages, lastSummary);
197
+ }
104
198
  const summaryPrompt = this.buildSummaryPrompt(messages, lastSummary);
105
- const llmClient = new LLMClient((await modelsService.list()).data.map((x) => toModel(x)), [{ role: 'user', content: summaryPrompt }], this.signal, []);
199
+ const token = authStore.get(this.session.userId).accessToken;
200
+ const llmClient = new LLMClient((await modelsService.list(token)).data.map((x) => toModel(x)), [{ role: 'user', content: summaryPrompt }], this.signal, []);
106
201
  const response = await llmClient.Chat();
107
202
  const content = (response.content || '').trim();
108
203
  if (!content) {
@@ -114,4 +209,72 @@ ${lastSummary ? '上一次内容摘要:\n' + lastSummary : ''}
114
209
  }
115
210
  return content;
116
211
  }
212
+ /**
213
+ * 分层摘要:将消息分批生成子摘要,再合并为最终摘要
214
+ */
215
+ async hierarchicalSummary(formattedMessages, lastSummary) {
216
+ const BATCH_SIZE = 15; // 每批消息数
217
+ const subSummaries = [];
218
+ for (let i = 0; i < formattedMessages.length; i += BATCH_SIZE) {
219
+ if (this.signal.aborted)
220
+ break;
221
+ const batch = formattedMessages.slice(i, i + BATCH_SIZE);
222
+ const batchPrompt = `请简洁总结以下对话片段的关键信息,特别关注:项目结构、关键决策、错误及修复。\n\n${batch.join('\n\n')}`;
223
+ const token = authStore.get(this.session.userId).accessToken;
224
+ const llmClient = new LLMClient((await modelsService.list(token)).data.map((x) => toModel(x)), [{ role: 'user', content: batchPrompt }], this.signal, []);
225
+ const response = await llmClient.Chat();
226
+ const subContent = (response.content || '').trim();
227
+ if (subContent) {
228
+ subSummaries.push(subContent);
229
+ }
230
+ }
231
+ // 合并所有子摘要
232
+ if (subSummaries.length === 0) {
233
+ return lastSummary || '(摘要生成失败)';
234
+ }
235
+ if (subSummaries.length === 1) {
236
+ return subSummaries[0];
237
+ }
238
+ // 合并子摘要为最终摘要
239
+ const mergePrompt = this.buildMergePrompt(subSummaries, lastSummary);
240
+ const token = authStore.get(this.session.userId).accessToken;
241
+ const mergeLlmClient = new LLMClient((await modelsService.list(token)).data.map((x) => toModel(x)), [{ role: 'user', content: mergePrompt }], this.signal, []);
242
+ const mergeResponse = await mergeLlmClient.Chat();
243
+ const mergedContent = (mergeResponse.content || '').trim();
244
+ return mergedContent || subSummaries.join('\n\n');
245
+ }
246
+ /**
247
+ * 构建合并摘要提示词
248
+ */
249
+ buildMergePrompt(subSummaries, lastSummary) {
250
+ const summaries = subSummaries
251
+ .map((s, i) => `--- 片段 ${i + 1} ---\n${s}`)
252
+ .join('\n\n');
253
+ return `你是一个对话摘要专家。以下是多个对话片段的摘要,请合并为一份完整的结构化摘要。
254
+
255
+ 重要规则:
256
+ 1. 合并重复信息,去重项目结构描述
257
+ 2. 如果涉及项目开发,务必完整保留项目结构信息(技术栈、目录结构、关键文件、模块关系)
258
+ 3. 按时间顺序整理决策和进展
259
+ 4. 保留所有错误信息及解决方案
260
+
261
+ ${lastSummary ? '=== 上一轮摘要 ===\n' + lastSummary + '\n\n' : ''}=== 分片段摘要 ===
262
+ ${summaries}
263
+
264
+ === 输出格式 ===
265
+ 【摘要】简要概括对话主题和关键进展
266
+
267
+ 【项目结构】(如涉及项目开发)详细描述:
268
+ - 技术栈与框架
269
+ - 目录结构与各目录职责
270
+ - 关键文件及其作用
271
+ - 模块间依赖关系
272
+ - 已完成和待完成的功能
273
+
274
+ 【关键决策】列出重要决策及原因
275
+
276
+ 【要点】列出3-8个关键要点
277
+
278
+ 【错误与修复】列出遇到的错误及解决方案,标注"⚠️"避免重复`;
279
+ }
117
280
  }
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * 模型选择模块
3
3
  */
4
- export * from './ModelCapabilities';
4
+ export * from './ModelCapabilities.js';
@@ -1,6 +1,6 @@
1
1
  import path from 'path';
2
2
  import fs from 'fs';
3
- import { getLogger } from '@pocketclaw/shared';
3
+ import { getLogger } from '@myassis/shared';
4
4
  const logger = getLogger('MigrationManager');
5
5
  export class MigrationManager {
6
6
  db;