kode-sdk 2.7.0
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/LICENSE +201 -0
- package/README.md +74 -0
- package/dist/core/agent/breakpoint-manager.d.ts +16 -0
- package/dist/core/agent/breakpoint-manager.js +36 -0
- package/dist/core/agent/message-queue.d.ts +26 -0
- package/dist/core/agent/message-queue.js +47 -0
- package/dist/core/agent/permission-manager.d.ts +9 -0
- package/dist/core/agent/permission-manager.js +32 -0
- package/dist/core/agent/todo-manager.d.ts +26 -0
- package/dist/core/agent/todo-manager.js +91 -0
- package/dist/core/agent/tool-runner.d.ts +9 -0
- package/dist/core/agent/tool-runner.js +45 -0
- package/dist/core/agent.d.ts +271 -0
- package/dist/core/agent.js +2334 -0
- package/dist/core/checkpointer.d.ts +96 -0
- package/dist/core/checkpointer.js +57 -0
- package/dist/core/checkpointers/file.d.ts +20 -0
- package/dist/core/checkpointers/file.js +153 -0
- package/dist/core/checkpointers/index.d.ts +3 -0
- package/dist/core/checkpointers/index.js +9 -0
- package/dist/core/checkpointers/redis.d.ts +35 -0
- package/dist/core/checkpointers/redis.js +113 -0
- package/dist/core/compression/ai-strategy.d.ts +53 -0
- package/dist/core/compression/ai-strategy.js +298 -0
- package/dist/core/compression/index.d.ts +12 -0
- package/dist/core/compression/index.js +27 -0
- package/dist/core/compression/prompts.d.ts +35 -0
- package/dist/core/compression/prompts.js +114 -0
- package/dist/core/compression/simple-strategy.d.ts +44 -0
- package/dist/core/compression/simple-strategy.js +240 -0
- package/dist/core/compression/token-estimator.d.ts +42 -0
- package/dist/core/compression/token-estimator.js +121 -0
- package/dist/core/compression/types.d.ts +140 -0
- package/dist/core/compression/types.js +9 -0
- package/dist/core/config.d.ts +10 -0
- package/dist/core/config.js +2 -0
- package/dist/core/context-manager.d.ts +115 -0
- package/dist/core/context-manager.js +107 -0
- package/dist/core/errors.d.ts +6 -0
- package/dist/core/errors.js +17 -0
- package/dist/core/events.d.ts +49 -0
- package/dist/core/events.js +312 -0
- package/dist/core/file-pool.d.ts +43 -0
- package/dist/core/file-pool.js +120 -0
- package/dist/core/hooks.d.ts +23 -0
- package/dist/core/hooks.js +71 -0
- package/dist/core/permission-modes.d.ts +31 -0
- package/dist/core/permission-modes.js +61 -0
- package/dist/core/pool.d.ts +31 -0
- package/dist/core/pool.js +87 -0
- package/dist/core/room.d.ts +15 -0
- package/dist/core/room.js +57 -0
- package/dist/core/scheduler.d.ts +33 -0
- package/dist/core/scheduler.js +58 -0
- package/dist/core/template.d.ts +69 -0
- package/dist/core/template.js +35 -0
- package/dist/core/time-bridge.d.ts +18 -0
- package/dist/core/time-bridge.js +100 -0
- package/dist/core/todo.d.ts +34 -0
- package/dist/core/todo.js +89 -0
- package/dist/core/types.d.ts +380 -0
- package/dist/core/types.js +3 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +147 -0
- package/dist/infra/provider.d.ts +144 -0
- package/dist/infra/provider.js +294 -0
- package/dist/infra/sandbox-factory.d.ts +10 -0
- package/dist/infra/sandbox-factory.js +21 -0
- package/dist/infra/sandbox.d.ts +87 -0
- package/dist/infra/sandbox.js +255 -0
- package/dist/infra/store.d.ts +154 -0
- package/dist/infra/store.js +584 -0
- package/dist/skills/index.d.ts +12 -0
- package/dist/skills/index.js +36 -0
- package/dist/skills/injector.d.ts +29 -0
- package/dist/skills/injector.js +96 -0
- package/dist/skills/loader.d.ts +59 -0
- package/dist/skills/loader.js +215 -0
- package/dist/skills/manager.d.ts +85 -0
- package/dist/skills/manager.js +221 -0
- package/dist/skills/parser.d.ts +40 -0
- package/dist/skills/parser.js +107 -0
- package/dist/skills/types.d.ts +107 -0
- package/dist/skills/types.js +7 -0
- package/dist/skills/validator.d.ts +30 -0
- package/dist/skills/validator.js +121 -0
- package/dist/store.d.ts +1 -0
- package/dist/store.js +5 -0
- package/dist/tools/bash_kill/index.d.ts +1 -0
- package/dist/tools/bash_kill/index.js +35 -0
- package/dist/tools/bash_kill/prompt.d.ts +2 -0
- package/dist/tools/bash_kill/prompt.js +14 -0
- package/dist/tools/bash_logs/index.d.ts +1 -0
- package/dist/tools/bash_logs/index.js +40 -0
- package/dist/tools/bash_logs/prompt.d.ts +2 -0
- package/dist/tools/bash_logs/prompt.js +14 -0
- package/dist/tools/bash_run/index.d.ts +16 -0
- package/dist/tools/bash_run/index.js +61 -0
- package/dist/tools/bash_run/prompt.d.ts +2 -0
- package/dist/tools/bash_run/prompt.js +18 -0
- package/dist/tools/builtin.d.ts +9 -0
- package/dist/tools/builtin.js +27 -0
- package/dist/tools/define.d.ts +101 -0
- package/dist/tools/define.js +214 -0
- package/dist/tools/fs_edit/index.d.ts +1 -0
- package/dist/tools/fs_edit/index.js +62 -0
- package/dist/tools/fs_edit/prompt.d.ts +2 -0
- package/dist/tools/fs_edit/prompt.js +15 -0
- package/dist/tools/fs_glob/index.d.ts +1 -0
- package/dist/tools/fs_glob/index.js +60 -0
- package/dist/tools/fs_glob/prompt.d.ts +2 -0
- package/dist/tools/fs_glob/prompt.js +18 -0
- package/dist/tools/fs_grep/index.d.ts +1 -0
- package/dist/tools/fs_grep/index.js +66 -0
- package/dist/tools/fs_grep/prompt.d.ts +2 -0
- package/dist/tools/fs_grep/prompt.js +16 -0
- package/dist/tools/fs_multi_edit/index.d.ts +1 -0
- package/dist/tools/fs_multi_edit/index.js +106 -0
- package/dist/tools/fs_multi_edit/prompt.d.ts +2 -0
- package/dist/tools/fs_multi_edit/prompt.js +16 -0
- package/dist/tools/fs_read/index.d.ts +1 -0
- package/dist/tools/fs_read/index.js +40 -0
- package/dist/tools/fs_read/prompt.d.ts +2 -0
- package/dist/tools/fs_read/prompt.js +16 -0
- package/dist/tools/fs_rm/index.d.ts +1 -0
- package/dist/tools/fs_rm/index.js +41 -0
- package/dist/tools/fs_rm/prompt.d.ts +2 -0
- package/dist/tools/fs_rm/prompt.js +14 -0
- package/dist/tools/fs_write/index.d.ts +1 -0
- package/dist/tools/fs_write/index.js +40 -0
- package/dist/tools/fs_write/prompt.d.ts +2 -0
- package/dist/tools/fs_write/prompt.js +15 -0
- package/dist/tools/index.d.ts +9 -0
- package/dist/tools/index.js +56 -0
- package/dist/tools/mcp.d.ts +73 -0
- package/dist/tools/mcp.js +198 -0
- package/dist/tools/registry.d.ts +29 -0
- package/dist/tools/registry.js +26 -0
- package/dist/tools/skill_activate/index.d.ts +5 -0
- package/dist/tools/skill_activate/index.js +63 -0
- package/dist/tools/skill_list/index.d.ts +5 -0
- package/dist/tools/skill_list/index.js +48 -0
- package/dist/tools/skill_resource/index.d.ts +5 -0
- package/dist/tools/skill_resource/index.js +82 -0
- package/dist/tools/task_run/index.d.ts +7 -0
- package/dist/tools/task_run/index.js +60 -0
- package/dist/tools/task_run/prompt.d.ts +5 -0
- package/dist/tools/task_run/prompt.js +29 -0
- package/dist/tools/todo_read/index.d.ts +1 -0
- package/dist/tools/todo_read/index.js +29 -0
- package/dist/tools/todo_read/prompt.d.ts +2 -0
- package/dist/tools/todo_read/prompt.js +18 -0
- package/dist/tools/todo_write/index.d.ts +1 -0
- package/dist/tools/todo_write/index.js +42 -0
- package/dist/tools/todo_write/prompt.d.ts +2 -0
- package/dist/tools/todo_write/prompt.js +23 -0
- package/dist/tools/tool.d.ts +43 -0
- package/dist/tools/tool.js +104 -0
- package/dist/tools/toolkit.d.ts +69 -0
- package/dist/tools/toolkit.js +98 -0
- package/dist/tools/type-inference.d.ts +127 -0
- package/dist/tools/type-inference.js +207 -0
- package/dist/utils/agent-id.d.ts +1 -0
- package/dist/utils/agent-id.js +28 -0
- package/dist/utils/session-id.d.ts +21 -0
- package/dist/utils/session-id.js +64 -0
- package/dist/utils/unicode.d.ts +17 -0
- package/dist/utils/unicode.js +62 -0
- package/package.json +117 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* AI 智能压缩策略
|
|
4
|
+
*
|
|
5
|
+
* 使用 LLM 生成结构化摘要:
|
|
6
|
+
* - 按 token 数保留消息(而非消息数)
|
|
7
|
+
* - 使用 AI 生成高质量摘要
|
|
8
|
+
* - 保留关键上下文信息
|
|
9
|
+
* - 内置降级机制
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.AICompressionStrategy = void 0;
|
|
13
|
+
const token_estimator_1 = require("./token-estimator");
|
|
14
|
+
const prompts_1 = require("./prompts");
|
|
15
|
+
class AICompressionStrategy {
|
|
16
|
+
constructor(opts) {
|
|
17
|
+
this.store = opts.store;
|
|
18
|
+
this.agentId = opts.agentId;
|
|
19
|
+
this.maxTokens = opts.maxTokens;
|
|
20
|
+
this.compressToTokens = opts.compressToTokens;
|
|
21
|
+
this.preserveRecentTurns = opts.preserveRecentTurns ?? 3;
|
|
22
|
+
this.summaryPrompt = opts.summaryPrompt;
|
|
23
|
+
this.eventEmitter = opts.eventEmitter;
|
|
24
|
+
// 创建摘要模型
|
|
25
|
+
this.summaryModel = this.createSummaryModel(opts);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 创建摘要模型
|
|
29
|
+
*
|
|
30
|
+
* 优先使用单独配置的摘要模型,否则复用主模型配置
|
|
31
|
+
*/
|
|
32
|
+
createSummaryModel(opts) {
|
|
33
|
+
const mainConfig = opts.modelConfig;
|
|
34
|
+
if (opts.summaryModelConfig?.model) {
|
|
35
|
+
// 使用单独配置的摘要模型
|
|
36
|
+
const summaryConfig = {
|
|
37
|
+
provider: mainConfig.provider,
|
|
38
|
+
model: opts.summaryModelConfig.model,
|
|
39
|
+
apiKey: opts.summaryModelConfig.apiKey ?? mainConfig.apiKey,
|
|
40
|
+
baseUrl: opts.summaryModelConfig.baseUrl ?? mainConfig.baseUrl,
|
|
41
|
+
};
|
|
42
|
+
// 使用模型工厂创建
|
|
43
|
+
return opts.modelFactory(summaryConfig);
|
|
44
|
+
}
|
|
45
|
+
// 复用主模型配置
|
|
46
|
+
return opts.modelFactory(mainConfig);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 分析上下文使用情况
|
|
50
|
+
*/
|
|
51
|
+
analyze(messages) {
|
|
52
|
+
const totalTokens = (0, token_estimator_1.estimateTotalTokens)(messages);
|
|
53
|
+
return {
|
|
54
|
+
totalTokens,
|
|
55
|
+
messageCount: messages.length,
|
|
56
|
+
shouldCompress: totalTokens > this.maxTokens,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 执行压缩
|
|
61
|
+
*/
|
|
62
|
+
async compress(messages, events, filePool, sandbox) {
|
|
63
|
+
const usage = this.analyze(messages);
|
|
64
|
+
if (!usage.shouldCompress)
|
|
65
|
+
return undefined;
|
|
66
|
+
// 发送开始事件
|
|
67
|
+
this.eventEmitter?.emit({
|
|
68
|
+
phase: 'start',
|
|
69
|
+
algorithm: 'ai',
|
|
70
|
+
});
|
|
71
|
+
const timestamp = Date.now();
|
|
72
|
+
const windowId = `window-${timestamp}`;
|
|
73
|
+
const compressionId = `comp-${timestamp}`;
|
|
74
|
+
// 1. 保存历史窗口
|
|
75
|
+
const window = {
|
|
76
|
+
id: windowId,
|
|
77
|
+
messages,
|
|
78
|
+
events,
|
|
79
|
+
stats: {
|
|
80
|
+
messageCount: messages.length,
|
|
81
|
+
tokenCount: usage.totalTokens,
|
|
82
|
+
eventCount: events.length,
|
|
83
|
+
},
|
|
84
|
+
timestamp,
|
|
85
|
+
};
|
|
86
|
+
await this.store.saveHistoryWindow(this.agentId, window);
|
|
87
|
+
// 2. 按 token 数选择要保留的消息
|
|
88
|
+
const minMessages = Math.max(2, this.preserveRecentTurns * 2); // 每轮对话约 2 条消息
|
|
89
|
+
const { retained: retainedMessages, removed: removedMessages } = (0, token_estimator_1.selectMessagesByTokens)(messages, this.compressToTokens, minMessages);
|
|
90
|
+
// 处理孤立的 tool_result
|
|
91
|
+
const toolSanitized = this.sanitizeOrphanToolResults(retainedMessages);
|
|
92
|
+
const retainedSanitized = toolSanitized.messages;
|
|
93
|
+
// 3. 生成摘要
|
|
94
|
+
let summaryText;
|
|
95
|
+
let aiGenerated = true;
|
|
96
|
+
try {
|
|
97
|
+
// 发送正在生成摘要事件
|
|
98
|
+
this.eventEmitter?.emit({
|
|
99
|
+
phase: 'summarizing',
|
|
100
|
+
algorithm: 'ai',
|
|
101
|
+
});
|
|
102
|
+
summaryText = await this.generateAISummary(removedMessages);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
// AI 调用失败,降级到简单摘要
|
|
106
|
+
console.warn('AI summary generation failed, falling back to simple summary:', error);
|
|
107
|
+
this.eventEmitter?.emit({
|
|
108
|
+
phase: 'fallback',
|
|
109
|
+
algorithm: 'ai',
|
|
110
|
+
error: error instanceof Error ? error.message : String(error),
|
|
111
|
+
});
|
|
112
|
+
summaryText = this.generateSimpleSummary(removedMessages);
|
|
113
|
+
aiGenerated = false;
|
|
114
|
+
}
|
|
115
|
+
const summary = {
|
|
116
|
+
role: 'system',
|
|
117
|
+
content: [
|
|
118
|
+
{
|
|
119
|
+
type: 'text',
|
|
120
|
+
text: `<context-summary timestamp="${new Date().toISOString()}" window="${windowId}" algorithm="ai" ai-generated="${aiGenerated}">\n${summaryText}\n</context-summary>`,
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
};
|
|
124
|
+
// 4. 保存文件快照
|
|
125
|
+
const recoveredPaths = [];
|
|
126
|
+
if (filePool && sandbox) {
|
|
127
|
+
const accessed = filePool.getAccessedFiles().slice(0, 5);
|
|
128
|
+
for (const { path, mtime } of accessed) {
|
|
129
|
+
recoveredPaths.push(path);
|
|
130
|
+
try {
|
|
131
|
+
const content = await sandbox.fs.read(path);
|
|
132
|
+
const file = {
|
|
133
|
+
path,
|
|
134
|
+
content,
|
|
135
|
+
mtime,
|
|
136
|
+
timestamp,
|
|
137
|
+
};
|
|
138
|
+
await this.store.saveRecoveredFile(this.agentId, file);
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
const file = {
|
|
142
|
+
path,
|
|
143
|
+
content: `// Failed to read file: ${err instanceof Error ? err.message : String(err)}`,
|
|
144
|
+
mtime,
|
|
145
|
+
timestamp,
|
|
146
|
+
};
|
|
147
|
+
await this.store.saveRecoveredFile(this.agentId, file);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// 5. 保存压缩记录
|
|
152
|
+
const ratio = retainedMessages.length / messages.length;
|
|
153
|
+
const tokensRetained = (0, token_estimator_1.estimateTotalTokens)(retainedSanitized);
|
|
154
|
+
const tokensRemoved = usage.totalTokens - tokensRetained;
|
|
155
|
+
const record = {
|
|
156
|
+
id: compressionId,
|
|
157
|
+
windowId,
|
|
158
|
+
config: {
|
|
159
|
+
model: this.summaryModel.model,
|
|
160
|
+
prompt: this.summaryPrompt || 'AI structured summary',
|
|
161
|
+
threshold: this.maxTokens,
|
|
162
|
+
},
|
|
163
|
+
summary: summaryText.slice(0, 500),
|
|
164
|
+
ratio,
|
|
165
|
+
recoveredFiles: recoveredPaths,
|
|
166
|
+
timestamp,
|
|
167
|
+
};
|
|
168
|
+
await this.store.saveCompressionRecord(this.agentId, record);
|
|
169
|
+
// 发送完成事件
|
|
170
|
+
this.eventEmitter?.emit({
|
|
171
|
+
phase: 'end',
|
|
172
|
+
algorithm: 'ai',
|
|
173
|
+
ratio,
|
|
174
|
+
tokensRemoved,
|
|
175
|
+
tokensRetained,
|
|
176
|
+
});
|
|
177
|
+
return {
|
|
178
|
+
summary,
|
|
179
|
+
removedMessages,
|
|
180
|
+
retainedMessages: retainedSanitized,
|
|
181
|
+
windowId,
|
|
182
|
+
compressionId,
|
|
183
|
+
ratio,
|
|
184
|
+
algorithm: 'ai',
|
|
185
|
+
aiGenerated,
|
|
186
|
+
tokensRemoved,
|
|
187
|
+
tokensRetained,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* 使用 AI 生成摘要
|
|
192
|
+
*/
|
|
193
|
+
async generateAISummary(messages) {
|
|
194
|
+
// 格式化消息为文本
|
|
195
|
+
const messagesText = (0, prompts_1.formatMessagesForSummary)(messages);
|
|
196
|
+
// 构建提示词
|
|
197
|
+
const prompt = (0, prompts_1.buildSummaryPrompt)(messagesText, this.summaryPrompt);
|
|
198
|
+
// 调用模型生成摘要
|
|
199
|
+
const response = await this.summaryModel.complete([
|
|
200
|
+
{
|
|
201
|
+
role: 'user',
|
|
202
|
+
content: [{ type: 'text', text: prompt }],
|
|
203
|
+
},
|
|
204
|
+
], {
|
|
205
|
+
maxTokens: 2000,
|
|
206
|
+
temperature: 0.3,
|
|
207
|
+
});
|
|
208
|
+
// 提取响应文本
|
|
209
|
+
const responseText = response.content
|
|
210
|
+
.filter((block) => block.type === 'text')
|
|
211
|
+
.map((block) => block.text)
|
|
212
|
+
.join('\n');
|
|
213
|
+
// 提取摘要内容
|
|
214
|
+
return (0, prompts_1.extractSummaryFromResponse)(responseText);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* 生成简单摘要(降级方案)
|
|
218
|
+
*/
|
|
219
|
+
generateSimpleSummary(messages) {
|
|
220
|
+
return messages
|
|
221
|
+
.map((msg, idx) => {
|
|
222
|
+
const header = `${idx + 1}. [${msg.role}]`;
|
|
223
|
+
const content = msg.content
|
|
224
|
+
.map((block) => {
|
|
225
|
+
if (block.type === 'text')
|
|
226
|
+
return block.text.slice(0, 200);
|
|
227
|
+
if (block.type === 'tool_use')
|
|
228
|
+
return `🔧 ${block.name}(...)`;
|
|
229
|
+
if (block.type === 'tool_result') {
|
|
230
|
+
const preview = JSON.stringify(block.content).slice(0, 100);
|
|
231
|
+
return `✅ result: ${preview}`;
|
|
232
|
+
}
|
|
233
|
+
return '';
|
|
234
|
+
})
|
|
235
|
+
.join('\n');
|
|
236
|
+
return `${header}\n${content}`;
|
|
237
|
+
})
|
|
238
|
+
.join('\n\n');
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* 预览文本(截断长文本)
|
|
242
|
+
*/
|
|
243
|
+
preview(value, limit = 1000) {
|
|
244
|
+
try {
|
|
245
|
+
const text = typeof value === 'string' ? value : JSON.stringify(value);
|
|
246
|
+
return text.length > limit ? `${text.slice(0, limit)}…` : text;
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
const text = String(value ?? '');
|
|
250
|
+
return text.length > limit ? `${text.slice(0, limit)}…` : text;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* 处理孤立的 tool_result
|
|
255
|
+
*/
|
|
256
|
+
sanitizeOrphanToolResults(messages) {
|
|
257
|
+
const toolUseIds = new Set();
|
|
258
|
+
for (const msg of messages || []) {
|
|
259
|
+
for (const block of msg?.content || []) {
|
|
260
|
+
if (block && block.type === 'tool_use') {
|
|
261
|
+
const id = String(block.id ?? '').trim();
|
|
262
|
+
if (id)
|
|
263
|
+
toolUseIds.add(id);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
let converted = 0;
|
|
268
|
+
const out = [];
|
|
269
|
+
for (const msg of messages || []) {
|
|
270
|
+
if (!msg || !Array.isArray(msg.content)) {
|
|
271
|
+
out.push(msg);
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
let changed = false;
|
|
275
|
+
const nextBlocks = [];
|
|
276
|
+
for (const block of msg.content) {
|
|
277
|
+
if (block && block.type === 'tool_result') {
|
|
278
|
+
const toolUseId = String(block.tool_use_id ?? '').trim();
|
|
279
|
+
if (!toolUseId || !toolUseIds.has(toolUseId)) {
|
|
280
|
+
changed = true;
|
|
281
|
+
converted += 1;
|
|
282
|
+
const isErr = Boolean(block.is_error);
|
|
283
|
+
const payload = this.preview(block.content, 1400);
|
|
284
|
+
nextBlocks.push({
|
|
285
|
+
type: 'text',
|
|
286
|
+
text: `[tool_result orphaned by compression] tool_use_id=${toolUseId || 'unknown'}${isErr ? ' (error)' : ''}\n${payload}`,
|
|
287
|
+
});
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
nextBlocks.push(block);
|
|
292
|
+
}
|
|
293
|
+
out.push(changed ? { ...msg, content: nextBlocks } : msg);
|
|
294
|
+
}
|
|
295
|
+
return { messages: out, converted };
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
exports.AICompressionStrategy = AICompressionStrategy;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 上下文压缩模块
|
|
3
|
+
*
|
|
4
|
+
* 提供多种压缩策略:
|
|
5
|
+
* - SimpleCompressionStrategy: 简单截断(默认)
|
|
6
|
+
* - AICompressionStrategy: AI 智能压缩
|
|
7
|
+
*/
|
|
8
|
+
export { ContextUsage, CompressionResult, FilePoolState, CompressionEventEmitter, CompressionEventPayload, ICompressionStrategy, ModelFactory, CompressionStrategyBaseOptions, SimpleCompressionStrategyOptions, AICompressionStrategyOptions, } from './types';
|
|
9
|
+
export { SimpleCompressionStrategy } from './simple-strategy';
|
|
10
|
+
export { AICompressionStrategy } from './ai-strategy';
|
|
11
|
+
export { estimateTextTokens, estimateBlockTokens, estimateMessageTokens, estimateTotalTokens, selectMessagesByTokens, } from './token-estimator';
|
|
12
|
+
export { DEFAULT_SUMMARY_PROMPT, buildSummaryPrompt, formatMessagesForSummary, extractSummaryFromResponse, } from './prompts';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 上下文压缩模块
|
|
4
|
+
*
|
|
5
|
+
* 提供多种压缩策略:
|
|
6
|
+
* - SimpleCompressionStrategy: 简单截断(默认)
|
|
7
|
+
* - AICompressionStrategy: AI 智能压缩
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.extractSummaryFromResponse = exports.formatMessagesForSummary = exports.buildSummaryPrompt = exports.DEFAULT_SUMMARY_PROMPT = exports.selectMessagesByTokens = exports.estimateTotalTokens = exports.estimateMessageTokens = exports.estimateBlockTokens = exports.estimateTextTokens = exports.AICompressionStrategy = exports.SimpleCompressionStrategy = void 0;
|
|
11
|
+
// 策略导出
|
|
12
|
+
var simple_strategy_1 = require("./simple-strategy");
|
|
13
|
+
Object.defineProperty(exports, "SimpleCompressionStrategy", { enumerable: true, get: function () { return simple_strategy_1.SimpleCompressionStrategy; } });
|
|
14
|
+
var ai_strategy_1 = require("./ai-strategy");
|
|
15
|
+
Object.defineProperty(exports, "AICompressionStrategy", { enumerable: true, get: function () { return ai_strategy_1.AICompressionStrategy; } });
|
|
16
|
+
// 工具函数导出
|
|
17
|
+
var token_estimator_1 = require("./token-estimator");
|
|
18
|
+
Object.defineProperty(exports, "estimateTextTokens", { enumerable: true, get: function () { return token_estimator_1.estimateTextTokens; } });
|
|
19
|
+
Object.defineProperty(exports, "estimateBlockTokens", { enumerable: true, get: function () { return token_estimator_1.estimateBlockTokens; } });
|
|
20
|
+
Object.defineProperty(exports, "estimateMessageTokens", { enumerable: true, get: function () { return token_estimator_1.estimateMessageTokens; } });
|
|
21
|
+
Object.defineProperty(exports, "estimateTotalTokens", { enumerable: true, get: function () { return token_estimator_1.estimateTotalTokens; } });
|
|
22
|
+
Object.defineProperty(exports, "selectMessagesByTokens", { enumerable: true, get: function () { return token_estimator_1.selectMessagesByTokens; } });
|
|
23
|
+
var prompts_1 = require("./prompts");
|
|
24
|
+
Object.defineProperty(exports, "DEFAULT_SUMMARY_PROMPT", { enumerable: true, get: function () { return prompts_1.DEFAULT_SUMMARY_PROMPT; } });
|
|
25
|
+
Object.defineProperty(exports, "buildSummaryPrompt", { enumerable: true, get: function () { return prompts_1.buildSummaryPrompt; } });
|
|
26
|
+
Object.defineProperty(exports, "formatMessagesForSummary", { enumerable: true, get: function () { return prompts_1.formatMessagesForSummary; } });
|
|
27
|
+
Object.defineProperty(exports, "extractSummaryFromResponse", { enumerable: true, get: function () { return prompts_1.extractSummaryFromResponse; } });
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 上下文压缩 - 摘要提示词模板
|
|
3
|
+
*
|
|
4
|
+
* 用于 AI 策略生成结构化摘要
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* 默认摘要提示词
|
|
8
|
+
*/
|
|
9
|
+
export declare const DEFAULT_SUMMARY_PROMPT = "\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u5BF9\u8BDD\u6458\u8981\u52A9\u624B\u3002\u8BF7\u4ED4\u7EC6\u9605\u8BFB\u4EE5\u4E0B\u5BF9\u8BDD\u5386\u53F2\uFF0C\u751F\u6210\u4E00\u4EFD\u7B80\u6D01\u4F46\u4FE1\u606F\u5B8C\u6574\u7684\u6458\u8981\u3002\n\n## \u6458\u8981\u8981\u6C42\n\n1. **\u4FDD\u7559\u5173\u952E\u4FE1\u606F**\uFF1A\u7528\u6237\u7684\u6838\u5FC3\u8BF7\u6C42\u3001\u91CD\u8981\u51B3\u7B56\u3001\u5173\u952E\u4EE3\u7801\u4FEE\u6539\n2. **\u7ED3\u6784\u5316\u8F93\u51FA**\uFF1A\u6309\u7167\u4E0B\u9762\u7684\u6A21\u677F\u683C\u5F0F\u8F93\u51FA\n3. **\u7B80\u6D01\u7CBE\u70BC**\uFF1A\u6BCF\u4E2A\u90E8\u5206 2-5 \u6761\u8981\u70B9\uFF0C\u907F\u514D\u5197\u4F59\n4. **\u4FDD\u7559\u4E0A\u4E0B\u6587**\uFF1A\u786E\u4FDD\u540E\u7EED\u5BF9\u8BDD\u53EF\u4EE5\u57FA\u4E8E\u6458\u8981\u7EE7\u7EED\n\n## \u8F93\u51FA\u6A21\u677F\n\n<summary>\n## \u4EFB\u52A1\u6982\u89C8\n- [\u7528\u6237\u7684\u6838\u5FC3\u8BF7\u6C42\u662F\u4EC0\u4E48]\n- [\u4EFB\u52A1\u7684\u6210\u529F\u6807\u51C6\u662F\u4EC0\u4E48]\n\n## \u5DF2\u5B8C\u6210\u5DE5\u4F5C\n- [\u5DF2\u4FEE\u6539\u7684\u6587\u4EF6\u548C\u5173\u952E\u4EE3\u7801\u53D8\u66F4]\n- [\u505A\u51FA\u7684\u91CD\u8981\u51B3\u7B56\u548C\u7406\u7531]\n\n## \u91CD\u8981\u53D1\u73B0\n- [\u9047\u5230\u7684\u95EE\u9898\u548C\u89E3\u51B3\u65B9\u6848]\n- [\u6280\u672F\u7EA6\u675F\u548C\u6CE8\u610F\u4E8B\u9879]\n\n## \u4E0B\u4E00\u6B65\n- [\u5F85\u5B8C\u6210\u7684\u4EFB\u52A1]\n- [\u963B\u585E\u95EE\u9898\u6216\u5F85\u786E\u8BA4\u4E8B\u9879]\n\n## \u9700\u8981\u4FDD\u7559\u7684\u4E0A\u4E0B\u6587\n- [\u7528\u6237\u504F\u597D\u548C\u7279\u6B8A\u8981\u6C42]\n- [\u91CD\u8981\u7684\u627F\u8BFA\u548C\u7EA6\u5B9A]\n</summary>\n\n## \u5BF9\u8BDD\u5386\u53F2\n\n";
|
|
10
|
+
/**
|
|
11
|
+
* 构建完整的摘要提示词
|
|
12
|
+
*
|
|
13
|
+
* @param messages - 需要摘要的消息内容(已格式化的文本)
|
|
14
|
+
* @param customPrompt - 自定义提示词(可选)
|
|
15
|
+
* @returns 完整的提示词
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildSummaryPrompt(messagesText: string, customPrompt?: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* 格式化消息为文本
|
|
20
|
+
*
|
|
21
|
+
* @param messages - 消息列表
|
|
22
|
+
* @param maxLength - 每条消息的最大长度(默认 500)
|
|
23
|
+
* @returns 格式化后的文本
|
|
24
|
+
*/
|
|
25
|
+
export declare function formatMessagesForSummary(messages: Array<{
|
|
26
|
+
role: string;
|
|
27
|
+
content: any[];
|
|
28
|
+
}>, maxLength?: number): string;
|
|
29
|
+
/**
|
|
30
|
+
* 从 AI 响应中提取摘要内容
|
|
31
|
+
*
|
|
32
|
+
* @param response - AI 响应文本
|
|
33
|
+
* @returns 提取的摘要内容
|
|
34
|
+
*/
|
|
35
|
+
export declare function extractSummaryFromResponse(response: string): string;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 上下文压缩 - 摘要提示词模板
|
|
4
|
+
*
|
|
5
|
+
* 用于 AI 策略生成结构化摘要
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.DEFAULT_SUMMARY_PROMPT = void 0;
|
|
9
|
+
exports.buildSummaryPrompt = buildSummaryPrompt;
|
|
10
|
+
exports.formatMessagesForSummary = formatMessagesForSummary;
|
|
11
|
+
exports.extractSummaryFromResponse = extractSummaryFromResponse;
|
|
12
|
+
const unicode_1 = require("../../utils/unicode");
|
|
13
|
+
/**
|
|
14
|
+
* 默认摘要提示词
|
|
15
|
+
*/
|
|
16
|
+
exports.DEFAULT_SUMMARY_PROMPT = `你是一个专业的对话摘要助手。请仔细阅读以下对话历史,生成一份简洁但信息完整的摘要。
|
|
17
|
+
|
|
18
|
+
## 摘要要求
|
|
19
|
+
|
|
20
|
+
1. **保留关键信息**:用户的核心请求、重要决策、关键代码修改
|
|
21
|
+
2. **结构化输出**:按照下面的模板格式输出
|
|
22
|
+
3. **简洁精炼**:每个部分 2-5 条要点,避免冗余
|
|
23
|
+
4. **保留上下文**:确保后续对话可以基于摘要继续
|
|
24
|
+
|
|
25
|
+
## 输出模板
|
|
26
|
+
|
|
27
|
+
<summary>
|
|
28
|
+
## 任务概览
|
|
29
|
+
- [用户的核心请求是什么]
|
|
30
|
+
- [任务的成功标准是什么]
|
|
31
|
+
|
|
32
|
+
## 已完成工作
|
|
33
|
+
- [已修改的文件和关键代码变更]
|
|
34
|
+
- [做出的重要决策和理由]
|
|
35
|
+
|
|
36
|
+
## 重要发现
|
|
37
|
+
- [遇到的问题和解决方案]
|
|
38
|
+
- [技术约束和注意事项]
|
|
39
|
+
|
|
40
|
+
## 下一步
|
|
41
|
+
- [待完成的任务]
|
|
42
|
+
- [阻塞问题或待确认事项]
|
|
43
|
+
|
|
44
|
+
## 需要保留的上下文
|
|
45
|
+
- [用户偏好和特殊要求]
|
|
46
|
+
- [重要的承诺和约定]
|
|
47
|
+
</summary>
|
|
48
|
+
|
|
49
|
+
## 对话历史
|
|
50
|
+
|
|
51
|
+
`;
|
|
52
|
+
/**
|
|
53
|
+
* 构建完整的摘要提示词
|
|
54
|
+
*
|
|
55
|
+
* @param messages - 需要摘要的消息内容(已格式化的文本)
|
|
56
|
+
* @param customPrompt - 自定义提示词(可选)
|
|
57
|
+
* @returns 完整的提示词
|
|
58
|
+
*/
|
|
59
|
+
function buildSummaryPrompt(messagesText, customPrompt) {
|
|
60
|
+
const basePrompt = customPrompt || exports.DEFAULT_SUMMARY_PROMPT;
|
|
61
|
+
return `${basePrompt}\n${messagesText}\n\n请根据以上对话历史生成摘要:`;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 格式化消息为文本
|
|
65
|
+
*
|
|
66
|
+
* @param messages - 消息列表
|
|
67
|
+
* @param maxLength - 每条消息的最大长度(默认 500)
|
|
68
|
+
* @returns 格式化后的文本
|
|
69
|
+
*/
|
|
70
|
+
function formatMessagesForSummary(messages, maxLength = 500) {
|
|
71
|
+
return messages
|
|
72
|
+
.map((msg, idx) => {
|
|
73
|
+
const roleLabel = msg.role === 'user' ? '用户' : msg.role === 'assistant' ? '助手' : msg.role;
|
|
74
|
+
const header = `### ${idx + 1}. [${roleLabel}]`;
|
|
75
|
+
const content = msg.content
|
|
76
|
+
.map((block) => {
|
|
77
|
+
if (block.type === 'text') {
|
|
78
|
+
const text = block.text || '';
|
|
79
|
+
return text.length > maxLength ? `${(0, unicode_1.safeTruncateUtf16)(text, maxLength)}...` : (0, unicode_1.safeTruncateUtf16)(text, maxLength);
|
|
80
|
+
}
|
|
81
|
+
if (block.type === 'tool_use') {
|
|
82
|
+
const input = (0, unicode_1.safeTruncateUtf16)(JSON.stringify(block.input || {}), 200);
|
|
83
|
+
return `🔧 调用工具: ${block.name}\n 参数: ${input}${input.length >= 200 ? '...' : ''}`;
|
|
84
|
+
}
|
|
85
|
+
if (block.type === 'tool_result') {
|
|
86
|
+
const result = typeof block.content === 'string'
|
|
87
|
+
? block.content
|
|
88
|
+
: JSON.stringify(block.content || '');
|
|
89
|
+
const preview = (0, unicode_1.safeTruncateUtf16)(result, 200);
|
|
90
|
+
return `✅ 工具结果: ${preview}${result.length > 200 ? '...' : ''}`;
|
|
91
|
+
}
|
|
92
|
+
return '';
|
|
93
|
+
})
|
|
94
|
+
.filter(Boolean)
|
|
95
|
+
.join('\n');
|
|
96
|
+
return `${header}\n${content}`;
|
|
97
|
+
})
|
|
98
|
+
.join('\n\n');
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 从 AI 响应中提取摘要内容
|
|
102
|
+
*
|
|
103
|
+
* @param response - AI 响应文本
|
|
104
|
+
* @returns 提取的摘要内容
|
|
105
|
+
*/
|
|
106
|
+
function extractSummaryFromResponse(response) {
|
|
107
|
+
// 尝试提取 <summary> 标签中的内容
|
|
108
|
+
const summaryMatch = response.match(/<summary>([\s\S]*?)<\/summary>/i);
|
|
109
|
+
if (summaryMatch) {
|
|
110
|
+
return summaryMatch[1].trim();
|
|
111
|
+
}
|
|
112
|
+
// 如果没有标签,返回整个响应(去除首尾空白)
|
|
113
|
+
return response.trim();
|
|
114
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 简单压缩策略
|
|
3
|
+
*
|
|
4
|
+
* 从现有 ContextManager 提取的压缩逻辑:
|
|
5
|
+
* - 按消息数保留 60% 或 compressToTokens/totalTokens 比例
|
|
6
|
+
* - 简单截断生成摘要
|
|
7
|
+
* - 处理孤立 tool_result
|
|
8
|
+
*/
|
|
9
|
+
import { Message, Timeline } from '../types';
|
|
10
|
+
import { Sandbox } from '../../infra/sandbox';
|
|
11
|
+
import { ICompressionStrategy, ContextUsage, CompressionResult, FilePoolState, SimpleCompressionStrategyOptions } from './types';
|
|
12
|
+
export declare class SimpleCompressionStrategy implements ICompressionStrategy {
|
|
13
|
+
private readonly store;
|
|
14
|
+
private readonly agentId;
|
|
15
|
+
private readonly maxTokens;
|
|
16
|
+
private readonly compressToTokens;
|
|
17
|
+
private readonly compressionModel;
|
|
18
|
+
private readonly compressionPrompt;
|
|
19
|
+
private readonly eventEmitter?;
|
|
20
|
+
constructor(opts: SimpleCompressionStrategyOptions);
|
|
21
|
+
/**
|
|
22
|
+
* 分析上下文使用情况(粗略的 token 估算)
|
|
23
|
+
*/
|
|
24
|
+
analyze(messages: Message[]): ContextUsage;
|
|
25
|
+
/**
|
|
26
|
+
* 执行压缩
|
|
27
|
+
*/
|
|
28
|
+
compress(messages: Message[], events: Timeline[], filePool?: FilePoolState, sandbox?: Sandbox): Promise<CompressionResult | undefined>;
|
|
29
|
+
/**
|
|
30
|
+
* 预览文本(截断长文本)
|
|
31
|
+
*/
|
|
32
|
+
private preview;
|
|
33
|
+
/**
|
|
34
|
+
* 处理孤立的 tool_result
|
|
35
|
+
*
|
|
36
|
+
* 简单的 slice 可能保留 tool_result 但丢弃配对的 tool_use,
|
|
37
|
+
* 导致 Anthropic API 报错 (2013)。将孤立的 tool_result 转换为文本。
|
|
38
|
+
*/
|
|
39
|
+
private sanitizeOrphanToolResults;
|
|
40
|
+
/**
|
|
41
|
+
* 生成简单摘要(截断文本)
|
|
42
|
+
*/
|
|
43
|
+
private generateSummary;
|
|
44
|
+
}
|