refactor(tasks): 移植主 Agent 同款合并活动折叠(数据层)

把上一轮验证过的数据层搬到工作图基座上,图/树两种模式共用:

- 新增 src/process-activity.ts:宿主 ui-chat 的 process group 算法移植
  (activity() 分类表逐条照抄、liveToolDetail 键优先级与 160 字截断、
  counts 计数排序与并列规则、preparing 语义、按 user/reply 边界切范围)。
  按本轮设计裁剪掉逐阶段列表(阶段改由徽标 + 颜色表达)。新增一条
  偏差:最新范围为空(子代理刚回复完)时回退到上一个「做过事」的范围,
  否则已结算卡片的小条只会显示「已完成分析」。
- chatT:从宿主 chat 命名空间读 message.stepProcess.* 措辞(方法调用 +
  try/catch 兜底,key 未命中即回退),4 个回归用例钉住「不许解绑 bind」。
- subagents.live 载荷换新:SidebarChildLiveView { running?, text?, summary?,
  lastEventTime? },报告树内每个子节点(含已结算)与拓扑根;删除
  expand/stages 机制;childActivity → childLive(「不在 map 里 = 未运行」
  的读法随旧协议一起作废)。
- tasks-model 消费新形状;LiveLine 与节点详情浮窗改显示合并活动措辞。
- 删除 src/subagent-activity.ts 与 tests/subagent-activity.spec.ts。

验证:typecheck / lint 通过;pnpm test 1252 passed / 9 skipped。

(cherry picked from commit 5e5284f214846b0c366048acbee459f3a7a79456)
This commit is contained in:
Menghuan1918
2026-09-27 22:18:35 +08:00
parent 2a39a0c8b2
commit a454cfc9fe
21 changed files with 1198 additions and 338 deletions
+11 -6
View File
@@ -7,6 +7,7 @@
* the width-aware solver.
*/
import { describe, expect, it } from 'vitest'
import type { SidebarChildLiveView } from '../src/context-types.ts'
import {
GRAPH_GAP_Y,
GRAPH_LIVE_H,
@@ -50,6 +51,12 @@ function layoutRow(layout: ReturnType<typeof layoutTasksGraph>, id: string): num
return (box.y - GRAPH_PAD) / GRAPH_ROW_STRIDE
}
/** One running node's live view: a merged activity row, no text tail. */
const ACTIVITY: SidebarChildLiveView = {
running: true,
summary: { counts: [{ kind: 'read', count: 1 }], running: 'read', runningDetail: 'x' },
}
describe('layoutTasksGraph', () => {
it('places the root on row 0 and children one row below', () => {
const nodes: TasksNode[] = [agent('root'), agent('a', 'root'), agent('b', 'root')]
@@ -98,11 +105,11 @@ describe('layoutTasksGraph', () => {
expect(plain.boxes.get('mate')?.h).toBe(GRAPH_NODE_H)
})
it('reserves the live tail row only when the tail carries a tool AND text', () => {
it('reserves the live tail row only when the tail carries activity AND text', () => {
const nodes: TasksNode[] = [
agent('root'),
agent('tool-only', 'root', false, { live: { tool: { name: 'read', args: 'x' } } }),
agent('both', 'root', false, { live: { tool: { name: 'read', args: 'x' }, text: 'y' } }),
agent('tool-only', 'root', false, { live: ACTIVITY }),
agent('both', 'root', false, { live: { ...ACTIVITY, text: 'y' } }),
]
const layout = layoutTasksGraph(nodes)
// One live row either way (the tool row, or the meta-styled thinking
@@ -118,9 +125,7 @@ describe('layoutTasksGraph', () => {
expect(GRAPH_ROW_STRIDE).toBeGreaterThanOrEqual(tallest)
const nodes: TasksNode[] = [
agent('root'),
agent('busiest', 'root', false, {
live: { tool: { name: 'read', args: 'x' }, text: 'y' }, tasks: [task('t1')],
}),
agent('busiest', 'root', false, { live: { ...ACTIVITY, text: 'y' }, tasks: [task('t1')] }),
]
const layout = layoutTasksGraph(nodes)
expect(layout.boxes.get('busiest')?.h).toBe(tallest)