dongasai/running-sessions-float

running-sessions-float

仓库尚未提供可用的公开描述。

未分類deepseek-harnessdshsession-monitor

@dongasai/running-sessions-float

在 DSH Web GUI 左侧栏底部的动作区里放一块「进行中」坞:把正在跑、卡在等你处理和刚跑完还没看过的会话收成一张小列表,点一行就把主视图切过去;没有这类会话时收成一行细字,只留「暂无进行中」。坞是布局里的真实一块,排在这排动作的最上面,不浮在会话列表上。

官方侧栏本来就会在会话行上标运行状态点(StateDot)和待处理状态,但那些行分散在各个工作区分组里 —— 组一折叠就看不见,要判断「现在有几个会话在等我」得逐个展开找。

这个坞不新增数据源,只做聚合:复用同一套标准 Hook(useSessions / useSessionStatus),筛出 running、pending 或 completionUnread 的会话,按最近更新时间排,固定在侧栏底部动作区。它不轮询、不落盘、没有 Host 状态,也不改任何会话。

想知道 · 看 怎么装、怎么用、怎么排错 · 本文件 为什么这么实现(槽位取舍、过滤规则、空态、locale 降级) · IMPLEMENTATION.md 给 agent 的约束、命令、改动连带项 · AGENTS.md

本仓库是 DSH 独立插件仓库,当前只有这一个插件;仓库根就是插件包 —— package.json、index.js、client.js、cordis.patch.yml、locale/ 都在根目录,装哪个 profile 都指到仓库根。

功能列表

能力 · 说明 进行中聚合 · running === true 的会话进坞,行尾「运行中」 待处理聚合 · 有 pendingInteraction 的会话进坞,按类型显示「等待审批 / 计划待审 / 等待回答」;待处理优先于运行中 完成未读聚合 · completionUnread === true(主视图之外停了运行、结果还没看过)的会话进坞,行尾灰字「已完成」;打开该会话后官方清掉这个标记,行随之消失 一键切换 · 点行调用 uiWorkspace.openSession,主视图切到该会话 计数徽标 · 坞头右侧显示当前行数 头部状态点 · 只要还有会话在跑或在等你处理,绿点就脉冲;只剩「已完成」提醒时切成静止的 6px 成功色圆点,不再假装有任务在跑 空态细行 · 无进行中会话时收成一行无边框细字,灰点静止,不显示计数 中英双语 · 内置 zh / en 两套文案,跟随界面语言;locale 服务缺失时回落中文 仅 Web 客户端 · dsh.client.platform: web;headless 等没有 Web 客户端的 profile 不加载它 占位不遮挡 · 注册在 sidebar.footer.action(侧栏底部动作区),是布局里的真实一块,不盖住会话列表 与官方入口共存 · 坞独占动作区最上面一行,用量统计 会话管理 等原有入口照旧排在它下面,一个都不少 键盘可达 · 每行是 `,可 Tab 聚焦,Enter / Space 触发,:focus-visible` 有品牌色描边

不提供:停止运行、回答提问 / 批准审批、归档过滤、设置项 / 开关。那些动作属于官方浮层,坞刻意不复制第二套交互。

快速开始

四步:装 → 确认装配行 → 用 → 卸载。

1. 装插件

target 就是仓库根(package.json、index.js、client.js、cordis.patch.yml、locale/ 都在根目录)。 2026-10-25 之前包体塞在同名子目录 running-sessions-float/ 里,github: 直装和 #tag 钉版本全都装不出来 (pnpm 把没有 package.json 的根当空壳,依赖名落成 running-sessions-float#0.1、版本 0.0.0,随后 dsh plugin add 报 cannot resolve profile bundle);那层子目录已经删掉,现在三种装法都指仓库根。

方式一:Web 界面(推荐)

DSH Web → 左侧栏 插件 → 添加插件 → 填仓库根的绝对路径(在仓库根执行 realpath .)→ 安装 → 装完点 立即启用。

仓库地址(clone 用):https://github.com/dongasai/running-sessions-float.git

  • 「添加插件」接受:包名(可带版本)、Git 地址、压缩包、本地绝对路径。
  • 装完不点 立即启用,就只是「装着但没启用」。
  • 侧栏这个「插件」页才能装和开关;设置 → 内置插件 里的清单是只读的。

方式二:命令行 · git 源(分发)

dsh plugin --profile web add github:dongasai/running-sessions-float
# 或
dsh plugin --profile web add git+https://github.com/dongasai/running-sessions-float.git
# 钉版本(URL 后加 #<tag>,锁到某个发布版本来复现问题 / 稳定使用)
dsh plugin --profile web add github:dongasai/running-sessions-float#0.3

dsh plugin --profile 把参数转发给该 profile 目录里的 pnpm。装进来的是副本:之后改本仓库代码不影响已装的副本(想边改边用就走方式三)。

  • --profile 必填(不写直接报 required option '--profile not specified')。
  • 必须带 `git+` 或 `github:` 前缀。git@github.com:owner/repo.git 这种 scp 写法不会被认成 git spec,pnpm 会把它当路径,装出一条指向不存在目录的 link: 死链。
  • 钉版本:URL 后加 #(上面第三条就是钉 0.3),tag / 分支 / commit 都行。本仓库当前 tag 是 0.3,对应 package.json 的 0.3.0 —— 不带 # 则跟 main 走,每次装到的都可能是新代码。0.2 及更早的 tag 是「包在子目录」的旧布局(0.1 还是旧包名 @local/running-sessions-float),钉它们装不出当前这套,只能走「克隆 + checkout」。
  • pnpm 默认会拦 prepare 等依赖构建脚本;本包没有任何构建步骤(client.js 是手写的浏览器 bundle,由 Host 直接读盘发出),不会遇到。

本机实测(0.3,临时 `DSH_HOME`):github:dongasai/running-sessions-float#0.3 装完依赖名是 @dongasai/running-sessions-float github:dongasai/running-sessions-float#0.3(不是旧布局那种 running-sessions-float#0.1 0.0.0),副本 package.json 版本 0.3.0,dsh.profile.bundles 自动登记,--dump-config 能查到装配行(见下面「装完确认」)。

方式三:命令行 · 本地目录(link,开发)

# 在仓库根执行;--profile 必填,web 换成你要装进的 profile
dsh plugin --profile web add "link:$(pwd)"

dsh plugin --profile 把参数转发给该 profile 目录里的 pnpm。link: 装的是软链:改仓库里的 client.js,客户端半边会在一两秒内换新(见「生效条件」)。

这条命令要写 profile 目录,在宿主 shell 里跑;受限的 Agent 沙箱通常写不了 ~/.dsh。

让 Agent 装(等价路径,不是命令行):action: install_bundle、target: link:(或 github:dongasai/running-sessions-float#0.3)。这个动作需要 danger-full-access 批准(它会改 profile、装包、可能跑安装脚本);返回 application: applied 才算当场生效,restart-required 表示要重启 Harness。

钉版本:克隆 + checkout tag

tag 0.3 真实存在,对应 package.json 的 0.3.0。想连 git 历史一起钉住时这样装:

git clone https://github.com/dongasai/running-sessions-float.git
cd running-sessions-float
git checkout 0.3
dsh plugin --profile web add "link:$(pwd)"

0.2 及更早的 tag 里包还在子目录,realpath 要指到 /running-sessions-float(那一层已在新版删掉)。

包名与旧版本

包名是 `@dongasai/running-sessions-float`(2026-10-23 从 @local/running-sessions-float 改过来,与本机其它自研插件同一 scope)。改名是破坏性的:

  • 0.1 及更早的 tag 与 main 上 2026-10-23 之前的提交里,package.json、cordis.patch.yml、client.js 的 client id 都还是 @local/...;装那些版本得到的插件页条目与装配 id 都是旧名。
  • 已装旧名的 profile 不会自动迁移:依赖名、dsh.profile.bundles 选中项、node_modules/@local/running-sessions-float 软链都指向旧名。按下面「换包名时怎么升」重装一次即可。

换包名时怎么升(本机实测流程):

# 1) 移除旧名(先卸再装,避免两个名字同时被装配)
dsh plugin --profile web remove @local/running-sessions-float
# 2) 用当前仓库装新名
dsh plugin --profile web add "link:$(pwd)"
# 3) 确认依赖名、bundles 选中项、软链三处都换了
grep -n running-sessions-float ~/.dsh/profiles/web/package.json
ls -l ~/.dsh/profiles/web/node_modules/@dongasai/running-sessions-float
# 4) 确认装配行(应显示 @dongasai/running-sessions-float)
dsh --profile web --dump-config | grep -A2 running-sessions-float
# 5) 重启正在跑的实例(插件行在启动时合成)

pnpm 移除链接包时不一定会清掉旧 node_modules/@local/... 软链 —— 那条孤立软链不在 loader entries 里、不会被加载,但要留意将来 pnpm install 的歧义。确认它指向本仓库这个包再删(realpath 核验后再 rm,别删成别的插件的链接)。

装完确认

命令行可以直接看合成后的 profile 树(只读,不启动服务):

dsh --profile web --dump-config | grep -A2 running-sessions-float

期望输出(本机实测):

# == @dongasai/running-sessions-float
- id: running-sessions-float
  name: '@dongasai/running-sessions-float'

运行中的实例要重启才会加载它(插件行在启动时合成)。

五条通用坑:

  1. --profile 必填 —— 不写直接报 error: required option '--profile ' not specified。
  2. 只加依赖、没登记 dsh.profile.bundles = 装了不挂载 —— 命令行的 dsh plugin add 写的是依赖,挂载还取决于 bundles;实测清空 bundles 后 --dump-config 输出为空,所以装完必须用上面的命令验一遍。
  3. git spec 必须带 github: / git+ 前缀 —— git@github.com:owner/repo.git 这种 scp 写法会被 pnpm 当成本地路径,装出一条指向不存在目录的 link: 死链。
  4. 钉版本的 tag 必须真实存在 —— 本仓库是 0.3(对应 0.3.0)。0.2 及更早的 tag 是「包在子目录」的旧布局、0.1 还是旧包名 @local/running-sessions-float,钉它们装不出当前这套;那些版本只能「克隆 + checkout + link 子目录」。
  5. 不要在仓库根下再套一层同名子目录 —— 根没有 package.json 时 pnpm 会把仓库当空壳(依赖名落成 running-sessions-float#0.3、版本 0.0.0),dsh plugin add 随后报 cannot resolve profile bundle。2026-10-25 已按这条把结构改回「根即包」。

本仓库 private: true、未发布 npm,没有 registry 装法;package.json 里没有声明任何 DSH peer 依赖,不会触发版本兼容闸。

2. 用起来

装完等一两秒 —— 客户端半边在启动时加载,Web 组合会通过 client-hmr 把新模块推给页面;没出现就刷新一次页面。侧栏底部、设置行上方出现坞:

  1. 有进行中 / 待处理 / 刚跑完未读的会话时,坞头是「进行中」+ 计数徽标,下面一行一个会话:左边标题、右边状态。
  2. 行按会话的 updatedAt 从新到旧排;刚发过消息的会话在最上面。
  3. 点一行:主视图切到那个会话(不是开新页签)。悬停有提示「打开这个会话 — 标题」。
  4. 没有这样的会话时:坞收成一行无边框细字,文字「暂无进行中」,绿点变灰点、停止脉冲。
  5. 行多了列表在坞内滚动(坞最高 min(28vh, 200px),给下面的官方入口和设置行留位置)。

3. 确认它真的在工作

  • 插件页「已安装」列表里出现 进行中会话坞(英文界面:Running Sessions Dock)。
  • 装配层认得它:--dump-config 里能查到装配行(命令与期望输出见「快速开始 → 装完确认」)。
  • 在另一个会话发一条要跑一会儿的消息,回到当前会话:坞里应多出一行「运行中」,计数 +1。
  • 点那一行:主视图切过去。
  • 让某个会话卡在审批上(例如让它执行一条需要确认的命令):行尾应变成「等待审批」。
  • 让一个会话在你的另一个页签 / 主视图之外跑完:它的行尾应变成灰字「已完成」,而且不会从坞里掉出去。
  • 点那行「已完成」进去看一眼再回来:官方清掉 completionUnread,这行应当消失。
  • 把所有会话停下来且都看过:坞应收成一行「暂无进行中」细字。

看不到坞时按「排错」表逐条对。

4. 卸载

Web 插件页:停用这个插件 → 移除。或让 Agent 执行:

action: remove_bundle
target: @dongasai/running-sessions-float

remove_bundle 会先取消 bundle 选中、再删包。不要只跑 `pnpm remove`:依赖没了、dsh.profile.bundles 里的选中项还在,下次装配会解析不到这个包。

界面长什么样

侧栏(默认 280px,最窄 264px)
┌────────────────────────────┐
│ 工作区 A                   │
│  · 会话 1                  │
│ 工作区 B                   │
│  · 会话 2                  │
│  · 会话 3                  │
│                            │
│  ┌──────────────────────┐  │ ← 坞:侧栏底部动作区顶部
│  │ 进行中             3 │  │    独占一行,排在官方动作之上
│  │ 会话 2        运行中 │  │    最高 min(28vh, 200px)
│  │ 会话 3      等待审批 │  │    列表在坞内滚动
│  │ 会话 1        已完成 │  │    跑完但还没看过的也在这里
│  └──────────────────────┘  │
│  用量统计                  │ ← 原有入口照旧,排在坞下面
│  会话管理                  │
│  [设置]                    │
└────────────────────────────┘

逐块说明:

  • 坞头:状态点 + 标题 + 计数。只要还有会话在跑或在等你处理,绿点就脉冲(1.8s 循环);只剩「已完成」提醒时换成 6px 静止成功色圆点;空态灰点静止。
  • 行:整行是一个按钮。标题单行省略,状态文字不换行、右对齐。
  • 状态色:运行中用 --dsw-alias-state-success-primary;待处理用 --dsw-alias-state-warn-primary;已完成用次级文字色 --dsw-alias-label-tertiary(它是提醒,不是告警,不抢眼);其余文字 / 边框 / 背景都走主题 token,明暗主题都跟随。
  • 空态:不画边框、背景透明,收成 padding: 2px 6px 的一行 11px 次级色文字。
  • 位置:挂在 sidebar.footer.action 槽位,order: -10,排在所有官方底部动作之前;width: 100%,高度由内容决定,最高 min(28vh, 200px)。它占真实布局:坞变高时上方会话列表相应变矮,而不是被盖住。
  • 独占一行:宿主把这一行排成不换行的 flex-direction: row,而这条生态里的底部入口(用量统计 / 会话管理 / 搜索)都写着 flex: 0 0 100%,意思都是「我要占满一整行」。不换行时第一个就把别人挤出可视区。坞挂载时给那一行加 flex-wrap: wrap(promoteFooterContainer,另有一条等价 :has() 规则管首帧),于是每个入口各占一行、都在坞下面。
  • 为什么不是改成列:试过 flex-direction: column —— 列里 100% 会按高度算,搜索插件那个入口直接涨成整块 140px,把别人顶出屏幕。只有「可换行的行」同时满足它们的 100% 宽度语义。
  • 窄栏:侧栏折叠成 56px 窄栏时,槽位只给 wide: false,坞换成 36px 圆点 + 计数的圆片(rsf-rail),不摊开列表。
  • 点击范围:坞是普通文档流元素,没有浮层吃掉点击的问题;坞外的内容照常可点。

它怎么判断「进行中」

会话目录与运行状态都来自框架标准 Hook,插件自己不做任何请求:

条件 · 是否进坞 · 行尾状态 有 pendingInteraction · 是 · 按 kind 归类(见下表),优先于运行中与已完成 running === true 且无待处理 · 是 · 运行中 completionUnread === true 且已停下 · 是 · 已完成(灰字) blank === true(新建但还没发消息) · 否 · — origin === 'subagent' · 否 · — 其余空闲会话(含已看过结果的结束会话) · 否 · —

三个保留条件之间是并集,显示优先级是 待处理 > 运行中 > 已完成:一个会话既在跑又有待审批时显示「等待审批」,刚跑完但还没看过时显示「已完成」。

  • running 优先取状态快照;状态里没有这个字段(或没有该会话的状态)时,退回列表快照的 summary.running。
  • completionUnread 只从状态快照读,缺字段就当作「已读过」:列表快照里没有对应事实,猜一个会让坞凭空多出一行「已完成」。官方在主视图里停下运行时不会置这个标记,所以坞里出现的每一条「已完成」都是你没在当前视图里看过的。
  • kind 是各域自己定的字符串,插件只做大小写无关的包含匹配:

kind 实际值 · 坞里显示 approval · 等待审批 permission(当前没有发布者,兼容用) · 等待审批 plan-review · 计划待审 question · 等待回答 其它 / 未知 · 等待回答

官方目前会发布 approval、plan-review、question 三种;未知 kind 不会让会话消失,统一落到「等待回答」。

生效条件(客户端半边)

本插件 Host 半边是空的(index.js 只有一个空 apply()),没有 Host 状态、路由或配置,所以不存在「Host 改了要重启」的问题;所有行为都在浏览器半边。

  • 装的是 link:,改 client.js:Web 组合里的 client-hmr 每 500ms 轮询 bundle 文件的 mtime / ctime / size,变了就换新 revision 并通过 /plugins/events 推给浏览器;通常一两秒内自动换新,不用刷新。没换再刷新页面。
  • 部署没挂 client-hmr:Host 内存里还是旧字节,刷新页面也不够 —— 重新安装或重启 Harness 才会重读。
  • 装的是副本(git / tarball / npm):改仓库代码不影响已装副本 → 重新安装。
  • 改 package.json 的 dsh.client / dsh.bundle 字段:需要重新装配(重新安装或重启对应 profile)。

结构

running-sessions-float/                 ← 仓库根 == 插件包(安装 target)
├─ package.json                        # name / version / exports / files / dsh.bundle + dsh.client
├─ cordis.patch.yml                    # insert 一行插件入口
├─ index.js                            # Host 半边:export function apply() {}(8 行)
├─ client.js                           # 浏览器半边:CSS + 组件 + sidebar.footer.action 注册(477 行)
├─ locale/en.json                      # 插件页元数据(meta.title / meta.description)
├─ locale/zh.json                      # 同上,中文
├─ icon.svg                            # 插件图标
├─ image/1.png                         # README 顶部的预览截图(文档资产,不进 npm files)
├─ README.md
├─ IMPLEMENTATION.md
├─ AGENTS.md
├─ test/smoke.mjs                      ← 无 DOM 冒烟 + 包契约(42 项)
└─ .agents/index.json                  ← 架构扫描状态:模块清单 / 覆盖率 / 缺口

README.md / IMPLEMENTATION.md / AGENTS.md / test/ / .agents/ 靠 package.json 的 files 白名单挡在包外,只进 git。

文件 · 行数 · 作用 package.json · 32 · 包声明:dsh.bundle.patch 指向 patch,dsh.client 声明 web 平台、启动即加载、依赖 session UI 包 cordis.patch.yml · 3 · insert 一行 running-sessions-float → @dongasai/running-sessions-float index.js · 8 · Host 半边,空 apply();不提供服务、不注册工具、不挂路由 client.js · 477 · 浏览器半边:样式、组件、locale 注册、sidebar.footer.action 注册 locale/*.json · 6 × 2 · 只给插件页的标题 / 描述;界面文案在 client.js 的 DICTS 里 icon.svg · 11 · 插件图标 AGENTS.md · — · 给 agent 的命令、硬约束与改动连带项 test/smoke.mjs · 324 · 无 DOM 组件冒烟 + 包契约(42 项)

两侧注入

  • Host:无 inject。index.js 只导出 apply(),不依赖任何 Host 服务。
  • Client(client.js 模块返回):inject: ['slots'];apply(ctx) 里 ctx.get('locale') 与 ctx.slots.inject('sidebar.footer.action', ...);uiWorkspace 不在这里取,而是每次点击时现取(它由启动更晚的 workspace 插件补挂)。
  • 加载顺序(package.json → dsh.client.inject):@deepseek-ai/dsh-client-ui-session —— 它提供 useSessions / useSessionStatus 这两个全局标准 props,必须排在它之后激活。
  • 启动加载:immediately: true,启动时就加载,不等打开某个面板。

扩展点占用表

扩展点 · 类型 · id / 命名空间 · order · priority · patterns · 条件 · 说明 sidebar.footer.action · list 槽位 · running-sessions-float · -10 · 未设 · 无(该槽没有 patterns) · 无条件 · 唯一 UI 注册;list 槽按 order 升序渲染,负值排在官方动作(默认 0)之前 locale · 字典命名空间 · local.runningSessions · — · — · — · locale 服务存在时 · 注册 en / zh 各 8 个 key(title running approval plan answer done empty openHint);不在时用内置中文 uiWorkspace · 可选服务 · — · — · — · — · 每次点击 · 点击时才 ctx.get('uiWorkspace') 并调 openSession;不要在 `apply` 时缓存 —— 它由启动更晚的插件提供,缓存到的是 undefined;缺失则点击无动作

不占 sidebar / sidebar.workspaces.* / composer.* 等其它槽位;sidebar.footer.action 是本插件唯一的 UI 注册。

安全模型