feat: v0.4.0 四阶段迭代 — 安全加固 + 工程基线 + 架构重构 + 双 Provider 扩展
P0 安全修复: - API Key 加密存储(safeStorage 密钥链,版本化前缀,历史明文平滑兼容) - 间接提示注入防护(SecurityScanHook 工具结果深扫描,网络工具脱敏/本地工具警示分级) - error:report IPC 断链修复(渲染进程错误上报落 electron-log + 审计) - abort 信号贯通工具层(run_command/dev-tools 子进程随会话中断终止) - run_command 沙箱加固(cd 系统目录/敏感文件读取拦截 + chcp 前缀剥离防解析退化) - .env 真实生效(dotenv 回退加载,应用内配置优先) P1 工程基础: - ESLint 9 flat config + 全部 34 条存量 warnings 清零(零容忍基线) - 测试基线 118 用例 11 文件(token/文件防护/权限/沙箱/注入/命令/引擎/注册表/审计链/摘要分层) - test:electron 双模式(ELECTRON_RUN_AS_NODE 跑 Electron ABI,SQLite 套件全执行) - SessionRecorder 多会话隔离 + 9 种 TRACE 事件补全(含最终轮 iteration_end) - Provider 故障转移(重试耗尽/不可重试一次性切换 fallback + 前端通知) - MCP 真就绪(等待全部连接完成再广播 tools:ready) - SLO/HealthChecker 真实接入(60s 巡检 + 托盘状态) - CONFIG_DEFAULTS 单一来源(消除 SEED 双源漂移) P2 架构升级: - handlers.ts 1940 行拆分为 13 个 IPC 域模块(防重入注册 + 多窗口广播) - AgentEngineManager 每会话独立引擎(LRU 30 + adapter 工厂隔离 abort 信号) - TaskOrchestrator EngineProvider 改造 + abortByParent 联动中断 SubAgent - 会话摘要分层上下文(session_summaries 滚动摘要 + 截断游标清理防因果污染) - 消息编辑重发/重新生成(truncateAfter IPC + store 动作 + UI) - Markdown 导出 / WebSearch 并行抓取(并发 3)/ 记忆 TF 缓存 / 版本构建期注入 P3 能力扩展: - OpenAI Adapter(o 系列推理模型 reasoning_effort/max_completion_tokens) - Anthropic Adapter(原生 Messages API:tool_use 块/角色合并/thinking budget/图片 base64/SSE 事件机) - 设置页/Onboarding 六 Provider 全链路接入
This commit is contained in:
@@ -21,6 +21,74 @@ function toErrorMessage(error: unknown): string {
|
||||
return error instanceof Error ? error.message : String(error);
|
||||
}
|
||||
|
||||
/** 配置默认值条目(P1-13: 单一来源,global-config.service.ts 的 SEED_DEFAULTS 由此派生) */
|
||||
export interface ConfigDefaultEntry {
|
||||
key: string;
|
||||
value: unknown;
|
||||
category: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 全部配置默认值(唯一维护点)
|
||||
*
|
||||
* 注意:新增/修改配置默认值时只需改这里,全局配置层(SEED_DEFAULTS 判定"未配置"的依据)
|
||||
* 会自动同步,避免双源漂移。
|
||||
*/
|
||||
export const CONFIG_DEFAULTS: ConfigDefaultEntry[] = [
|
||||
// LLM 配置(无硬编码值,用户必须手动配置)
|
||||
{ key: 'llm.provider', value: '', category: 'llm' },
|
||||
{ key: 'llm.model', value: '', category: 'llm' },
|
||||
{ key: 'llm.apiKey', value: '', category: 'llm' },
|
||||
{ key: 'llm.baseURL', value: '', category: 'llm' },
|
||||
{ key: 'llm.temperature', value: 0, category: 'llm' },
|
||||
{ key: 'llm.maxTokens', value: 63488, category: 'llm' },
|
||||
// P1: Provider 故障转移配置
|
||||
{ key: 'llm.fallbackProvider', value: '', category: 'llm' },
|
||||
{ key: 'llm.fallbackModel', value: '', category: 'llm' },
|
||||
{ key: 'llm.fallbackApiKey', value: '', category: 'llm' },
|
||||
{ key: 'llm.fallbackBaseURL', value: '', category: 'llm' },
|
||||
|
||||
// Agent 配置
|
||||
{ key: 'agent.maxIterations', value: 20, category: 'agent' },
|
||||
{ key: 'agent.totalTimeoutMs', value: 600000, category: 'agent' },
|
||||
{ key: 'agent.enableThinking', value: true, category: 'agent' },
|
||||
{ key: 'agent.thinkingEffort', value: 'high', category: 'agent' },
|
||||
{ key: 'agent.enableReflection', value: false, category: 'agent' },
|
||||
// C-10 修复: 补充缺失的 agent 配置默认值
|
||||
// @see project_memory.md — Tool confirmation timeout is configurable via agent.confirmationTimeoutMs (30s~600s, default 120s)
|
||||
{ key: 'agent.confirmationTimeoutMs', value: 120000, category: 'agent' },
|
||||
{ key: 'agent.toolExecutionTimeoutMs', value: 120000, category: 'agent' },
|
||||
|
||||
// 安全配置
|
||||
{ key: 'security.requireWriteConfirmation', value: true, category: 'security' },
|
||||
{ key: 'security.maxFileWriteSizeKB', value: 1024, category: 'security' },
|
||||
{ key: 'security.promptInjectionDefense', value: true, category: 'security' },
|
||||
|
||||
// UI 配置
|
||||
{ key: 'ui.theme', value: 'auto', category: 'ui' },
|
||||
{ key: 'ui.animationMode', value: 'auto', category: 'ui' },
|
||||
{ key: 'ui.fontSize', value: 'medium', category: 'ui' },
|
||||
|
||||
// 日志配置
|
||||
{ key: 'logging.level', value: 'info', category: 'logging' },
|
||||
{ key: 'logging.auditEnabled', value: true, category: 'logging' },
|
||||
{ key: 'logging.traceEnabled', value: true, category: 'logging' },
|
||||
|
||||
// Ollama 配置
|
||||
{ key: 'ollama.numCtx', value: null, category: 'ollama' },
|
||||
|
||||
// Provider 上下文窗口配置(用于 Engine 压缩判断和 UI 显示)
|
||||
{ key: 'deepseek.contextWindow', value: 1000000, category: 'deepseek' },
|
||||
{ key: 'agnes.contextWindow', value: 1000000, category: 'agnes' },
|
||||
{ key: 'mimo.contextWindow', value: 1000000, category: 'mimo' },
|
||||
// P3: OpenAI / Anthropic Provider
|
||||
{ key: 'openai.contextWindow', value: 128000, category: 'openai' },
|
||||
{ key: 'anthropic.contextWindow', value: 200000, category: 'anthropic' },
|
||||
|
||||
// Onboarding
|
||||
{ key: 'onboarding.completed', value: false, category: 'general' },
|
||||
];
|
||||
|
||||
export class DatabaseService {
|
||||
private db: Database.Database | null = null;
|
||||
private dbPath: string;
|
||||
@@ -212,6 +280,14 @@ export class DatabaseService {
|
||||
FOREIGN KEY (parent_id) REFERENCES tasks(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- ===== P2: 会话摘要表(分层上下文——超长会话早期消息压缩为摘要,LLM 只加载摘要 + 近期原文) =====
|
||||
CREATE TABLE IF NOT EXISTS session_summaries (
|
||||
session_id TEXT PRIMARY KEY,
|
||||
summary TEXT NOT NULL,
|
||||
summarized_until_rowid INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
|
||||
);
|
||||
|
||||
-- ===== 索引 =====
|
||||
CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id, created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_messages_role ON messages(role);
|
||||
@@ -257,7 +333,7 @@ export class DatabaseService {
|
||||
|
||||
// L-6 修复: 提取 tryAddColumn 辅助方法,消除 4 处重复的 try/catch 模式
|
||||
// L-8 修复: 使用 toErrorMessage 替代重复的 error instanceof Error 三元表达式
|
||||
const tryAddColumn = (table: string, column: string, type: string, migrationName: string) => {
|
||||
const tryAddColumn = (table: string, column: string, type: string) => {
|
||||
try {
|
||||
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${type}`);
|
||||
log.info(`[DB] Migration: added ${column} column to ${table}`);
|
||||
@@ -276,13 +352,18 @@ export class DatabaseService {
|
||||
// better-sqlite3 的事务是同步原子的,嵌套事务使用 SAVEPOINT 实现。
|
||||
const runAllMigrations = db.transaction(() => {
|
||||
// 迁移 1: messages 表添加 attachments 列
|
||||
tryAddColumn('messages', 'attachments', 'TEXT', 'attachments');
|
||||
tryAddColumn('messages', 'attachments', 'TEXT');
|
||||
// 迁移 2: audit_logs 表添加 iteration 列
|
||||
tryAddColumn('audit_logs', 'iteration', 'INTEGER', 'iteration');
|
||||
tryAddColumn('audit_logs', 'iteration', 'INTEGER');
|
||||
// v0.2.0 迁移 3: audit_logs 表添加 prev_hash 列(链式哈希)
|
||||
tryAddColumn('audit_logs', 'prev_hash', 'TEXT', 'prev_hash');
|
||||
tryAddColumn('audit_logs', 'prev_hash', 'TEXT');
|
||||
// v0.2.0 迁移 4: audit_logs 表添加 current_hash 列(链式哈希)
|
||||
tryAddColumn('audit_logs', 'current_hash', 'TEXT', 'current_hash');
|
||||
tryAddColumn('audit_logs', 'current_hash', 'TEXT');
|
||||
|
||||
// P2: 记忆 TF 缓存列(存储 tokenize 结果,避免每次检索重复分词)
|
||||
tryAddColumn('episodic_memories', 'tf_cache', 'TEXT');
|
||||
tryAddColumn('semantic_memories', 'tf_cache', 'TEXT');
|
||||
tryAddColumn('working_memories', 'tf_cache', 'TEXT');
|
||||
|
||||
// C-6 修复 迁移 5: 重建 messages 表,将 content 列从 NOT NULL 改为允许 NULL
|
||||
// @see project_memory.md — Assistant messages with tool_calls must set content to null
|
||||
@@ -345,73 +426,22 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入默认配置
|
||||
* 插入默认配置(P1-13: 从 CONFIG_DEFAULTS 单一来源派生)
|
||||
*/
|
||||
private seedDefaults(): void {
|
||||
const db = this.db!;
|
||||
|
||||
const defaults: Array<{ key: string; value: string; category: string }> = [
|
||||
// LLM 配置(无硬编码值,用户必须手动配置)
|
||||
{ key: 'llm.provider', value: '""', category: 'llm' },
|
||||
{ key: 'llm.model', value: '""', category: 'llm' },
|
||||
{ key: 'llm.apiKey', value: '""', category: 'llm' },
|
||||
{ key: 'llm.baseURL', value: '""', category: 'llm' },
|
||||
{ key: 'llm.temperature', value: '0', category: 'llm' },
|
||||
{ key: 'llm.maxTokens', value: '63488', category: 'llm' },
|
||||
{ key: 'llm.fallbackProvider', value: '""', category: 'llm' },
|
||||
{ key: 'llm.fallbackModel', value: '""', category: 'llm' },
|
||||
|
||||
// Agent 配置
|
||||
{ key: 'agent.maxIterations', value: '20', category: 'agent' },
|
||||
{ key: 'agent.totalTimeoutMs', value: '600000', category: 'agent' },
|
||||
{ key: 'agent.enableThinking', value: 'true', category: 'agent' },
|
||||
{ key: 'agent.thinkingEffort', value: '"high"', category: 'agent' },
|
||||
{ key: 'agent.enableReflection', value: 'false', category: 'agent' },
|
||||
// C-10 修复: 补充缺失的 agent 配置默认值
|
||||
// @see project_memory.md — Tool confirmation timeout is configurable via agent.confirmationTimeoutMs (30s~600s, default 120s)
|
||||
{ key: 'agent.confirmationTimeoutMs', value: '120000', category: 'agent' },
|
||||
{ key: 'agent.toolExecutionTimeoutMs', value: '120000', category: 'agent' },
|
||||
|
||||
// 安全配置
|
||||
{ key: 'security.requireWriteConfirmation', value: 'true', category: 'security' },
|
||||
{ key: 'security.maxFileWriteSizeKB', value: '1024', category: 'security' },
|
||||
{ key: 'security.promptInjectionDefense', value: 'true', category: 'security' },
|
||||
|
||||
// UI 配置
|
||||
{ key: 'ui.theme', value: '"auto"', category: 'ui' },
|
||||
{ key: 'ui.animationMode', value: '"auto"', category: 'ui' },
|
||||
{ key: 'ui.fontSize', value: '"medium"', category: 'ui' },
|
||||
|
||||
// 日志配置
|
||||
{ key: 'logging.level', value: '"info"', category: 'logging' },
|
||||
{ key: 'logging.auditEnabled', value: 'true', category: 'logging' },
|
||||
{ key: 'logging.traceEnabled', value: 'true', category: 'logging' },
|
||||
|
||||
// Ollama 配置
|
||||
{ key: 'ollama.numCtx', value: 'null', category: 'ollama' },
|
||||
|
||||
// Provider 上下文窗口配置(用于 Engine 压缩判断和 UI 显示)
|
||||
// v0.3.1: DeepSeek/Agnes 改为可配置,不再写死 1M
|
||||
{ key: 'deepseek.contextWindow', value: '1000000', category: 'deepseek' },
|
||||
{ key: 'agnes.contextWindow', value: '1000000', category: 'agnes' },
|
||||
// v0.3.4: MiMo 上下文窗口(与 DeepSeek 一致,1M)
|
||||
{ key: 'mimo.contextWindow', value: '1000000', category: 'mimo' },
|
||||
|
||||
// Onboarding
|
||||
{ key: 'onboarding.completed', value: 'false', category: 'general' },
|
||||
];
|
||||
|
||||
const insert = db.prepare(`
|
||||
INSERT OR IGNORE INTO app_config (key, value, category) VALUES (?, ?, ?)
|
||||
`);
|
||||
|
||||
const insertMany = db.transaction((items: typeof defaults) => {
|
||||
const insertMany = db.transaction((items: ConfigDefaultEntry[]) => {
|
||||
for (const item of items) {
|
||||
insert.run(item.key, item.value, item.category);
|
||||
insert.run(item.key, JSON.stringify(item.value), item.category);
|
||||
}
|
||||
});
|
||||
|
||||
insertMany(defaults);
|
||||
insertMany(CONFIG_DEFAULTS);
|
||||
log.info('Default config seeded');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user