Files
thzxx 3c5aea8fb7 feat: 升级至 v0.2.1 — 流式渲染修复、安全增强、工具自动执行
流式渲染修复:
- runId 机制防止 abort 后旧流事件污染新 run
- run lock 防止并发 run 污染引擎状态
- abort race 提前退出工具执行等待
- TERMINATED 状态通过 stateChange 发射
- tool_call_delta 流式参数拼接 + pending 占位替换
- 首轮卡片创建路径统一,traceStep 按 ID 精确匹配
- compressed 事件转发为 toast 通知

安全增强:
- ConfirmationHook 支持持久化自动执行(跨会话)
- 设置面板新增自动执行工具管理 UI
- SandboxManager 双重安全校验 fail-closed
- 审计日志链式哈希防篡改
- PromptInjectionDefender 中文注入标记清理
- scanCode 28 模式 + base64/$() 检测
- validatePath realpathSync 防符号链接逃逸
- code-search 使用 execFile 防命令注入

新增工具:
- file_editor、code_search、task_manager、diff_viewer

其他:
- Agent Loop 加 PARSING/REFLECTING 状态 + 指数退避重试
- MemoryManager TF-IDF 语义检索
- run_command Windows 中文编码修复(chcp 65001)
- 版本号 0.2.0 → 0.2.1
2026-07-12 12:54:52 +08:00

64 lines
2.3 KiB
TypeScript

/**
* Post-Tool Hooks — 工具执行后钩子
*
* 用途:结果验证、审计日志写入、记忆触发、错误恢复建议。
*
* @see docs/生产级通用 AI Agent 智能体桌面应用:完整设计与构建指南.html — 第五章
*/
import type { MetonaToolCall, MetonaToolResult } from '../types';
import type { AuditService } from '../../services/audit.service';
import type { MemoryManager } from '../memory/manager';
import log from 'electron-log';
export interface PostToolHook {
afterExecute(toolCall: MetonaToolCall, result: MetonaToolResult, sessionId: string): Promise<void>;
}
/** 审计日志钩子 */
export class AuditLogHook implements PostToolHook {
constructor(private auditService: AuditService) {}
async afterExecute(toolCall: MetonaToolCall, result: MetonaToolResult, sessionId: string): Promise<void> {
this.auditService.logToolCall({
sessionId,
iteration: toolCall.iteration,
toolName: toolCall.name,
args: toolCall.args,
outcome: result.success ? 'success' : 'error',
result: result.result,
error: result.error,
durationMs: result.durationMs,
});
}
}
/** 记忆触发钩子 */
export class MemoryTriggerHook implements PostToolHook {
/** 仅对搜索类工具触发记忆,避免每次 read_file 都产生低价值记忆条目 */
private memorableTools = ['web_search', 'memory_search'];
/** 单次工具结果存储上限(字符),防止过大内容淹没记忆系统 */
private readonly MAX_MEMORY_CONTENT = 500;
constructor(private memoryManager: MemoryManager) {}
async afterExecute(toolCall: MetonaToolCall, result: MetonaToolResult, sessionId: string): Promise<void> {
if (this.memorableTools.includes(toolCall.name) && result.success) {
const content = typeof result.result === 'string' ? result.result : JSON.stringify(result.result);
try {
this.memoryManager.store({
type: 'episodic',
content: `Tool ${toolCall.name} returned: ${content.slice(0, this.MAX_MEMORY_CONTENT)}`,
source: 'tool_result',
sessionId,
importance: 0.6,
});
} catch (error) {
// 记忆存储失败不应影响工具执行结果
log.error('[MemoryTriggerHook] Failed to store episodic memory:', error);
}
}
}
}