大版本迭代,新增安全增强、Agent Loop 增强、UI/UX 增强,经六轮全面审计修复所有问题。
新增功能:
- OutputValidator 事实一致性检查 + 幻觉检测
- PromptInjectionDefender 语义级检测(指令性动词密度、角色边界、分隔符嵌套)
- DeadLoopError 死循环检测(连续3轮相同工具调用自动终止)
- PolicyEngine maxFrequency 滑动窗口频率限制
- MemoryManager TF-IDF 语义检索 + IDF 缓存原子替换
- 斜杠命令(/tool /memory /clear /export)+ 快捷键(Ctrl+B/J/Shift+F/N/[/])
- 专注模式(Ctrl+Shift+F)带面板状态快照保存/恢复
六轮审计修复(共修复 3 CRITICAL + 8 HIGH + 13 MEDIUM + 11 LOW):
CRITICAL:
- main.ts 传入 createAdapter 而非 reloadAdapter,导致 Provider 切换完全失效
- Ctrl+N/Ctrl+[/Ctrl+] 不同步 agent-store,导致消息发到错误会话
- engine.ts emit('error') 无监听器导致 DONE 事件丢失、前端卡死
HIGH:
- 死循环检测在工具执行之后(移入 PARSING 后 EXECUTING 前)
- deadLoop 事件前端未处理
- ConfirmationHook.clearPending 从未调用导致定时器泄漏
- engine.ts retry abort listener 未移除导致监听器堆积
- MCPManager JSON.parse 无 try-catch 导致初始化崩溃
- sendMessage 自动创建会话不同步 session-store
- setCurrentSession 竞态导致旧请求覆盖新会话数据
MEDIUM:
- toggleFocusMode 覆盖用户原有面板状态
- 正则检测可被常见词绕过
- 频率限制内存泄漏 + customPolicies 覆盖
- LIKE 回退转义未包含反斜杠
- OutputValidator 新功能未传入 toolResults/context
- MemoryConsolidator LLM 调用无超时保护
- AuditService 每次 log 都查询数据库
- browser-window-manager 超时后未停止页面加载
- output-validator URL 比较大小写敏感
- FileReader 无 onerror 导致 Promise 永久挂起
- /clear /export 不关闭斜杠菜单
- 专注模式下 toggleSidebar/toggleDetail 未恢复另一面板快照
LOW:
- abortPromise 事件监听器堆积
- RateLimitHook Map 内存泄漏
- memory.ts await 同步方法
- PolicyEngine 死代码清理
- Orchestrator sessionDepth 会话结束不清理
- workspace.service.ts 元数据插入边界问题
107 lines
3.9 KiB
TypeScript
107 lines
3.9 KiB
TypeScript
/**
|
|
* 记忆工具(2 个)
|
|
*
|
|
* memory_store, memory_search
|
|
*
|
|
* @see docs/MetonaAI-Desktop 架构与交互设计.html — 9 个基础工具
|
|
*/
|
|
|
|
import type { IMetonaTool, ToolExecutionContext } from '../../types/metona-tool';
|
|
import type { MetonaToolDef } from '../../../harness/types';
|
|
import { MetonaToolCategory, MetonaRiskLevel } from '../../../harness/types';
|
|
import type { MemoryManager } from '../../memory/manager';
|
|
|
|
// ===== 7. memory_store =====
|
|
|
|
export class MemoryStoreTool implements IMetonaTool {
|
|
readonly definition: MetonaToolDef = {
|
|
name: 'memory_store',
|
|
description: 'Store a piece of information in persistent memory. Useful for remembering important facts, decisions, or user preferences across sessions.',
|
|
parameters: {
|
|
type: 'object',
|
|
properties: {
|
|
content: { type: 'string', description: 'The memory content to store' },
|
|
type: { type: 'string', description: 'Memory type: "episodic" (events), "semantic" (knowledge), or "working" (task state)', enum: ['episodic', 'semantic', 'working'] },
|
|
importance: { type: 'number', description: 'Importance score 0-1 (default 0.5)' },
|
|
source: { type: 'string', description: 'Source of the memory', enum: ['user_input', 'tool_result', 'agent_thought', 'imported'] },
|
|
},
|
|
required: ['content', 'type'],
|
|
},
|
|
category: MetonaToolCategory.DATABASE,
|
|
riskLevel: MetonaRiskLevel.MEDIUM,
|
|
requiresPermission: false,
|
|
timeoutMs: 10_000,
|
|
};
|
|
|
|
constructor(private memoryManager: MemoryManager) {}
|
|
|
|
async execute(args: Record<string, unknown>, context: ToolExecutionContext): Promise<unknown> {
|
|
const content = args.content as string;
|
|
const type = args.type as 'episodic' | 'semantic' | 'working';
|
|
const importance = (args.importance as number) ?? 0.5;
|
|
const source = (args.source as 'user_input' | 'tool_result' | 'agent_thought' | 'imported') ?? 'agent_thought';
|
|
|
|
// v0.3.0 修复: store() 是同步方法,移除多余的 await 避免误导维护者
|
|
const id = this.memoryManager.store({
|
|
type,
|
|
content,
|
|
source,
|
|
importance,
|
|
sessionId: context.sessionId,
|
|
});
|
|
|
|
return { id, type, content_preview: content.slice(0, 100), importance };
|
|
}
|
|
}
|
|
|
|
// ===== 8. memory_search =====
|
|
|
|
export class MemorySearchTool implements IMetonaTool {
|
|
readonly definition: MetonaToolDef = {
|
|
name: 'memory_search',
|
|
description: 'Search persistent memory for relevant information. Returns memories sorted by relevance.',
|
|
parameters: {
|
|
type: 'object',
|
|
properties: {
|
|
query: { type: 'string', description: 'Search query or keywords' },
|
|
type: { type: 'string', description: 'Filter by memory type', enum: ['episodic', 'semantic', 'working'] },
|
|
topK: { type: 'number', description: 'Number of results (default 5)' },
|
|
threshold: { type: 'number', description: 'Minimum importance score 0-1 (default 0.7). Filters memories by importance, not search relevance.' },
|
|
},
|
|
required: ['query'],
|
|
},
|
|
category: MetonaToolCategory.DATABASE,
|
|
riskLevel: MetonaRiskLevel.SAFE,
|
|
requiresPermission: false,
|
|
timeoutMs: 10_000,
|
|
};
|
|
|
|
constructor(private memoryManager: MemoryManager) {}
|
|
|
|
async execute(args: Record<string, unknown>, _context: ToolExecutionContext): Promise<unknown> {
|
|
const query = args.query as string;
|
|
const type = args.type as 'episodic' | 'semantic' | 'working' | undefined;
|
|
const topK = (args.topK as number) ?? 5;
|
|
const threshold = (args.threshold as number) ?? 0.7;
|
|
|
|
// v0.3.0 修复: search() 是同步方法,移除多余的 await 避免误导维护者
|
|
const results = this.memoryManager.search(query, {
|
|
topK,
|
|
type,
|
|
minImportance: threshold,
|
|
});
|
|
|
|
return {
|
|
query,
|
|
results: results.map((r) => ({
|
|
id: r.id,
|
|
type: r.type,
|
|
content: r.content.slice(0, 500),
|
|
importance: r.importance,
|
|
score: r.score,
|
|
})),
|
|
count: results.length,
|
|
};
|
|
}
|
|
}
|