重构: 应用状态拆分+事件分发统一+路径授权治本
三项治本性架构重构,解决长期遗留的技术债: 一、应用状态文件拆分(原 1416 行单文件 → 按职责分模块) - 文件授权白名单(AllowedDirs/路径校验/黑名单)独立成模块 - 知识库行为配置(ExtractTrigger/KnowledgeConfig/KV 常量)独立成模块 - LLM 并发控制(LlmConcurrency 双层信号量)独立成模块 - 主文件仅保留 AppState 结构体与初始化逻辑 二、事件分发器统一(消除双监听器架构隐患) - 压缩/清空等 5 类生命周期事件纳入主事件联合类型 - 删除上下文管理模块的独立第二监听器 - 改为由主事件分发器统一处理,经回调注入副作用 - 架构保证:一个事件源 → 一个监听器 → 一个分发器 三、路径授权去除编译期硬编码(分发适配治本) - 启动时不再硬塞编译机路径到授权白名单 - 工程根授权完全靠项目绑定目录自动授权 + 设置页手动配置 - 分发到其他机器后不再有脏白名单数据
This commit is contained in:
@@ -141,6 +141,29 @@ export interface PendingHelp {
|
||||
}
|
||||
export const pendingHelp = ref<PendingHelp | null>(null)
|
||||
|
||||
// F-15 上下文管理生命周期事件回调(原 useAiContext 独立监听器,现合入统一分发器)。
|
||||
// useAiContext.initContextListener 注入回调,useAiEvents.handleLifecycleEvent 统一分发。
|
||||
// 设计:一个事件源(ai-chat-event)→ 一个监听器(useAiEvents)→ 一个分发器(handleEvent),
|
||||
// 架构保证无双重处理风险(原 useAiContext 第二监听器已删除)。
|
||||
export interface ContextCallbacks {
|
||||
/** 压缩开始(AiCompressing):置 loading=true */
|
||||
onCompressing?: () => void
|
||||
/** 手动压缩成功(AiManualCompressed):置 loading=false + toast + 回填摘要 */
|
||||
onManualCompressed?: (conversationId: string, summary: string) => void
|
||||
/** 自动压缩成功(AiAutoCompressed):静默置 loading=false(不弹 toast) */
|
||||
onAutoCompressed?: () => void
|
||||
/** 上下文已清空(AiContextCleared):toast + 刷新消息列表 */
|
||||
onCleared?: (conversationId: string) => void
|
||||
/** 错误(AiError,含压缩失败):仅在 loading 时置 false + toast */
|
||||
onError?: (conversationId: string, message: string) => void
|
||||
}
|
||||
let _contextCallbacks: ContextCallbacks = {}
|
||||
|
||||
/** 注入上下文生命周期回调(useAiContext.initContextListener 调用) */
|
||||
export function setContextCallbacks(callbacks: ContextCallbacks): void {
|
||||
_contextCallbacks = callbacks
|
||||
}
|
||||
|
||||
// L2 统一状态机 convStates/getConvState/setConvState 已下沉到 aiShared.ts(批4 双轨收口破环:
|
||||
// stores/ai.ts、useAiStream.ts、useAiConversations.ts 等读点 import getConvState 会与
|
||||
// useAiEvents 现有依赖构成环,故下沉到本模块既有的破环共享层)。本模块从 aiShared re-import。
|
||||
@@ -664,11 +687,14 @@ function handleLifecycleEvent(event: AiChatEvent): boolean {
|
||||
errorType: event.error_type,
|
||||
timestamp: Date.now(),
|
||||
} as AiMessage)
|
||||
// F-15 上下文管理:AiError 也用于压缩 IPC 失败场景。通知上下文回调复位 loading +
|
||||
// 弹错误 toast(若处于压缩中)。与上方流式错误处理不冲突——上下文回调内部判 isCompressing 守卫。
|
||||
_contextCallbacks.onError?.(event.conversation_id || '', event.error)
|
||||
return true
|
||||
}
|
||||
|
||||
case 'AiHelpRequired': {
|
||||
// L1 求助协议(§2.3,2026-06-21):后端断路器熔断已 guard.reset(generating=false,loop 终止)。
|
||||
// L1 求助协议(§2.3,2026-06-21):后端断路器瘝断已 guard.reset(generating=false,loop 终止)。
|
||||
// 前端按终止态收尾(对齐 AiError 分支:清看门狗/流式态/快照/队尾文本 flushCurrentText),
|
||||
// 但不创建错误气泡(求助非错误,是 AI 主动求助)——改为翻 pendingHelp 驱动求助卡(HelpRequiredCard)
|
||||
// 显 reason + options 按钮供用户选。
|
||||
@@ -710,6 +736,28 @@ function handleLifecycleEvent(event: AiChatEvent): boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
// F-15 上下文管理生命周期事件(原 useAiContext 独立监听器,现合入统一分发器)
|
||||
case 'AiCompressing':
|
||||
_contextCallbacks.onCompressing?.()
|
||||
return true
|
||||
|
||||
case 'AiManualCompressed': {
|
||||
// 手动 IPC 压缩(用户点压缩按钮):复位 loading + 弹 toast + 刷新视图回填摘要。
|
||||
_contextCallbacks.onManualCompressed?.(event.conversation_id || '', event.summary || '')
|
||||
return true
|
||||
}
|
||||
|
||||
case 'AiAutoCompressed': {
|
||||
// loop 自动压缩对桌面静默——仅复位 loading(冗余兜底,Auto 路径前端 loading 本就为 false),
|
||||
// 不调 onManualCompressed(防每次发送误弹 toast + 误 switchConversation 打断 LLM 流)。
|
||||
_contextCallbacks.onAutoCompressed?.()
|
||||
return true
|
||||
}
|
||||
|
||||
case 'AiContextCleared':
|
||||
_contextCallbacks.onCleared?.(event.conversation_id || '')
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user