Skip to content

Commit e32c688

Browse files
committed
feat: 添加 useful-info 模块和预处理器
1 parent 62f5a81 commit e32c688

22 files changed

Lines changed: 1490 additions & 89 deletions

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ dist
33
bundle
44
out
55
.snow
6+
/.vscode/settings.json
7+
ROLE.md

build-ncc.mjs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,7 @@ console.log('Building with ncc...');
1515
await execAsync('ncc build dist/cli.js -o bundle --minify');
1616

1717
// Copy WASM file
18-
copyFileSync(
19-
'node_modules/sql.js/dist/sql-wasm.wasm',
20-
'bundle/sql-wasm.wasm',
21-
);
18+
copyFileSync('node_modules/sql.js/dist/sql-wasm.wasm', 'bundle/sql-wasm.wasm');
2219

2320
// Rename index.js to cli.cjs
2421
if (existsSync('bundle/index.js')) {

build-shim.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { fileURLToPath as _fileURLToPath } from 'url';
2-
import { dirname as _dirname } from 'path';
3-
import { createRequire as _createRequire } from 'module';
1+
import {fileURLToPath as _fileURLToPath} from 'url';
2+
import {dirname as _dirname} from 'path';
3+
import {createRequire as _createRequire} from 'module';
44

55
export const __filename = _fileURLToPath(import.meta.url);
66
export const __dirname = _dirname(__filename);

build.mjs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,7 @@ const __dirname = _fileURLToPath(new URL('.', import.meta.url));`,
5050
});
5151

5252
// Copy WASM files
53-
copyFileSync(
54-
'node_modules/sql.js/dist/sql-wasm.wasm',
55-
'bundle/sql-wasm.wasm',
56-
);
53+
copyFileSync('node_modules/sql.js/dist/sql-wasm.wasm', 'bundle/sql-wasm.wasm');
5754
copyFileSync(
5855
'node_modules/tiktoken/tiktoken_bg.wasm',
5956
'bundle/tiktoken_bg.wasm',

source/hooks/conversation/useCommandHandler.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import {exportMessagesToFile} from '../../utils/session/chatExporter.js';
1919
export async function executeContextCompression(): Promise<{
2020
uiMessages: Message[];
2121
usage: UsageInfo;
22+
preservedMessages?: Array<any>;
23+
summary?: string;
2224
} | null> {
2325
try {
2426
// 从会话文件读取真实的消息记录
@@ -142,6 +144,8 @@ export async function executeContextCompression(): Promise<{
142144
completion_tokens: compressionResult.usage.completion_tokens,
143145
total_tokens: compressionResult.usage.total_tokens,
144146
},
147+
preservedMessages: compressionResult.preservedMessages || [],
148+
summary: compressionResult.summary,
145149
};
146150
} catch (error) {
147151
console.error('Context compression failed:', error);

source/hooks/conversation/useConversation.ts

Lines changed: 182 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {getSystemPrompt} from '../../api/systemPrompt.js';
1010
import {
1111
collectAllMCPTools,
1212
getTodoService,
13+
getUsefulInfoService,
1314
} from '../../utils/execution/mcpToolsManager.js';
1415
import {
1516
executeToolCalls,
@@ -19,6 +20,7 @@ import {getOpenAiConfig} from '../../utils/config/apiConfig.js';
1920
import {sessionManager} from '../../utils/session/sessionManager.js';
2021
import {formatTodoContext} from '../../utils/core/todoPreprocessor.js';
2122
import {unifiedHooksExecutor} from '../../utils/execution/unifiedHooksExecutor.js';
23+
import {formatUsefulInfoContext} from '../../utils/core/usefulInfoPreprocessor.js';
2224
import type {Message} from '../../ui/components/MessageList.js';
2325
import {formatToolCallMessage} from '../../utils/ui/messageFormatter.js';
2426
import {resourceMonitor} from '../../utils/core/resourceMonitor.js';
@@ -136,6 +138,22 @@ export async function handleConversationWithTools(
136138
});
137139
}
138140

141+
// Add useful information context if available
142+
const usefulInfoService = getUsefulInfoService();
143+
const usefulInfoList = await usefulInfoService.getUsefulInfoList(
144+
currentSession.id,
145+
);
146+
147+
if (usefulInfoList && usefulInfoList.items.length > 0) {
148+
const usefulInfoContext = await formatUsefulInfoContext(
149+
usefulInfoList.items,
150+
);
151+
conversationMessages.push({
152+
role: 'user',
153+
content: usefulInfoContext,
154+
});
155+
}
156+
139157
// Add history messages from session (includes tool_calls and tool results)
140158
// Load from session to get complete conversation history with tool interactions
141159
// Filter out internal sub-agent messages (marked with subAgentInternal: true)
@@ -1194,8 +1212,89 @@ export async function handleConversationWithTools(
11941212
// 压缩后需要重新构建conversationMessages
11951213
conversationMessages = [];
11961214
const session = sessionManager.getCurrentSession();
1215+
1216+
// 1. 添加系统消息
1217+
conversationMessages.push({
1218+
role: 'system',
1219+
content: getSystemPrompt(),
1220+
});
1221+
1222+
// 2. 如果有TODOs,添加TODO上下文
1223+
if (existingTodoList && existingTodoList.todos.length > 0) {
1224+
const todoContext = formatTodoContext(existingTodoList.todos);
1225+
conversationMessages.push({
1226+
role: 'user',
1227+
content: todoContext,
1228+
});
1229+
}
1230+
1231+
// 3. 压缩后重新获取并添加有用信息上下文
1232+
const usefulInfoService = getUsefulInfoService();
1233+
const updatedUsefulInfoList =
1234+
await usefulInfoService.getUsefulInfoList(session?.id || '');
1235+
1236+
if (
1237+
updatedUsefulInfoList &&
1238+
updatedUsefulInfoList.items.length > 0
1239+
) {
1240+
const usefulInfoContext = await formatUsefulInfoContext(
1241+
updatedUsefulInfoList.items,
1242+
);
1243+
conversationMessages.push({
1244+
role: 'user',
1245+
content: usefulInfoContext,
1246+
});
1247+
}
1248+
1249+
// 4. 添加压缩摘要
1250+
conversationMessages.push({
1251+
role: 'user',
1252+
content: `[Context Summary from Previous Conversation]\n\n${compressionResult.summary}`,
1253+
});
1254+
1255+
// 5. 添加保留的消息(未完成的工具调用链)
1256+
if (
1257+
compressionResult.preservedMessages &&
1258+
compressionResult.preservedMessages.length > 0
1259+
) {
1260+
for (const msg of compressionResult.preservedMessages) {
1261+
conversationMessages.push(msg);
1262+
}
1263+
}
1264+
1265+
// 6. 添加会话中的其他消息(排除已保留的)
11971266
if (session && session.messages.length > 0) {
1198-
conversationMessages.push(...session.messages);
1267+
// 获取已保留的消息ID集合,避免重复
1268+
const preservedIds = new Set(
1269+
compressionResult.preservedMessages?.map(
1270+
msg =>
1271+
msg.tool_call_id ||
1272+
(msg.tool_calls && msg.tool_calls[0]?.id) ||
1273+
`${msg.role}-${msg.content.slice(0, 20)}`,
1274+
) || [],
1275+
);
1276+
1277+
for (const sessionMsg of session.messages) {
1278+
const msgId =
1279+
sessionMsg.tool_call_id ||
1280+
(sessionMsg.tool_calls && sessionMsg.tool_calls[0]?.id) ||
1281+
`${sessionMsg.role}-${sessionMsg.content.slice(0, 20)}`;
1282+
1283+
// 跳过已保留的消息和工具消息
1284+
if (!preservedIds.has(msgId) && sessionMsg.role !== 'tool') {
1285+
conversationMessages.push({
1286+
role: sessionMsg.role,
1287+
content: sessionMsg.content,
1288+
...(sessionMsg.tool_calls && {
1289+
tool_calls: sessionMsg.tool_calls,
1290+
}),
1291+
...(sessionMsg.images && {images: sessionMsg.images}),
1292+
...(sessionMsg.reasoning && {
1293+
reasoning: sessionMsg.reasoning,
1294+
}),
1295+
});
1296+
}
1297+
}
11991298
}
12001299
}
12011300
} catch (error) {
@@ -1420,8 +1519,89 @@ export async function handleConversationWithTools(
14201519
// 压缩后需要重新构建conversationMessages
14211520
conversationMessages = [];
14221521
const session = sessionManager.getCurrentSession();
1522+
1523+
// 1. 添加系统消息
1524+
conversationMessages.push({
1525+
role: 'system',
1526+
content: getSystemPrompt(),
1527+
});
1528+
1529+
// 2. 如果有TODOs,添加TODO上下文
1530+
if (existingTodoList && existingTodoList.todos.length > 0) {
1531+
const todoContext = formatTodoContext(
1532+
existingTodoList.todos,
1533+
);
1534+
conversationMessages.push({
1535+
role: 'user',
1536+
content: todoContext,
1537+
});
1538+
}
1539+
1540+
// 3. 压缩后重新获取并添加有用信息上下文
1541+
const usefulInfoService = getUsefulInfoService();
1542+
const updatedUsefulInfoList =
1543+
await usefulInfoService.getUsefulInfoList(
1544+
session?.id || '',
1545+
);
1546+
1547+
if (
1548+
updatedUsefulInfoList &&
1549+
updatedUsefulInfoList.items.length > 0
1550+
) {
1551+
const usefulInfoContext = await formatUsefulInfoContext(
1552+
updatedUsefulInfoList.items,
1553+
);
1554+
conversationMessages.push({
1555+
role: 'user',
1556+
content: usefulInfoContext,
1557+
});
1558+
}
1559+
1560+
// 4. 添加压缩摘要
1561+
conversationMessages.push({
1562+
role: 'user',
1563+
content: `[Context Summary from Previous Conversation]\n\n${compressionResult.summary}`,
1564+
});
1565+
1566+
// 5. 添加保留的消息(未完成的工具调用链)
1567+
if (
1568+
compressionResult.preservedMessages &&
1569+
compressionResult.preservedMessages.length > 0
1570+
) {
1571+
for (const msg of compressionResult.preservedMessages) {
1572+
conversationMessages.push(msg);
1573+
}
1574+
}
1575+
1576+
// 6. 添加会话中的其他消息(排除已保留的)
14231577
if (session && session.messages.length > 0) {
1424-
conversationMessages.push(...session.messages);
1578+
// 获取已保留的消息ID集合,避免重复
1579+
const preservedIds = new Set(
1580+
compressionResult.preservedMessages?.map(
1581+
msg =>
1582+
msg.tool_call_id ||
1583+
(msg.tool_calls && msg.tool_calls[0]?.id) ||
1584+
`${msg.role}-${msg.content.slice(0, 20)}`,
1585+
) || [],
1586+
);
1587+
1588+
for (const sessionMsg of session.messages) {
1589+
const msgId =
1590+
sessionMsg.tool_call_id ||
1591+
(sessionMsg.tool_calls &&
1592+
sessionMsg.tool_calls[0]?.id) ||
1593+
`${sessionMsg.role}-${sessionMsg.content.slice(0, 20)}`;
1594+
1595+
// 跳过已保留的消息和工具消息
1596+
if (
1597+
preservedIds.has(msgId) ||
1598+
sessionMsg.role === 'tool'
1599+
) {
1600+
continue;
1601+
}
1602+
1603+
conversationMessages.push(sessionMsg);
1604+
}
14251605
}
14261606
}
14271607
} catch (error) {

source/hooks/conversation/useToolConfirmation.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export function useToolConfirmation() {
5151
return (
5252
alwaysApprovedToolsRef.current.has(toolName) ||
5353
toolName.startsWith('todo-') ||
54+
toolName.startsWith('useful-info-') ||
5455
toolName.startsWith('subagent-')
5556
);
5657
},

source/i18n/lang/en.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ export const en: TranslationKeys = {
273273
codebaseTools: 'Codebase Search Tools',
274274
terminalTools: 'Terminal Tools',
275275
todoTools: 'TODO Management Tools',
276+
usefulInfoTools: 'Useful Information Tools',
276277
webSearchTools: 'Web Search Tools',
277278
ideTools: 'IDE Diagnostics Tools',
278279
userInteractionTools: 'User Interaction Tools',

source/i18n/lang/es.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,8 @@ export const es: TranslationKeys = {
291291
aceTools: 'Herramientas de Búsqueda de Código ACE',
292292
codebaseTools: 'Herramientas de Búsqueda de Base de Código',
293293
terminalTools: 'Herramientas de Terminal',
294-
todoTools: 'Herramientas de Gestión TODO',
294+
todoTools: 'Herramientas de Gestión de TODO',
295+
usefulInfoTools: 'Herramientas de Información Útil',
295296
webSearchTools: 'Herramientas de Búsqueda Web',
296297
ideTools: 'Herramientas de Diagnóstico IDE',
297298
userInteractionTools: 'Herramientas de Interacción del Usuario',

source/i18n/lang/ja.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,8 @@ export const ja: TranslationKeys = {
263263
codebaseTools: 'コードベース検索ツール',
264264
terminalTools: 'ターミナルツール',
265265
todoTools: 'TODO管理ツール',
266-
webSearchTools: 'Web検索ツール',
266+
usefulInfoTools: '有用情報ツール',
267+
webSearchTools: 'ウェブ検索ツール',
267268
ideTools: 'IDE診断ツール',
268269
userInteractionTools: 'ユーザー対話ツール',
269270
},

0 commit comments

Comments
 (0)