Commit Graph
6 Commits
Author SHA1 Message Date
Menghuan1918 48294d336e refactor(tasks): 后台任务改读宿主客户端 jobs 服务,删除自建三条路由
- 新增 src/client/jobs-client.ts:结构探测 ctx.jobs(watchRows / observe /
  state / kill),引用计数的按会话 roster 订阅、只观测展开的那一行、
  跨树去重(无主任务归到承载它的被观测会话,树外 owner 丢弃)。
- 抽屉与输出浮窗改读该服务:行来自推送 roster(状态 / 时长 / owner / 进度),
  输出面换 TerminalBlock 显示宿主为观察者流的保留输出(gapBefore → 截断提示、
  error → 失败提示),kill 走 jobs.kill(owner, jobId),两击确认不变。
- 自动激活(use-host-feeds)改骑同一条推送 roster;detectNewJob 增加
  「观测起点时钟」参数,避免「页面打开时已在跑的任务」被当成新工作。
- 删除 src/jobs-routes.ts(+ tests/jobs-routes.spec.ts)、index.ts 的三条路由、
  api.jobsList/jobOutput/jobKill 与 JobOutputResult、context-types 的
  SidebarJobsService(换成 SidebarClientJobsService/SidebarJobsSnapshot/
  SidebarObservedJob 镜像,SidebarJobView 补 owner/progress)、
  页面里的 jobs.list 轮询与 collectTreeJobs。
- 三个 spec 改用假 ctx.jobs 服务(jobs-view 14 例 / 自动激活 12 例 /
  页面 20 例):观测只在展开行、切换与关闭释放观测、watch 隐藏时不建立、
  卸载时全部释放、无服务时不渲染任务面。

验证:typecheck / lint 通过;pnpm test 1240 passed / 9 skipped;pnpm build + pack 通过。

(cherry picked from commit 7d9b82e73190218f05910433ed520cb90e4d4ab2)
2026-09-27 22:18:35 +08:00
Menghuan1918 f584b67813 feat(tasks): 图节点改为双段式卡片 + 折叠聚合 + 窄栏可读性
图模式(主显示模式)换成新的卡片语言,树模式保持不变:

- 卡片 = 上段(类型徽章 + 相位徽标 + 名称两行 + mono 元信息 + 团队任务行)
  + 下段小条(状态点 + 状态词 + 主 Agent 同款合并活动 + 完成后的折叠 chevron)。
  类型徽章用页面自己的词汇(主代理 / 子代理 / 成员 / 工作流 / ✓N 已完成)。
- 分组色(层级):按树深度取 4 档令牌色阶(color-mix 混 --dsw-alias-state-business-primary),
  作用于左描边与上段极淡底色;相位徽标走 ink 色族,两套分组不撞色。
  相位虚线框保留但改用同色极淡描边,卡片徽标承担「这是哪个阶段」。
- 执行态:小条 `data-running` + 从左到右扫光(1.5s,末 1/3 停顿,
  prefers-reduced-motion 关闭);合并活动行来自宿主 chat 词条,缺失时回退插件文案。
- 折叠:已完成节点的 chevron 把该节点「收进已完成聚合」(模型新增 foldedIds,
  与全局 folded 走同一组守卫:只在已结算叶子、非当前会话、非团队成员时生效);
  聚合节点点击 = 全部展开(清空手动集 + 关闭全局折叠);控制条仍只管全局规则。
- 布局与窄栏(用户反馈 a–f):卡片高度预算按新行重算(徽章 + 两行标题 + 元信息 +
  小条 = 103px,任务行 +18),宽度 176 → 190;fit 按内容包围盒双轴居中、
  FIT_MAX_SCALE 1.15 → 1.30;缩放范围 0.4–2.0 并归一化 wheel deltaMode;
  平移加边界夹取(至少 72px 内容留在视口内);空画布双击重新适配并恢复自动 fit;
  连线改「垂直出线 stub + 贝塞尔」并加粗到 1.5px。

验证:typecheck / lint 通过;pnpm test 1253 passed / 9 skipped(含新增双段结构、
data-running、chevron → 聚合、聚合 → 全部展开、工作流相位徽标 5 例)。

(cherry picked from commit aee571e50beb5dc979995f0841cffd0a575f2173)
2026-09-27 22:18:35 +08:00
Menghuan1918 a454cfc9fe 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)
2026-09-27 22:18:35 +08:00
Menghuan1918 6586e3e2d3 feat: adapt to DSH 0.1.7-alpha.1 and yield the read-only previews
DSH 0.1.7-alpha.1 is 1299 commits past 0.1.6-alpha.2 and rewrites four
contracts this plugin was built on. The peer floor moves to
^0.1.7-alpha.1 (semver's prerelease rule means ^0.1.6-alpha.2 can never
match a 0.1.7 prerelease); 0.1.6-alpha.2 and earlier stay on v0.19.1.
Released as 0.21.0-alpha.1 (dist-tag alpha).

Hard breaks:
- dsh-settings was rewritten: register(ns, schema) is gone, a form is
  addressed by the plugin row's Loader entry id and read from the
  module's exported Config. The preferences are merged into Config and
  marked volatile — which requires DSH's own schemastery, because only
  that build wraps a volatile field in the cosmokit reference the
  loader's volatile commit walks. With the public package a settings
  write reports success and revision 1 while the effective value never
  changes.
- ui-primitives renamed its whole icon family (IconXxx14/16 ->
  IconXxxRegular/Medium): 26 imported symbols, 17 files. The failing
  mode is render-time, not load-time. Five call sites that relied on the
  old 14px default now pass size={14} explicitly.
- Session format v4 rejects source.kind === 'plugin'; the side-chat
  boundary injection now writes plugin:dsh-better-sidebar, the same kind
  DSH's own v3->v4 migration produces, so historical threads survive.
  Tool results moved to role 'tool' with top-level content/isError; all
  four parsers accept both shapes.
- The client session snapshot dropped subagentsByParent/jobsBySession
  and the on-demand subagent-catalog observation API. The Tasks page
  reads projectionsBySession and a new jobs.list route.

Yields and convergence:
- The read-only previews go to DSH's documentpreview (Office->PDF, a
  spreadsheet worker, zoom, per-directory refresh); editor.canOpen
  refuses those 26 extensions. Markdown, HTML (with its sandbox
  switches) and the editable code buffer stay.
- The host browser kind is desktop-profile-only now, so the web e2e
  lanes expect no browser entry and assert its absence.
- Link takeover claims only urlTarget links and passes the rest to the
  host, fixing a regression from the 0.1.6 round: the fallback pointed
  at a 'browser' type the plugin no longer registers, so http links in
  its own markdown did nothing.

Also fixed, found along the way:
- The file tree refreshes live (new directory watch over the plugin's
  own socket), since the plugin owns the `files` kind.
- ctx.sessions.list has no `current` field at either tag; the three
  readers were dead. They now use ctx.sidebarRight.mounted.
- package.json.files was missing lib/client-locale.js.
- Two genuinely dead call sites: sessions.openSubagent/open/
  refreshSubagents do not exist on the current baseline, so the
  topology click, the jump-back and the retry were all no-ops.
- The test harness window shim lacked addEventListener, which the 0.1.7
  primitives call at module-evaluation time; and the e2e token exchange
  needs Playwright's request stack, not Node's fetch.
2026-09-22 17:09:08 +08:00
Menghuan1918 936df662b0 test(specs): 收敛 28 个组件 spec 的渲染样板到共享 test-utils
- 7 个 spec 的本地 mount()(createRoot + act 容器循环)替换为
  renderRoot():sidechat-view、subagent-jobs-view、subagent-live-polling、
  lazy-chunk、side-card-section-rows(本地 mount 删除,调用点改名);
  editor-refresh、markdown-manual-refresh(EditorHost 包装 mount 改为
  委托 renderRoot,调用点不动)
- 24 个 spec 的内联 IS_REACT_ACT_ENVIRONMENT 赋值替换为 setupReactAct()
  (subagent-jobs-view / subagent-live-polling / lazy-chunk 原本未设
  flag,保持原样不引入)

全部为语义相同的样板替换:迁移后全量 118 files / 1241 passed +
9 skipped,与迁移前一致。
2026-09-03 12:04:35 +08:00
NuperAkiandMenghuan1918 6cea4bdd6f fix(subagent): 子代理页实时预览改为批量接口,避免 O(N²) history 请求风暴 (#298)
* fix(subagent): 子代理页实时预览改为批量接口,避免 O(N²) history 请求风暴

## 背景

DSH Web UI 中,当主代理同时运行多个子代理时,`dsh-better-sidebar` 的「任务管理/子代理」页面会出现明显卡顿。经调查,卡顿根因不在页面渲染,而在实时预览的请求模式。

## 根因

旧实现中,每个 running 子代理卡片都独立轮询 `subagents.history`:

- N 个 running 子代理 → 每 3 秒产生 N 次 `subagents.history` 请求;
- 每次 `subagents.history` 都会在 host 侧触发一次完整的 `listChildren` 全量子代理目录枚举;
- 子代理数量越多,服务端重复扫描越严重,近似 O(N²) 放大;
- 请求超过 3 秒时,客户端还会 abort 旧请求并重发,进一步形成请求风暴。

## 改动内容

1. **新增 host 批量接口 `POST /sidebar/api/subagents.live`**
   - 请求 `{ rootSessionId }`;
   - 通过 `ctx.subagents.listDescendants(rootSessionId)` 一次枚举整棵子代理树;
   - 仅对 running 且非 Side Chat 的子代理,从 session events 尾部提取最新 `text/tool` 活动;
   - 返回 `{ live: Record<childId, { text?, tool? }> }`;
   - 缺 `subagents`/`agents` 服务或枚举失败时返回 503;单个子代理日志异常只跳过该子代理。

2. **客户端改为单轮询 + 单飞行请求**
   - 删除每个 running 子代理独立的 `subagents.history` 定时器;
   - 在 `SubagentView` 中合并为一个 `useSubagentLive` 轮询 hook;
   - 使用递归 `setTimeout` 和单在途请求,慢请求不再被 abort/重发;
   - 页面隐藏或 root 变化时停止轮询并中止在途请求。

3. **解析器优化**
   - `lastActivity` 改为接收原始 `SidebarSessionEvent[]`;
   - 从事件尾部向前扫描,找到最新 text 和 tool 后提前停止,避免复制/遍历完整历史。

4. **UI 保持不变**
   - 实时预览的展示逻辑、文案、截断规则均未改变。

## 测试

- 新增 `tests/subagent-live-route.spec.ts`:覆盖批量接口成功聚合、只返回 running、跳过 Side Chat、缺服务 503、枚举失败 503、`ctx.get` 回退、bad-request。
- 新增 `tests/subagent-live-polling.spec.tsx`:覆盖每轮只发 1 次 `subagents.live`、不再调用 `subagents.history`、单在途不并发。
- 更新 `tests/subagent-activity.spec.ts` 与 `tests/subagent-jobs-view.spec.tsx`。
- `pnpm typecheck` ✅
- `pnpm build` ✅
- 相关测试 29 个全部通过 ✅

> 本机完整 `pnpm test` 仍有与本改动无关的环境性失败(node-pty 超时、Windows CRLF 断言、pty-deps 路径断言),不影响本 PR 逻辑。

* fix(subagent): live 预览以 catalog activity 为准,删除 agents 第二事实源 (#298)

- host 路由 running 门改回 entry.activity === 'running'(与客户端卡片渲染门一致),
  恢复 PR 承诺的 UI 不变:turn 间 idle 的 continuable 子代理仍显示最后活动
- 删除 ctx.agents 依赖与 SidebarAgent.status 新增(agents 缺失时不再静默 200 空 map;
  503 契约收窄为只涉及 subagents,与文档/测试一致)
- service 解析统一为 ctx.get('subagents')(兄弟路由惯例;插件未 inject subagents,
  直接属性访问有抛错风险)
- buildSubagentLiveApi 签名改收完整 Context,删除 SidebarSubagentLiveContext 窄接口
- api.ts 复用 LastActivity,删除重复类型 SubagentLiveEntry
- 测试:route spec 删除 agents 相关用例;polling spec 新增页面隐藏停止轮询、
  root 变化清空 live 并重启轮询两个用例

* fix(subagent): live 预览恢复最近 12 条消息窗口,排除陈旧活动 (#298)

CR 反馈:新批量接口把完整 session event log 喂给 lastActivity,
- 长时间只输出文本、早期调用过工具的 running 子代理会持续显示陈旧 tool
- 无 tool 的长会话每次轮询反向扫描整个事件数组,重新引入与历史长度
  相关的 CPU 成本

修复(与旧 subagents.history({ maxMessages: 12 }) 窗口语义一致):
- lastActivity 增加可选 maxMessages 参数:反向扫描按表面消息
  (user/message + assistant/message)计数,窗口满后更老事件一律跳过
- 路由传 LIVE_WINDOW_MESSAGES = 12(导出常量)
- 回归测试:窗口外旧 tool 不返回;窗口内 tool 保留;非消息事件不消耗配额

---------

Co-authored-by: Menghuan1918 <menghuan2003@outlook.com>
2026-08-21 18:15:49 +08:00