新增: 批次工作落地(推进链/评估闭环/事件总线/并发/加固) + 技术债清理 + 文档整理

后端:
- 工作流推进链(D-03):advance_task/状态机/闸门走 df-nodes Node trait,conditions 条件引擎扩展
- 想法评估闭环:启发式评分+对抗评估,df-ideas/scoring + df-storage/idea_eval_repo + idea 前端打通
- 全局事件数据总线:df-ai/context+context_helpers+augmentation 跨模块解耦
- AI planner/plan_hint/intent:aichat B 路线并行多轮基础
- patch_file 加固(TD-03/04):读改写整体锁防 lost update,expected_hash 合约闭环
- 压缩超时兜底(F-15 卡死根治)
- F-09 多会话并发:LlmConcurrency per-conv + streamingGuard 前端守护 + verify 脚本
- 知识注入 DRY/skills/audit 扩展

清理:
- aichat 技术债(误报 allow/死导入/过时注释 30 项)
- URGENT.md 删除(11 项加急全解决/迁 todo)
- 文档整理(todo/待决策/待审查/ARCHITECTURE/INDEX + 总线/技术债审查新文档)
This commit is contained in:
lxy
2026-06-21 20:51:26 +08:00
parent 330bb7f505
commit bd6a41fe6e
111 changed files with 11931 additions and 1033 deletions
+114
View File
@@ -0,0 +1,114 @@
//! streaming 状态写收敛 guard(对齐 memory [[devflow-generating-statemachine]] 前端落地)。
//!
//! 背景:`state.streaming`(当前活跃视图的流式态)原本散布在 8+ 处直接赋值
//! (useAiSend/useAiEvents/useAiConversations/useAiWindow/useAiPanel/useAiStream),
//! 任一新增 return 漏写、或异常路径(前端 JS 错误/窗口崩溃恢复)跳过复位 →
//! streaming 永久 true 卡死输入框(stop 按钮常驻、新消息入队不发)。
//!
//! 本模块把写侧收敛到单一 `setStreaming` 入口,做三件事:
//! 1. 合法性观测:`true→true` / `false→false` 幂等场景记 debug 日志(不拒绝——
//! resetStreamWatchdog 等副作用场景需合法重入),异常模式(如 false 时仍在跑看门狗)
//! 记 warn 日志供排查卡死。
//! 2. 联动 generatingConvs:value=true 且带 convId → add(convId);value=false 且带 convId
//! → delete(convId)。不带 convId 的复位(全局兜底)不动 generatingConvs,因其由
//! AiCompleted/AiError 按 conversation_id 精确管理(避免误删并发会话生成态)。
//! 3. feature flag `df-ai-generating-statemachine`:关时 guard 退化为直接赋值,
//! 不校验不联动,回退原散布语义(灰度/回退开关)。
//!
//! 注:本 guard 是「写收敛 + 可观测 + 联动」三合一,不是拒绝式状态机——
//! streaming 是 boolean 无真正非法跃迁,guard 价值在集中入口与日志而非拦截。
import { state } from '@/stores/ai'
import { useAppSettingsStore } from '@/stores/appSettings'
/// feature flag key(appSettings KV)。关=回退散布语义(灰度/回退用)。
const STREAMING_GUARD_FLAG = 'df-ai-generating-statemachine'
const appSettings = useAppSettingsStore()
/// guard 是否启用(读 appSettings,默认开=true)。
///
/// 默认开的理由:本 guard 是收敛性改造(行为等价 + 加可观测),非行为变更,
/// 默认开启直接收效;flag 仅作紧急回退通道(若发现联动 generatingConvs 引入回归,
/// 用户可在设置里关掉回退原散布语义)。
function isGuardEnabled(): boolean {
return appSettings.get<boolean>(STREAMING_GUARD_FLAG, true)
}
/**
* streaming 写收敛入口 —— 替代散布的 `state.streaming = true/false`(原 14 处,现全收敛至此)。
* @param value 目标流式态
* @param opts.convId 关联会话 id。value=true 时 add 进 generatingConvs;
* value=false 时 delete 出 generatingConvs。省略则不动 generatingConvs
* (全局兜底复位场景,generatingConvs 由 AiCompleted/AiError 精确管理)。
* @param opts.reason 调用方标注(日志可观测,如 'AiCompleted'/'stopChat'/'onStreamTimeout')。
*
* 行为:
* - flag 关 → 直接 `state.streaming = value`(回退散布语义,不联动不校验)。
* - flag 开 → 幂等检测(value===当前值记 debug)、联动 generatingConvs、写日志。
*/
export function setStreaming(
value: boolean,
opts: { convId?: string | null; reason?: string } = {},
): void {
const { convId, reason } = opts
// flag 关:回退散布语义,直接赋值不动其他。
if (!isGuardEnabled()) {
state.streaming = value
return
}
const prev = state.streaming
// 幂等观测:同值重复写不拒绝(resetStreamWatchdog 等副作用场景需合法重入),
// 仅记 debug 日志供排查「为何重复置位」的卡死场景。
if (prev === value) {
console.debug(
`[AI-Guard] setStreaming 幂等(prev=${prev}, convId=${convId ?? 'none'}, reason=${reason ?? 'unknown'})`,
)
} else {
console.debug(
`[AI-Guard] setStreaming ${prev}${value} (convId=${convId ?? 'none'}, reason=${reason ?? 'unknown'})`,
)
}
// 联动 generatingConvs:仅在有 convId 时联动。
// value=true:convId 入 Set(标记该会话生成中,侧栏/分离窗口据此显示)。
// value=false:convId 出 Set(该会话收尾)。无 convId 的全局复位不动 Set
// (并发会话生成态由各自 AiCompleted/AiError 精确 delete 管理,全局清会误杀)。
if (convId) {
if (value) {
state.generatingConvs.add(convId)
} else {
state.generatingConvs.delete(convId)
}
}
state.streaming = value
}
/**
* 强制复位 streaming=false 的兜底入口(panic/卡死恢复用)。
*
* 与 setStreaming(false) 区别:本函数额外清 currentText + queue,
* 专用于看门狗超时 / 前端异常恢复等「必须彻底清态」场景。
*
* TD-260621-01 per-conv:看门狗超时携带 convId 时,仅清该 conv 的 generatingConvs
* (经 setStreaming 联动 delete),不再全清误杀并发会话生成态。不带 convId(全局兜底,
* 如 stopListener 卸载场景)时 generatingConvs 不动,由各自会话收尾事件精确管理。
*
* 注:currentText/queue 仍是单例(F-09 域,本批不动),per-conv 超时在多会话并发下
* 会清当前累积的 currentText——但 currentText 仅活跃视图写入(useAiEvents handleEvent delta 累积),
* 而超时仅发生在该 conv 长时间无数据,此场景 currentText 多为空或已 flush,
* 实际误伤面远小于 generatingConvs 全清(队列 queue 的清理由 F-09 统一改,见 TD-260621-01 注释)。
*
* @param reason 兜底来源(日志可观测)
* @param convId 关联会话 id(看门狗 per-conv 超时传;省略=全局兜底)
*/
export function forceResetStreaming(reason: string, convId?: string): void {
console.warn(`[AI-Guard] forceResetStreaming(卡死兜底): ${reason} (convId=${convId ?? 'none'})`)
setStreaming(false, { convId: convId ?? null, reason })
state.currentText = ''
state.queue = []
}