**崩溃/挂死修复 (5):** - 统一 TrayManager.isQuitting 变量,修复 Cmd+Q 无法退出 - useAgentStream 闭包过期快照 → 每次 getState() - Agnes chatStream 添加 AbortSignal.timeout - SSE JSON.parse 添加 try-catch 保护 - Orchestrator setTools 污染 → save/restore 模式 **功能修复 (14):** - 上下文压缩实现 (每5轮 COMPRESSING 状态) - 修复 requestId 硬编码空串 - ConfigService.set() 保留已有 category - MemoryManager 新增 working 类型搜索 - PromptInjectionDefender 补全 sanitize() - Ollama: 补全 dynamicReminders + reasoningContent - openai-format: 所有 assistant 消息保留 reasoningContent - SSE: finish_reason 时提前 flush tool_calls - DeepSeek thinking effort 映射注释 - Ollama done_reason load→stop - RateLimitHook >= 边界修复 - WorkspaceService isValid 首次启动修复 - sessions:archive IPC handler - 托盘/窗口图标路径生产环境修复 **系统提示词优化:** - SOUL.md 存在时不显示兜底身份,原文放最前 - 兜底身份改为中文 (MetonaAI 自身描述) - 用户文本在前,附件内容在后 **文件上传:** - 非图片文件不再 base64 编码,保留 JSON 结构 - 用户文本优先于文件内容 **UI 修复:** - 首页 Logo 路径修复 (public/ + 相对路径) - TokenUsage contextWindow 动态计算 (Provider 感知) - 切换 Provider 同步 contextWindow - 托盘图标始终显示 Logo (状态由右键菜单展示)
54 lines
1.7 KiB
TypeScript
54 lines
1.7 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> {
|
|
const key = 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 };
|
|
}
|
|
}
|