feat: 任务管理页二级开关「有新后台任务时自动展开」(autoOpenTasks)

- 新 pref autoOpenTasks(默认开):当前会话出现新后台任务(任意新任务 id,
  非仅首个)时自动展开侧边栏并打开任务管理页,与 autoOpenSubagent 并列
- 注册为 subagent tab 的声明式二级开关(设置页齿轮弹窗,key 在 PrefsSchema)
- Sidebar 新增独立基线 effect(镜像子代理自动展开;刷新不触发、开关关闭/类型
  禁用不触发);detectNewTask 纯函数(settling/同 id/他会话不触发)
- 测试:detectNewTask 用例、prefs 解析断言、builtins 开关清单、schema 默认值
  断言;全量 371 通过
This commit is contained in:
Menghuan1918
2026-08-12 23:04:30 +08:00
parent b90251cce5
commit 88f116338a
13 changed files with 117 additions and 15 deletions
+23
View File
@@ -44,6 +44,7 @@ import type { TabDragPayload } from './TabBar.tsx'
import { relativeTo } from './paths.ts'
import { OrphanedTab } from './OrphanedTab.tsx'
import { detectNewDirectSubagent } from './subagent-detect.ts'
import { detectNewTask } from './subagent-tasks.ts'
import { t } from './locales.ts'
import { api, type SessionScope } from './api.ts'
import css from './sidebar.module.css'
@@ -241,6 +242,28 @@ export function Sidebar(props: { ctx: Context; store: SidebarStore }) {
ctx.betterSidebar?.openTab({ type: 'subagent', title: t('subagent') })
}, [sessionList, sessionId, store, ctx])
/**
* Task auto-activation: the moment a NEW background task appears for the
* current conversation (a task id the previous snapshot lacked), the
* auto-open pref is on, and the Tasks tab type is enabled, open the panel
* (if collapsed) and focus the Tasks page. Unlike the subagent trigger
* (0 → N only), ANY new task id triggers: the agent may start several
* tasks in one session, and each should surface. A fresh page load never
* triggers — its baseline starts at the current snapshot.
*/
const taskBaselineRef = useRef<SidebarSessionList | undefined>(undefined)
useEffect(() => {
const prev = taskBaselineRef.current
taskBaselineRef.current = sessionList
if (sessionId === undefined || prev === undefined) return
if (!detectNewTask(prev, sessionList, sessionId)) return
if (!store.getPrefs().autoOpenTasks) return
if (ctx.betterSidebar?.isTabEnabled('subagent') === false) return
store.reduce(s => s.panelOpen ? s : togglePanel(s))
store.reduce(s => ({ ...s, activePane: firstLeaf(s.splits).id }))
ctx.betterSidebar?.openTab({ type: 'subagent', title: t('subagent') })
}, [sessionList, sessionId, store, ctx])
/**
* Topology jump-back: clicking a subagent node on the Subagent page calls
* the official `openSubagent`, which switches the sidebar to that child