修复: 模型停用无效根因 + 上下文归位/压缩守卫

- 模型停用无效根因: get_active_provider 两条路径均不校验 enabled(显式活跃直返/默认只挑 is_default) → 收敛为启用集合解析,无启用 provider 明确报错
- 压缩/标题兜底模型不校验 enabled → 新增 resolve_fallback_model 纯函数(title 复用同一实现,单一源无双轨)
- 上下文多夹层归位修复: gather_tool_responses 聚拢该头全部响应(此前第二个夹层之后的响应仍被判未闭合剥除)
- 压缩后台任务加内容版本哨兵(取首条消息 id,否掉 len:压缩期间 loop 正常 append 会致守卫永不生效) + panic 兜底复位标志
- 902 超时类失败与其它 Fatal 区分停靠文案(点继续可续跑)
- 子代理事件按 conv 改挂(route_scope) + 运行期事件推父会话视图
- 飞书 13a 真根因: run_stream_refresher 死代码(提前 drop)+静默钟被心跳重置 → 帧通道+仅内容事件重置
- 项目详情文件查看: F22 目录不存在误显非 Git 仓库 / F24 模块为仓库子目录时 git 徽标全失效(scope_git_path 按 show-prefix 剥前缀)
- 更新徽章通道错配(Tauri 事件 vs window CustomEvent 同名不同通道,徽章此前永不触发)
- ProjectCard 卡片根 keydown 顶替内嵌按钮(补 e.target 守卫)
- i18n 坏键 ai.aiTool.rejectedHint → aiTool.rejectedHint(双缺会渲染裸 key)
- miniapp 三处: 会话列表刷新漏网/看门狗节流致新一轮无兜底/newConversation 漏清看门狗
This commit is contained in:
lxy
2026-10-10 08:08:48 +08:00
parent 2a3a6cd25b
commit 9c68f98d89
24 changed files with 1023 additions and 3644 deletions
+15
View File
@@ -9,6 +9,7 @@
class="update-badge-btn"
:class="{ 'is-active': expanded }"
:title="$t('settings.updateBadgeTitle')"
:aria-expanded="expanded"
@click="expanded = !expanded"
>
<span class="update-badge-dot"></span>
@@ -67,6 +68,15 @@ const installError = ref('')
let unlistenAvailable: UnlistenFn | null = null
let unlistenProgress: UnlistenFn | null = null
/** window CustomEvent 形态的更新通知:生产者 useUpdateCheck 用浏览器事件广播(非 Tauri emit),
* 与 Tauri 监听同名不同通道,故单列此处理器接住它。 */
function onUpdateAvailable(e: Event) {
const detail = (e as CustomEvent<UpdateAvailablePayload>).detail
if (!detail) return
info.value = detail
visible.value = true
}
onMounted(async () => {
try {
unlistenAvailable = await listen<UpdateAvailablePayload>('app-update-available', (e) => {
@@ -76,6 +86,10 @@ onMounted(async () => {
} catch {
/* 静默 */
}
// 通道对齐(2026-10-10 实证):生产者 useUpdateCheck 走的是**浏览器 window CustomEvent**
// (同名 'app-update-available'),与上面的 Tauri listen 不是同一通道 → 徽章此前永不触发。
// 保留 Tauri 监听(后端将来若改走 emit 亦可用),同时挂 window 监听接住现有生产者。
window.addEventListener('app-update-available', onUpdateAvailable as EventListener)
try {
unlistenProgress = await listen<{ stage: string; downloaded: number; total: number | null }>(
'app-update-progress',
@@ -93,6 +107,7 @@ onUnmounted(() => {
unlistenProgress?.()
unlistenAvailable = null
unlistenProgress = null
window.removeEventListener('app-update-available', onUpdateAvailable as EventListener)
})
const progressPercent = computed(() => {
+25 -3
View File
@@ -1,13 +1,21 @@
<template>
<div class="project-card" :class="{ 'project-card--list': view === 'list' }" @click="router.push('/projects/' + project.id)">
<div
class="project-card"
:class="{ 'project-card--list': view === 'list' }"
role="button"
tabindex="0"
@click="openProject"
@keydown.enter.prevent="onCardKey"
@keydown.space.prevent="onCardKey"
>
<div class="card-top">
<div class="card-title-row">
<h2 class="card-name">{{ project.name }}</h2>
</div>
<div class="card-top-actions">
<span class="stage-badge" :class="stageClass(project.status)">{{ $t(statusLabel(project.status)) }}</span>
<button class="card-del-btn" :title="$t('projects.archiveTitle')" @click.stop="emit('archive', project)">📦</button>
<button class="card-del-btn" :title="$t('projects.deleteTitle')" @click.stop="emit('delete', project)">🗑</button>
<button class="card-del-btn" :title="$t('projects.archiveTitle')" :aria-label="$t('projects.archiveTitle')" @click.stop="emit('archive', project)">📦</button>
<button class="card-del-btn" :title="$t('projects.deleteTitle')" :aria-label="$t('projects.deleteTitle')" @click.stop="emit('delete', project)">🗑</button>
</div>
</div>
@@ -74,6 +82,20 @@ const emit = defineEmits<{
const router = useRouter()
// 整卡点击进详情:可点根元素用 role/tabindex 兜底(卡内嵌套删除/归档按钮,不能整体换 <button>),
// 键盘与鼠标复用同一 handler
function openProject() {
router.push('/projects/' + props.project.id)
}
/** 卡片根键盘触发:仅当事件源就是根本身才开详情——卡内嵌套的归档/删除/展开按钮聚焦后按
* Enter/Space 会冒泡到此,原实现(.prevent 直接调 openProject)会把「删除」顶替成「跳转详情」
* 并取消按钮自身激活(2026-10-10 a11y 对抗核验实测缺陷)。 */
function onCardKey(e: KeyboardEvent) {
if (e.target !== e.currentTarget) return
openProject()
}
// 工程数(异步拉取,展示在卡片底部)
const moduleCount = ref(0)
// UX走查 P3-6: 加载态标记,模板据此显骨架占位而非 0(成功/失败都收 loading,失败仍显 0 属既有约定)