foliko 1.1.13 → 1.1.15

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 (102) hide show
  1. package/.agent/data/plugins-state.json +1 -1
  2. package/.agent/data/weixin/images/file_1776188148383jpg +0 -0
  3. package/.agent/data/weixin/images/file_1776188458326.jpg +0 -0
  4. package/.agent/data/weixin/images/file_1776188689423.jpg +0 -0
  5. package/.agent/data/weixin/images/file_1776188813604.jpg +0 -0
  6. package/.agent/data/weixin/images/file_1776189097450.jpg +0 -0
  7. package/.agent/data/weixin/videos/file_1776188318431.mp4 +0 -0
  8. package/.agent/mcp_config.json +7 -0
  9. package/.agent/memory/feedback/mnxe0cxc-14l6q5.md +17 -0
  10. package/.agent/memory/feedback/mnxe11pa-nxf577.md +9 -0
  11. package/.agent/memory/feedback/mnxe1an2-84faff.md +9 -0
  12. package/.agent/memory/feedback/mnxgcfj0-qg3wjc.md +9 -0
  13. package/.agent/memory/feedback/mnxgcn3y-40mqss.md +9 -0
  14. package/.agent/memory/feedback/mnxgcxq9-jm7ydl.md +9 -0
  15. package/.agent/memory/feedback/mnxgdyfj-pzjvkb.md +9 -0
  16. package/.agent/memory/feedback/mnxge3z1-7vyit1.md +9 -0
  17. package/.agent/memory/feedback/mnxhrg28-41hhjr.md +9 -0
  18. package/.agent/memory/feedback/mnxhrx0e-yth94k.md +9 -0
  19. package/.agent/memory/feedback/mnxhs3jd-rvx8aq.md +9 -0
  20. package/.agent/memory/feedback/mnxhs7p7-g5rtn9.md +9 -0
  21. package/.agent/memory/feedback/mnxhslx5-oqwuhr.md +9 -0
  22. package/.agent/memory/feedback/mnxhsvd6-nuyvvc.md +9 -0
  23. package/.agent/memory/project/mnxegq6z-5fc64w.md +22 -0
  24. package/.agent/memory/project/mnxh2w4r-le9hur.md +17 -0
  25. package/.agent/memory/project/mnxhq2yv-9qa8ay.md +31 -0
  26. package/.agent/memory/project/mnxhql11-iaun2o.md +34 -0
  27. package/.agent/memory/project/mnxhr78p-jpg7eq.md +23 -0
  28. package/.agent/memory/reference/mnxe0oa9-p6wzk6.md +27 -0
  29. package/.agent/memory/reference/mnxehcll-kcrmpf.md +29 -0
  30. package/.agent/memory/reference/mnxei0ts-jw091y.md +18 -0
  31. package/.agent/memory/reference/mnxfnrr4-rski36.md +40 -0
  32. package/.agent/memory/reference/mnxfo6n5-af9zls.md +18 -0
  33. package/.agent/memory/reference/mnxh2ady-u6cmvk.md +61 -0
  34. package/.agent/memory/reference/mnxhqdqh-ucsbsk.md +31 -0
  35. package/.agent/memory/reference/mnxiixyp-rz2gvw.md +34 -0
  36. package/.agent/memory/user/mnxhqxk3-vjjhlf.md +23 -0
  37. package/.agent/sessions/cli_default.json +11 -639
  38. package/.agent/sessions/weixin_o9cq80zgZqKPA2-s59PN43GdDy1w@im.wechat.json +25 -0
  39. package/.claude/settings.local.json +23 -1
  40. package/cli/src/commands/chat.js +9 -15
  41. package/cli/src/ui/chat-ui.js +40 -71
  42. package/package.json +4 -2
  43. package/plugins/default-plugins.js +5 -5
  44. package/plugins/file-system-plugin.js +1 -1
  45. package/plugins/memory-plugin.js +12 -12
  46. package/plugins/plugin-manager-plugin.js +1 -0
  47. package/plugins/subagent-plugin.js +55 -1
  48. package/plugins/telegram-plugin.js +9 -6
  49. package/plugins/weixin-plugin.js +75 -78
  50. package/src/core/agent-chat.js +468 -1612
  51. package/src/core/agent.js +53 -134
  52. package/src/core/chat-session.js +423 -0
  53. package/src/core/context-compressor.js +473 -0
  54. package/src/core/context-manager.js +0 -48
  55. package/src/core/framework.js +95 -68
  56. package/src/core/index.js +11 -0
  57. package/src/core/notification-manager.js +125 -0
  58. package/src/core/subagent.js +295 -0
  59. package/src/core/token-counter.js +190 -0
  60. package/src/core/tool-executor.js +270 -0
  61. package/src/executors/mcp-executor.js +14 -1
  62. package/src/utils/download.js +596 -0
  63. package/system.md +312 -2373
  64. package/.agent/agents/code-assistant.json +0 -17
  65. package/.agent/agents/email-assistant.json +0 -14
  66. package/.agent/agents/file-assistant.json +0 -18
  67. package/.agent/agents/orchestrator-demo.md +0 -53
  68. package/.agent/agents/orchestrator.json +0 -7
  69. package/.agent/agents/poster-expert.md +0 -228
  70. package/.agent/agents/system-assistant.json +0 -15
  71. package/.agent/agents/web-assistant.json +0 -12
  72. package/.agent/memory/feedback/mnv3nu27-3o15pf.md +0 -9
  73. package/.agent/memory/feedback/mnv3o078-b959yj.md +0 -9
  74. package/.agent/memory/feedback/mnv3o6ej-u0fif5.md +0 -9
  75. package/.agent/memory/feedback/mnv3obgl-bkkjoj.md +0 -9
  76. package/.agent/memory/feedback/mnv4a3js-dv6onx.md +0 -9
  77. package/.agent/memory/feedback/mnv4aacm-sxxowp.md +0 -9
  78. package/.agent/memory/feedback/mnv4ahto-w40ffm.md +0 -9
  79. package/.agent/memory/feedback/mnv4anvp-3cs06y.md +0 -9
  80. package/.agent/memory/feedback/mnvzgvtd-0o2900.md +0 -9
  81. package/.agent/memory/feedback/mnvzhajn-swbx61.md +0 -15
  82. package/.agent/memory/feedback/mnvzhgsp-p5vog3.md +0 -9
  83. package/.agent/memory/feedback/mnvzho0c-fgql7q.md +0 -14
  84. package/.agent/memory/feedback/mnvzhtzq-ufr5at.md +0 -9
  85. package/.agent/memory/feedback/mnvzhyb3-9byq2z.md +0 -9
  86. package/.agent/memory/feedback/mnvzi7hp-hyeafp.md +0 -9
  87. package/.agent/memory/feedback/mnvzibph-z7rwp5.md +0 -9
  88. package/.agent/memory/feedback/mnvzilys-7h176w.md +0 -14
  89. package/.agent/memory/feedback/mnvziuh5-zjshci.md +0 -9
  90. package/.agent/memory/feedback/mnw07wde-6zqsc8.md +0 -9
  91. package/.agent/memory/feedback/mnw084bp-j0ba2a.md +0 -9
  92. package/.agent/memory/user/mnv3n62r-y0h79j.md +0 -21
  93. package/.agent/memory/user/mnv3n9yf-ead4g8.md +0 -13
  94. package/.agent/memory/user/mnv3ne3j-82tq1k.md +0 -19
  95. package/.agent/memory/user/mnv3nhgm-g2s2us.md +0 -11
  96. package/.agent/memory/user/mnv3nl9u-ejd998.md +0 -16
  97. package/.agent/memory/user/mnv3nofp-ya5szl.md +0 -10
  98. package/.agent/memory/user/mnv49qne-bhk0ki.md +0 -9
  99. package/.agent/memory/user/mnv49w3y-rzr8ju.md +0 -13
  100. package/.agent/sessions/test.json +0 -16
  101. package/plugins/python-plugin-loader.js.bak +0 -856
  102. package/src/core/agent-context.js +0 -188
@@ -0,0 +1,473 @@
1
+ /**
2
+ * ContextCompressor - 上下文压缩器
3
+ *
4
+ * 职责:
5
+ * 1. AI 智能压缩(生成摘要)
6
+ * 2. 简单压缩(截断保留最近消息)
7
+ * 3. 消息配对验证(防止 orphaned tool result)
8
+ */
9
+
10
+ const { logger } = require('../utils/logger');
11
+ const { zodSchemaToMarkdown, zodSchemaToTable } = require('@chnak/zod-to-markdown');
12
+
13
+ // 模型上下文限制表
14
+ const MODEL_CONTEXT_LIMITS = {
15
+ 'deepseek-chat': 128000,
16
+ 'deepseek-coder': 128000,
17
+ 'deepseek-reasoner': 128000,
18
+ 'MiniMax-M2.7': 110000,
19
+ 'gpt-4': 100000,
20
+ 'gpt-4o': 100000,
21
+ 'gpt-4o-mini': 100000,
22
+ 'gpt-4-turbo': 100000,
23
+ 'claude-3-5-sonnet': 150000,
24
+ 'claude-3-opus': 150000,
25
+ 'claude-3-sonnet': 150000,
26
+ 'glm-5.1': 200000,
27
+ };
28
+
29
+ // 压缩超时
30
+ const COMPRESSION_TIMEOUT = 120000;
31
+
32
+ class ContextCompressor {
33
+ /**
34
+ * @param {Object} config - 配置
35
+ */
36
+ constructor(config = {}) {
37
+ this.config = config;
38
+ this.agent = config.agent;
39
+ this.framework = config.framework;
40
+
41
+ // 模型相关配置
42
+ this.model = config.model || 'deepseek-chat';
43
+ this._maxContextTokens = config.maxContextTokens || this._getDefaultContextLimit();
44
+ this._keepRecentMessages = config.keepRecentMessages || 20;
45
+ this._enableSmartCompress = config.enableSmartCompress !== false;
46
+
47
+ // 压缩状态
48
+ this._compressionCount = 0;
49
+ this._compressionInProgress = false;
50
+ this._compressionPromise = null;
51
+
52
+ // 工具结果压缩配置
53
+ this._maxToolResultSize = config.maxToolResultSize || 4000;
54
+ }
55
+
56
+ /**
57
+ * 获取默认上下文限制
58
+ * @returns {number}
59
+ * @private
60
+ */
61
+ _getDefaultContextLimit() {
62
+ const modelKey = Object.keys(MODEL_CONTEXT_LIMITS).find((k) =>
63
+ this.model.toLowerCase().includes(k.toLowerCase())
64
+ );
65
+ return modelKey ? MODEL_CONTEXT_LIMITS[modelKey] : 40000;
66
+ }
67
+
68
+ /**
69
+ * 压缩上下文
70
+ * @param {string} sessionId - Session ID
71
+ * @param {Array} messages - 消息数组引用
72
+ * @param {Object} messageStore - 消息存储
73
+ * @returns {Promise}
74
+ */
75
+ async compress(sessionId, messages, messageStore) {
76
+ if (this._compressionInProgress && this._compressionPromise) {
77
+ logger.debug('Compression already in progress, waiting...');
78
+ return this._compressionPromise;
79
+ }
80
+
81
+ if (messages.length <= this._keepRecentMessages) {
82
+ return;
83
+ }
84
+
85
+ this._compressionInProgress = true;
86
+
87
+ this._compressionPromise = this._executeWithTimeout(sessionId, messages, messageStore).finally(
88
+ () => {
89
+ this._compressionInProgress = false;
90
+ this._compressionPromise = null;
91
+ }
92
+ );
93
+
94
+ return this._compressionPromise;
95
+ }
96
+
97
+ /**
98
+ * 带超时的压缩执行
99
+ * @private
100
+ */
101
+ async _executeWithTimeout(sessionId, messages, messageStore) {
102
+ try {
103
+ return await Promise.race([
104
+ this._doCompress(sessionId, messages, messageStore),
105
+ this._createTimeoutPromise(),
106
+ ]);
107
+ } catch (err) {
108
+ logger.warn('Compression failed:', err.message);
109
+ this._simpleCompress(sessionId, messages, messageStore);
110
+ }
111
+ }
112
+
113
+ /**
114
+ * 创建超时 Promise
115
+ * @returns {Promise}
116
+ * @private
117
+ */
118
+ _createTimeoutPromise() {
119
+ return new Promise((_, reject) => {
120
+ setTimeout(() => {
121
+ reject(new Error(`Compression timeout (${COMPRESSION_TIMEOUT}ms)`));
122
+ }, COMPRESSION_TIMEOUT);
123
+ });
124
+ }
125
+
126
+ /**
127
+ * 执行智能压缩(使用 AI 生成摘要)
128
+ * @private
129
+ */
130
+ async _doCompress(sessionId, messages, messageStore) {
131
+ const systemMessages = messages.filter((m) => m.role === 'system');
132
+ const otherMessages = messages.filter((m) => m.role !== 'system');
133
+
134
+ // 保留最近的 N 条非系统消息
135
+ const recentMessages = otherMessages.slice(-this._keepRecentMessages);
136
+ const messagesToSummarize = otherMessages.slice(0, -this._keepRecentMessages);
137
+
138
+ const compressedCount = messagesToSummarize.length;
139
+ let summaryContent = '';
140
+
141
+ // 使用 AI 生成摘要
142
+ if (this._enableSmartCompress && this.agent?._chatHandler?._aiClient) {
143
+ try {
144
+ const summaryText = await this._summarizeMessages(messagesToSummarize);
145
+ summaryContent = `[早期对话摘要]: ${summaryText}`;
146
+ logger.info(`AI summary generated (${summaryText.length} chars)`);
147
+ } catch (err) {
148
+ logger.warn('AI summary failed, using simple compression:', err.message);
149
+ summaryContent = `[上下文已压缩: 省略了 ${compressedCount} 条早期消息。保留了最近 ${this._keepRecentMessages} 条对话记录。]`;
150
+ }
151
+ } else {
152
+ summaryContent = `[上下文已压缩: 省略了 ${compressedCount} 条早期消息。保留了最近 ${this._keepRecentMessages} 条对话记录。]`;
153
+ }
154
+
155
+ const summary = {
156
+ role: 'assistant',
157
+ content: summaryContent,
158
+ };
159
+
160
+ // 构建保留的消息(确保 tool call 和 tool result 配对)
161
+ const filteredRecentMessages = this._filterPairedMessages(recentMessages);
162
+
163
+ // 替换消息数组
164
+ messages.length = 0;
165
+ messages.push(...systemMessages, summary, ...filteredRecentMessages);
166
+
167
+ // 更新压缩状态
168
+ this._compressionCount++;
169
+ if (messageStore.compressionState) {
170
+ messageStore.compressionState.count++;
171
+ messageStore.compressionState.lastCompressedAt = Date.now();
172
+ messageStore.compressionState.lastTokenCount = this._countMessagesTokens(messages);
173
+ }
174
+
175
+ logger.info(
176
+ `Context compressed (${this._compressionCount} times). Messages: ${messages.length}`
177
+ );
178
+ }
179
+
180
+ /**
181
+ * 简单压缩(截断保留最近消息)
182
+ * @private
183
+ */
184
+ _simpleCompress(sessionId, messages, messageStore) {
185
+ const systemMessages = messages.filter((m) => m.role === 'system');
186
+ const otherMessages = messages.filter((m) => m.role !== 'system');
187
+ const recentMessages = otherMessages.slice(-this._keepRecentMessages);
188
+ const compressedCount = otherMessages.length - this._keepRecentMessages;
189
+
190
+ const summaryContent = `[上下文已压缩: 省略了 ${compressedCount} 条早期消息。保留了最近 ${this._keepRecentMessages} 条对话记录。]`;
191
+
192
+ const summary = {
193
+ role: 'assistant',
194
+ content: summaryContent,
195
+ };
196
+
197
+ // 确保 tool call 和 tool result 配对
198
+ const filteredRecentMessages = this._filterPairedMessages(recentMessages);
199
+
200
+ messages.length = 0;
201
+ messages.push(...systemMessages, summary, ...filteredRecentMessages);
202
+
203
+ this._compressionCount++;
204
+ if (messageStore.compressionState) {
205
+ messageStore.compressionState.count++;
206
+ messageStore.compressionState.lastCompressedAt = Date.now();
207
+ messageStore.compressionState.lastTokenCount = this._countMessagesTokens(messages);
208
+ }
209
+
210
+ logger.info(`Context simple compressed. Messages: ${messages.length}`);
211
+ }
212
+
213
+ /**
214
+ * 过滤消息,保留配对的 tool call 和 tool result
215
+ * @private
216
+ */
217
+ _filterPairedMessages(messages) {
218
+ // 工具类型检查
219
+ const isToolCall = (item) =>
220
+ item && (item.type === 'tool-call' || item.type === 'tool-use') && item.toolCallId;
221
+ const isToolResult = (item) =>
222
+ item && (item.type === 'tool-result' || item.type === 'tool_result') && item.toolCallId;
223
+
224
+ // 第一遍:找出所有 tool call 和它们的 results
225
+ const assistantToolCalls = new Map(); // toolCallId -> assistant message index
226
+ const toolResults = new Map(); // toolCallId -> tool result indices
227
+
228
+ for (let i = 0; i < messages.length; i++) {
229
+ const msg = messages[i];
230
+ if (msg.role === 'assistant' && msg.content) {
231
+ const content = Array.isArray(msg.content) ? msg.content : [msg.content];
232
+ for (const item of content) {
233
+ if (isToolCall(item)) {
234
+ assistantToolCalls.set(item.toolCallId, i);
235
+ }
236
+ }
237
+ }
238
+ if (msg.role === 'tool' && Array.isArray(msg.content)) {
239
+ for (const item of msg.content) {
240
+ if (isToolResult(item)) {
241
+ if (!toolResults.has(item.toolCallId)) {
242
+ toolResults.set(item.toolCallId, []);
243
+ }
244
+ toolResults.get(item.toolCallId).push(i);
245
+ }
246
+ }
247
+ }
248
+ }
249
+
250
+ // 第二遍:找出哪些 assistant 消息需要保留
251
+ const assistantIndicesToKeep = new Set();
252
+ for (let i = 0; i < messages.length; i++) {
253
+ const msg = messages[i];
254
+ if (msg.role === 'assistant') {
255
+ let hasToolCall = false;
256
+ if (msg.content) {
257
+ const content = Array.isArray(msg.content) ? msg.content : [msg.content];
258
+ for (const item of content) {
259
+ if (isToolCall(item)) {
260
+ hasToolCall = true;
261
+ break;
262
+ }
263
+ }
264
+ }
265
+ if (hasToolCall) {
266
+ assistantIndicesToKeep.add(i);
267
+ } else if (i >= messages.length - 3) {
268
+ // 保留最近几条 assistant 消息
269
+ assistantIndicesToKeep.add(i);
270
+ }
271
+ }
272
+ }
273
+
274
+ // 如果有孤儿的 tool call(没有 result),保留该 assistant 消息
275
+ for (const [toolCallId, assistantIdx] of assistantToolCalls) {
276
+ if (!toolResults.has(toolCallId)) {
277
+ assistantIndicesToKeep.add(assistantIdx);
278
+ }
279
+ }
280
+
281
+ // 第三遍:确定哪些 tool result 要保留
282
+ const indicesToKeep = new Set();
283
+ for (let i = 0; i < messages.length; i++) {
284
+ const msg = messages[i];
285
+ if (msg.role === 'tool') {
286
+ let hasPairedAssistant = false;
287
+ const content = Array.isArray(msg.content) ? msg.content : [msg.content];
288
+ for (const item of content) {
289
+ if (isToolResult(item)) {
290
+ const assistantIdx = assistantToolCalls.get(item.toolCallId);
291
+ if (assistantIdx !== undefined && assistantIndicesToKeep.has(assistantIdx)) {
292
+ hasPairedAssistant = true;
293
+ break;
294
+ }
295
+ }
296
+ }
297
+ if (hasPairedAssistant) {
298
+ indicesToKeep.add(i);
299
+ }
300
+ } else if (msg.role === 'assistant') {
301
+ if (assistantIndicesToKeep.has(i)) {
302
+ indicesToKeep.add(i);
303
+ }
304
+ } else {
305
+ // user, system 等角色默认保留
306
+ indicesToKeep.add(i);
307
+ }
308
+ }
309
+
310
+ // 按索引排序并返回
311
+ const sortedIndices = Array.from(indicesToKeep).sort((a, b) => a - b);
312
+ return sortedIndices.map((i) => messages[i]);
313
+ }
314
+
315
+ /**
316
+ * 验证消息配对(防止 orphaned tool result)
317
+ * @param {Array} messages - 消息数组
318
+ * @returns {Array} 验证后的消息
319
+ */
320
+ validateMessagesPairing(messages) {
321
+ // 收集所有 assistant 的 tool-call IDs(兼容多种类型名)
322
+ const assistantToolCallIds = new Set();
323
+ for (const msg of messages) {
324
+ if (msg.role === 'assistant' && Array.isArray(msg.content)) {
325
+ for (const item of msg.content) {
326
+ // 兼容 tool-call 和 tool-use 两种类型
327
+ if ((item.type === 'tool-call' || item.type === 'tool-use') && item.toolCallId) {
328
+ assistantToolCallIds.add(item.toolCallId);
329
+ }
330
+ }
331
+ }
332
+ }
333
+
334
+ // 检查并删除没有配对的 tool result
335
+ let removedCount = 0;
336
+ for (const msg of messages) {
337
+ if (msg.role === 'tool' && Array.isArray(msg.content)) {
338
+ const originalLength = msg.content.length;
339
+ msg.content = msg.content.filter((item) => {
340
+ // 兼容 tool-result 和 tool_result 两种类型
341
+ if (
342
+ item &&
343
+ (item.type === 'tool-result' || item.type === 'tool_result') &&
344
+ item.toolCallId
345
+ ) {
346
+ if (!assistantToolCallIds.has(item.toolCallId)) {
347
+ removedCount++;
348
+ return false; // 删除没有配对的 tool result
349
+ }
350
+ }
351
+ return true;
352
+ });
353
+
354
+ // 如果所有 content 都被删除了,标记整个消息待删除
355
+ if (msg.content.length === 0 && originalLength > 0) {
356
+ msg._orphaned = true;
357
+ }
358
+ }
359
+ }
360
+
361
+ // 移除被标记为 orphaned 的 tool 消息
362
+ const originalLength = messages.length;
363
+ const filtered = messages.filter((msg) => !(msg.role === 'tool' && msg._orphaned));
364
+
365
+ if (removedCount > 0 || filtered.length !== originalLength) {
366
+ logger.debug(
367
+ `Removed ${removedCount} orphaned tool-results, ${originalLength - filtered.length} orphaned tool messages`
368
+ );
369
+ }
370
+
371
+ return filtered;
372
+ }
373
+
374
+ /**
375
+ * 使用 AI 生成消息摘要
376
+ * @param {Array} messages - 消息数组
377
+ * @returns {Promise<string>}
378
+ * @private
379
+ */
380
+ async _summarizeMessages(messages) {
381
+ if (!this.agent?._chatHandler?._aiClient) {
382
+ throw new Error('AI client not available');
383
+ }
384
+
385
+ const { generateText } = await import('ai');
386
+
387
+ const prompt = `请简要总结以下对话的主要内容,用 100 字以内描述:
388
+
389
+ ${messages.map((m) => `${m.role}: ${typeof m.content === 'string' ? m.content : JSON.stringify(m.content)}`).join('\n')}
390
+
391
+ 摘要:`;
392
+
393
+ try {
394
+ const result = await generateText({
395
+ model: this.agent._chatHandler._aiClient,
396
+ prompt: prompt,
397
+ maxTokens: 200,
398
+ });
399
+ return result.text || '';
400
+ } catch (err) {
401
+ logger.warn('Summarize failed:', err.message);
402
+ throw err;
403
+ }
404
+ }
405
+
406
+ /**
407
+ * 计算消息数组的 token 数
408
+ * @param {Array} messages - 消息数组
409
+ * @returns {number}
410
+ */
411
+ _countMessagesTokens(messages) {
412
+ if (!messages || !Array.isArray(messages)) return 0;
413
+
414
+ let total = 0;
415
+ for (const msg of messages) {
416
+ total += this._countMessageTokens(msg);
417
+ }
418
+ return total;
419
+ }
420
+
421
+ /**
422
+ * 计算单条消息的 token 数
423
+ * @private
424
+ */
425
+ _countMessageTokens(msg) {
426
+ if (!msg) return 0;
427
+
428
+ let total = 4; // 角色开销
429
+
430
+ if (typeof msg.content === 'string') {
431
+ total += Math.ceil(Buffer.byteLength(msg.content, 'utf8') / 4);
432
+ } else if (Array.isArray(msg.content)) {
433
+ for (const block of msg.content) {
434
+ if (block.type === 'text') {
435
+ total += Math.ceil(Buffer.byteLength(block.text || '', 'utf8') / 4);
436
+ } else if (block.type === 'tool-call' || block.type === 'tool-use') {
437
+ if (block.input) {
438
+ total += Math.ceil(Buffer.byteLength(JSON.stringify(block.input), 'utf8') / 4);
439
+ }
440
+ } else if (block.type === 'tool-result' || block.type === 'tool_result') {
441
+ const content =
442
+ typeof block.content === 'string' ? block.content : JSON.stringify(block.content);
443
+ total += Math.ceil(Buffer.byteLength(content, 'utf8') / 4);
444
+ }
445
+ }
446
+ }
447
+
448
+ if (msg.tool_calls) {
449
+ total += 15;
450
+ for (const tc of msg.tool_calls) {
451
+ if (tc.function) {
452
+ total += Math.ceil(Buffer.byteLength(tc.function.name || '', 'utf8') / 4);
453
+ total += Math.ceil(Buffer.byteLength(tc.function.arguments || '{}', 'utf8') / 4);
454
+ }
455
+ }
456
+ }
457
+
458
+ return total;
459
+ }
460
+
461
+ /**
462
+ * 获取压缩统计
463
+ * @returns {Object}
464
+ */
465
+ getStats() {
466
+ return {
467
+ compressionCount: this._compressionCount,
468
+ inProgress: this._compressionInProgress,
469
+ };
470
+ }
471
+ }
472
+
473
+ module.exports = { ContextCompressor };
@@ -9,7 +9,6 @@
9
9
 
10
10
  const { RequestContext } = require('./request-context');
11
11
  const { SessionContext } = require('./session-context');
12
- const { AgentContext } = require('./agent-context');
13
12
 
14
13
  class ContextManager {
15
14
  /**
@@ -21,7 +20,6 @@ class ContextManager {
21
20
  // Context 存储
22
21
  this._requestContexts = new Map(); // requestId → RequestContext
23
22
  this._sessionContexts = new Map(); // sessionId → SessionContext
24
- this._agentContexts = new Map(); // agentId → AgentContext
25
23
 
26
24
  // 引用计数(用于自动清理)
27
25
  this._refCounts = new Map();
@@ -161,45 +159,6 @@ class ContextManager {
161
159
  return this._sessionContexts.has(sessionId);
162
160
  }
163
161
 
164
- // ==================== Agent Context ====================
165
-
166
- /**
167
- * 获取或创建 Agent Context
168
- * @param {string} agentId - Agent ID
169
- * @param {Agent} agent - Agent 实例
170
- * @param {Object} options - 配置选项
171
- * @returns {AgentContext}
172
- */
173
- getOrCreateAgentContext(agentId, agent, options = {}) {
174
- let ctx = this._agentContexts.get(agentId);
175
- if (!ctx) {
176
- ctx = new AgentContext(agentId, agent, options);
177
- this._agentContexts.set(agentId, ctx);
178
- }
179
- return ctx;
180
- }
181
-
182
- /**
183
- * 获取 Agent Context
184
- * @param {string} agentId - Agent ID
185
- * @returns {AgentContext|null}
186
- */
187
- getAgentContext(agentId) {
188
- return this._agentContexts.get(agentId) || null;
189
- }
190
-
191
- /**
192
- * 删除 Agent Context
193
- * @param {string} agentId - Agent ID
194
- */
195
- removeAgentContext(agentId) {
196
- const ctx = this._agentContexts.get(agentId);
197
- if (ctx) {
198
- ctx.destroy();
199
- this._agentContexts.delete(agentId);
200
- }
201
- }
202
-
203
162
  // ==================== 批量操作 ====================
204
163
 
205
164
  /**
@@ -235,11 +194,6 @@ class ContextManager {
235
194
 
236
195
  // 清理 Session Contexts
237
196
  this.destroyAllSessionContexts();
238
-
239
- // 清理 Agent Contexts
240
- for (const [agentId] of this._agentContexts) {
241
- this.removeAgentContext(agentId);
242
- }
243
197
  }
244
198
 
245
199
  // ==================== 统计 ====================
@@ -252,7 +206,6 @@ class ContextManager {
252
206
  return {
253
207
  activeRequests: this._requestContexts.size,
254
208
  activeSessions: this._sessionContexts.size,
255
- activeAgents: this._agentContexts.size,
256
209
  refCounts: Object.fromEntries(this._refCounts),
257
210
  };
258
211
  }
@@ -273,7 +226,6 @@ class ContextManager {
273
226
  return {
274
227
  requestContexts: this._requestContexts.size,
275
228
  sessionContexts: this._sessionContexts.size,
276
- agentContexts: this._agentContexts.size,
277
229
  totalSessionMessages: sessionMessagesSize,
278
230
  totalSessionVariables: sessionVariablesSize,
279
231
  };