流式渲染修复: - 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
55 lines
1.8 KiB
TypeScript
55 lines
1.8 KiB
TypeScript
/**
|
|
* Pre-Tool Hooks — 工具执行前钩子
|
|
*
|
|
* @see docs/生产级通用 AI Agent 智能体桌面应用:完整设计与构建指南.html — 第五章
|
|
*/
|
|
|
|
import type { MetonaToolCall } from '../types';
|
|
import type { PolicyEngine } from '../sandbox/permissions';
|
|
|
|
export interface HookResult {
|
|
blocked: boolean;
|
|
reason?: string;
|
|
modifiedArgs?: Record<string, unknown>;
|
|
}
|
|
|
|
export interface PreToolHook {
|
|
beforeExecute(toolCall: MetonaToolCall, sessionId: string): Promise<HookResult>;
|
|
}
|
|
|
|
/** 权限校验钩子 — 集成 PolicyEngine */
|
|
export class PermissionCheckHook implements PreToolHook {
|
|
constructor(private policyEngine: PolicyEngine) {}
|
|
|
|
async beforeExecute(toolCall: MetonaToolCall, _sessionId: string): Promise<HookResult> {
|
|
const result = this.policyEngine.checkAuthorization(toolCall.name, toolCall.args);
|
|
if (!result.authorized) {
|
|
return { blocked: true, reason: result.reason };
|
|
}
|
|
return { blocked: false };
|
|
}
|
|
}
|
|
|
|
/** 速率限制钩子 */
|
|
export class RateLimitHook implements PreToolHook {
|
|
private callCounts = new Map<string, { count: number; resetTime: number }>();
|
|
|
|
constructor(private maxCallsPerMinute: number = 20) {}
|
|
|
|
async beforeExecute(toolCall: MetonaToolCall, sessionId: string): Promise<HookResult> {
|
|
// 使用 sessionId:toolName 作为 key,实现会话隔离的 per-tool 速率限制
|
|
const key = `${sessionId}:${toolCall.name}`;
|
|
const now = Date.now();
|
|
const entry = this.callCounts.get(key);
|
|
if (entry && entry.resetTime >= now) {
|
|
if (entry.count >= this.maxCallsPerMinute) {
|
|
return { blocked: true, reason: `Rate limit exceeded for tool "${toolCall.name}"` };
|
|
}
|
|
entry.count++;
|
|
} else {
|
|
this.callCounts.set(key, { count: 1, resetTime: now + 60_000 });
|
|
}
|
|
return { blocked: false };
|
|
}
|
|
}
|