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
This commit is contained in:
@@ -53,6 +53,7 @@ export function formatDuration(ms: number): string {
|
||||
// ===== 截断文本(< 20 行纯函数,允许自写)=====
|
||||
|
||||
export function truncate(text: string, maxLength: number): string {
|
||||
if (typeof text !== 'string' || text.length === 0) return '';
|
||||
if (text.length <= maxLength) return text;
|
||||
return text.slice(0, maxLength - 1) + '…';
|
||||
}
|
||||
|
||||
@@ -1,181 +0,0 @@
|
||||
// DEPRECATED: Components use window.metona directly. This file is kept for future type-safe refactoring.
|
||||
|
||||
/**
|
||||
* IPC Client — 渲染进程 IPC 封装
|
||||
*
|
||||
* 为渲染进程提供类型安全的 IPC 调用封装。
|
||||
* 所有调用通过 window.metona 桥接层(由 preload.ts contextBridge 暴露)。
|
||||
*
|
||||
* @see electron/preload.ts — contextBridge 安全暴露
|
||||
* @see src/types/global.d.ts — 类型声明
|
||||
*/
|
||||
|
||||
// DEPRECATED: Components use window.metona directly. This file is kept for future type-safe refactoring.
|
||||
// Types are now globally available via src/types/global.d.ts (script file, no import needed).
|
||||
|
||||
|
||||
// ===== 桥接层访问 =====
|
||||
|
||||
function getBridge(): MetonaBridge {
|
||||
if (!window.metona) {
|
||||
throw new Error('Metona bridge not available. Ensure preload script is loaded.');
|
||||
}
|
||||
return window.metona;
|
||||
}
|
||||
|
||||
// ===== Agent API =====
|
||||
|
||||
export const agentAPI = {
|
||||
/**
|
||||
* 发送用户消息到 Agent
|
||||
*/
|
||||
sendMessage(message: { role: 'user'; content: string; timestamp: number }, sessionId: string): Promise<{ success: boolean }> {
|
||||
return getBridge().agent.sendMessage(message, sessionId);
|
||||
},
|
||||
|
||||
/**
|
||||
* 监听流式事件
|
||||
* @returns 取消监听函数
|
||||
*/
|
||||
onStreamEvent(callback: (event: MetonaStreamEventData) => void): () => void {
|
||||
return getBridge().agent.onStreamEvent(callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* 监听状态变化
|
||||
* @returns 取消监听函数
|
||||
*/
|
||||
onStateChange(callback: (state: { sessionId: string; state: string }) => void): () => void {
|
||||
return getBridge().agent.onStateChange(callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* 中断当前会话
|
||||
*/
|
||||
abortSession(sessionId: string): Promise<{ success: boolean }> {
|
||||
return getBridge().agent.abortSession(sessionId);
|
||||
},
|
||||
|
||||
/**
|
||||
* 监听 Provider 切换通知
|
||||
* @returns 取消监听函数
|
||||
*/
|
||||
onProviderSwitched(callback: (data: { from?: string; to?: string; reason?: string }) => void): () => void {
|
||||
return getBridge().agent.onProviderSwitched(callback);
|
||||
},
|
||||
};
|
||||
|
||||
// ===== Sessions API =====
|
||||
|
||||
export const sessionsAPI = {
|
||||
list(): Promise<MetonaSessionInfo[]> {
|
||||
return getBridge().sessions.list();
|
||||
},
|
||||
|
||||
create(title?: string): Promise<MetonaSessionInfo> {
|
||||
return getBridge().sessions.create(title);
|
||||
},
|
||||
|
||||
rename(sessionId: string, title: string): Promise<{ success: boolean }> {
|
||||
return getBridge().sessions.rename(sessionId, title);
|
||||
},
|
||||
|
||||
delete(sessionId: string): Promise<{ success: boolean }> {
|
||||
return getBridge().sessions.delete(sessionId);
|
||||
},
|
||||
|
||||
getMessages(sessionId: string): Promise<Array<{
|
||||
id: string;
|
||||
role: string;
|
||||
content: string;
|
||||
reasoningContent?: string;
|
||||
toolCalls?: unknown[];
|
||||
timestamp: number;
|
||||
}>> {
|
||||
return getBridge().sessions.getMessages(sessionId);
|
||||
},
|
||||
|
||||
pin(sessionId: string, pinned: boolean): Promise<{ success: boolean }> {
|
||||
return getBridge().sessions.pin(sessionId, pinned);
|
||||
},
|
||||
};
|
||||
|
||||
// ===== MCP API =====
|
||||
|
||||
export const mcpAPI = {
|
||||
listServers(): Promise<MetonaMCPServerStatus[]> {
|
||||
return getBridge().mcp.listServers();
|
||||
},
|
||||
|
||||
addServer(config: { name: string; transport: 'stdio' | 'sse'; command?: string; args?: string[]; url?: string; enabled: boolean }): Promise<{ success: boolean }> {
|
||||
return getBridge().mcp.addServer(config);
|
||||
},
|
||||
|
||||
removeServer(name: string): Promise<{ success: boolean }> {
|
||||
return getBridge().mcp.removeServer(name);
|
||||
},
|
||||
|
||||
toggleServer(name: string, enabled: boolean): Promise<{ success: boolean }> {
|
||||
return getBridge().mcp.toggleServer(name, enabled);
|
||||
},
|
||||
};
|
||||
|
||||
// ===== Memory API =====
|
||||
|
||||
export const memoryAPI = {
|
||||
search(query: string, options?: {
|
||||
topK?: number;
|
||||
type?: 'episodic' | 'semantic' | 'working';
|
||||
minImportance?: number;
|
||||
}): Promise<MetonaMemorySearchResult[]> {
|
||||
return getBridge().memory.search(query, options);
|
||||
},
|
||||
};
|
||||
|
||||
// ===== Config API =====
|
||||
|
||||
export const configAPI = {
|
||||
get<T = unknown>(key: string): Promise<T | null> {
|
||||
return getBridge().config.get(key) as Promise<T | null>;
|
||||
},
|
||||
|
||||
set(key: string, value: unknown): Promise<{ success: boolean }> {
|
||||
return getBridge().config.set(key, value);
|
||||
},
|
||||
};
|
||||
|
||||
// ===== App API =====
|
||||
|
||||
export const appAPI = {
|
||||
getVersion(): Promise<string> {
|
||||
return getBridge().app.getVersion();
|
||||
},
|
||||
|
||||
getAppDataPath(): Promise<string> {
|
||||
return getBridge().app.getAppDataPath();
|
||||
},
|
||||
|
||||
openExternal(url: string): Promise<void> {
|
||||
return getBridge().app.openExternal(url);
|
||||
},
|
||||
|
||||
showItemInFolder(path: string): Promise<void> {
|
||||
return getBridge().app.showItemInFolder(path);
|
||||
},
|
||||
};
|
||||
|
||||
// ===== Toast API =====
|
||||
|
||||
export const toastAPI = {
|
||||
/**
|
||||
* 监听主进程 Toast 通知桥接
|
||||
* @returns 取消监听函数
|
||||
*/
|
||||
onShow(callback: (data: {
|
||||
type: 'success' | 'error' | 'warning' | 'info';
|
||||
message: string;
|
||||
options?: Record<string, unknown>;
|
||||
}) => void): () => void {
|
||||
return getBridge().toast.onShow(callback);
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user