dsh-plugin-yanyan-toolkit 0.0.0-stage → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/LICENSE +29 -0
- package/README.i18n.yaml +6 -0
- package/README.md +65 -2
- package/README.zh.md +66 -0
- package/cordis.patch.yml +35 -0
- package/icon.svg +8 -0
- package/lib/client.js +26 -0
- package/lib/index.js +44 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +79 -4
- package/packages/dsh-plugin-yanyan-subagent/README.md +72 -0
- package/packages/dsh-plugin-yanyan-subagent/README.zh.md +72 -0
- package/packages/dsh-plugin-yanyan-subagent/cordis.patch.yml +8 -0
- package/packages/dsh-plugin-yanyan-subagent/icon.svg +8 -0
- package/packages/dsh-plugin-yanyan-subagent/lib/client.js +1250 -0
- package/packages/dsh-plugin-yanyan-subagent/lib/index.js +951 -0
- package/packages/dsh-plugin-yanyan-subagent/lib/legacy-hidden-ids.json +9 -0
- package/packages/dsh-plugin-yanyan-subagent/lib/log-rewrite.js +382 -0
- package/packages/dsh-plugin-yanyan-subagent/lib/zstd-frames.js +81 -0
- package/packages/dsh-plugin-yanyan-subagent/locale/en.json +1 -0
- package/packages/dsh-plugin-yanyan-subagent/locale/zh.json +1 -0
- package/packages/dsh-plugin-yanyan-subagent/package.json +32 -0
- package/packages/dsh-plugin-yanyan-update/README.md +24 -0
- package/packages/dsh-plugin-yanyan-update/README.zh.md +24 -0
- package/packages/dsh-plugin-yanyan-update/cordis.patch.yml +7 -0
- package/packages/dsh-plugin-yanyan-update/icon.svg +5 -0
- package/packages/dsh-plugin-yanyan-update/lib/client.js +400 -0
- package/packages/dsh-plugin-yanyan-update/lib/index.js +840 -0
- package/packages/dsh-plugin-yanyan-update/locale/en.json +1 -0
- package/packages/dsh-plugin-yanyan-update/locale/zh.json +1 -0
- package/packages/dsh-plugin-yanyan-update/package.json +32 -0
- package/scripts/cleanup-ghost-subagents.mjs +241 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here.
|
|
4
|
+
|
|
5
|
+
## 1.2.0 — 2026-11-14
|
|
6
|
+
|
|
7
|
+
首个公开发布版本,聚合「插件更新」「会话管理」「子智能体管理」三个可独立开关的插件。
|
|
8
|
+
|
|
9
|
+
### 子智能体管理:删除改为物理删除 + 日志根除
|
|
10
|
+
|
|
11
|
+
- **根因**:旧版删除只调用 `archiveSession` 归档,既删不掉 mnemon 虚拟委派(报 `session/not-found`),也移除不了父会话 `subagentCatalog` 的 append-only 引用,导致列表里仍显示该条目;点进去则报 `subagent is unavailable`。
|
|
12
|
+
- **五层删除**:① 真实会话优先复用现成 `/api/dsh-session-archive/delete` 物理删除管线;② host `/purge` 兜底清会话目录、projcache 与 catalog 引用、workspace 记账、归档台账与归档标记;③ mnemon 虚拟 id 直走第 2 层;④ v1.2.0 借鉴 `huajiao1998/dsh-plugin-subagent-clean@0.3.0`(MIT,原始实现 `heiheiha798/dsh-plugin-subagent-delete@0.2.3`)的「原位退休」,把父会话日志 `session.v4.jsonl.zstd` 里的 `subagent/catalog` 事件改写为 `subagent-delete/retired`(保留 seq/time、ignorable),用 `node:zlib` 的 zstd 与两次 stat + 原子 rename 保证安全,从 append-only 源头根除目录行;失败排队到 `yanyan-subagent-pending.json` 下次启动补做;⑤ `localStorage['yanyan-subagent.deletedIds']` 隐藏清单兜底。
|
|
13
|
+
- **结合两种清理方法**:用户建议的 `mnemon_forget` 删的是 Mnemon 记忆洞察(insight)而非子智能体会话,按字面调用不对症;其「让 Agent 也能触发清理」的意图改由注册三个可选模型工具承接:`yanyan_list_subagents` / `yanyan_delete_subagent` / `yanyan_cleanup_subagents`。client 新增「清理残留行」按钮(`/scrub`)、一键清理只改写一次日志(`/cleanup`)、`ctx.sessions.refresh()` 让侧边栏残留行同步消失。
|
|
14
|
+
- 子包新增 `lib/log-rewrite.js`、`lib/zstd-frames.js`(MIT 署名保留)与 8 个测试文件。
|
|
15
|
+
|
|
16
|
+
### 插件更新:修复「更新时卸载后没装回」
|
|
17
|
+
|
|
18
|
+
- **判定为真实代码 bug**:旧 registry 更新路径无条件先 `removeBundle(id)` 再 `installBundle(targetSpec)`,失败时只做一次 best-effort 回滚;第二次安装面对同一组失败条件(registry 超时 / 版本不存在 / engines 不兼容 / 构建脚本被拒),且 profile 状态已改动,回滚失败是结构性可能。
|
|
19
|
+
- **修复**:首选官方 `pluginManager.updateBundle(name, version)`(内建失败/取消自动装回原版本,非 HMR 根本不先卸载),本分支零 `removeBundle` 调用;无该 API 时降级为「先覆盖安装,仅 already-installed 才两阶段」,并硬保证回滚装回原版本(最多 2 次 + profile 依赖二次核验兜底);找不到管理器时直接返回且不动安装状态。client 失败信息改为在面板如实展示。
|
|
20
|
+
- 新增 9 个 mock 单测覆盖「官方通道零卸载」「抛异常 restored」「覆盖安装失败从未卸载」「两阶段失败回滚装回原版本」等失败路径。
|
|
21
|
+
|
|
22
|
+
### 发布
|
|
23
|
+
|
|
24
|
+
- 补齐 MIT LICENSE(保留派生署名的 heiheiha798 版权)、根包与子包发布元数据、`engines.node` 统一为 `^22.19.0 || >=24.0.0`(`node:zlib` zstd 需要 Node 22.15+/24)。
|
|
25
|
+
- 新增两个自动化流程:`.github/workflows/publish.yml`(OIDC 可信发布到 npm)与 `.github/workflows/sync-gitee.yml`(main 与标签镜像到 Gitee)。
|
|
26
|
+
- 生成 `.tgz` 分发包并随 GitHub Release 发布。
|
|
27
|
+
|
|
28
|
+
## 1.0.0 — 2026-11-13
|
|
29
|
+
|
|
30
|
+
内部初始版本:聚合根包 + 两个自写子包 + 复用 `@linxin666/dsh-session-archive@0.4.18`;30/30 测试通过并安装到本机 profile。当时删除语义为归档(已知限制),后续在 1.1.0 / 1.2.0 修复。
|
package/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 sweet560
|
|
4
|
+
|
|
5
|
+
Portions of this software are derived from:
|
|
6
|
+
- dsh-plugin-subagent-clean@0.3.0 (huajiao1998), and originally
|
|
7
|
+
- dsh-plugin-subagent-delete@0.2.3 (heiheiha798),
|
|
8
|
+
both licensed under the MIT License (Copyright (c) heiheiha798).
|
|
9
|
+
The derived files are:
|
|
10
|
+
packages/dsh-plugin-yanyan-subagent/lib/log-rewrite.js
|
|
11
|
+
packages/dsh-plugin-yanyan-subagent/lib/zstd-frames.js
|
|
12
|
+
|
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
14
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
15
|
+
in the Software without restriction, including without limitation the rights
|
|
16
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
18
|
+
furnished to do so, subject to the following conditions:
|
|
19
|
+
|
|
20
|
+
The above copyright notice and this permission notice shall be included in all
|
|
21
|
+
copies or substantial portions of the Software.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
29
|
+
SOFTWARE.
|
package/README.i18n.yaml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Bilingual-pair consistency record (docs/i18n.md): the git blob hash of each
|
|
2
|
+
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
|
+
# after editing either side, bring the other along and re-record with:
|
|
4
|
+
# node scripts/verify-docs.mjs --write <dir>
|
|
5
|
+
README.md: 3157ffd6998b1d5cdd3c2faae7e19bc3787a37b6
|
|
6
|
+
README.zh.md: 2f7ef3e8a66a4ffe8e40b21d5f4f4a879ce7f87e
|
package/README.md
CHANGED
|
@@ -1,3 +1,66 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Yanyan Toolkit
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An umbrella plugin bundle that installs three independently toggleable plugins at once. It follows the "reuse what already exists, do not reinvent the wheel" rule: session management is the existing community package, and the two custom plugins reuse verified official/community injection points wherever possible.
|
|
4
|
+
|
|
5
|
+
## Included plugins
|
|
6
|
+
|
|
7
|
+
| Plugin | Row id | Purpose |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| Plugin Update | `dsh-plugin-yanyan-update` | Adds a "Check updates" button beside the "Installed plugins" heading on the Plugins page. Clicking it opens a small dropdown listing updatable plugins (current version -> latest version), each with its own "Update" button. Covers npm registry sources and local `link:`/`file:` (git repository) sources. |
|
|
10
|
+
| Session Management | `session-archive` | Auto-archive sessions, auto-delete archived sessions (both off by default, configurable to 7/15/30/custom days), single and batch delete. **Directly reuses** `@linxin666/dsh-session-archive@0.4.18`, whose settings page and backend already cover the full requirement. |
|
|
11
|
+
| Subagent Management | `dsh-plugin-yanyan-subagent` | Adds a renameable "Agent management" tab to the session header; its popover lists all subagents of the current session, supports per-row delete and a bottom "Clean all idle" action, plus idle-time auto-cleanup (off by default, configurable in minutes). |
|
|
12
|
+
|
|
13
|
+
Each plugin has its own loader row and can be toggled independently on the Plugins page.
|
|
14
|
+
|
|
15
|
+
## Design decisions
|
|
16
|
+
|
|
17
|
+
- **Session management reused without changes**: `@linxin666/dsh-session-archive` already implements auto-archive by `updatedAt`, auto-delete by the archive ledger's `archivedAt`, single/batch physical delete, any day count from 1 to 3650, Host-side scheduling and a settings UI. Its day input accepts any integer from 1 to 3650, which already includes 7/15/30/custom.
|
|
18
|
+
- **Plugin update reuses the injection skeleton and fills the gap**: the official `@linxin666/dsh-client-ui-plugin-manager` provides the DOM seat `[data-plugin-panel] [data-plugin-group="bundles"]`, the check-updates button, the dropdown and the dual-channel RPC contract. Its checker, however, only covers npm registry sources (`source.kind === 'npm'`) and skips local `link:`/`file:` directories and GitHub sources. This plugin adds git local source version comparison and `git merge --ff-only` updates in its own Host half, and adds a per-row update button to the dropdown.
|
|
19
|
+
- **Subagent "delete" means physical deletion + log root-cause removal (irreversible)**: the old version only called `archiveSession`, which could neither delete mnemon virtual delegations (reports `session/not-found`) nor remove the append-only parent `subagentCatalog` reference, so the row stayed in the list. The host `/purge` route now reuses the existing `/api/dsh-session-archive/delete` pipeline for real sessions and falls back to cleaning session directories, projcache and catalog references, workspace accounting, archive ledger and archive markers; virtual ids go straight to the fallback. Since v1.2.0 it also borrows the in-place "retirement" idea from `dsh-plugin-subagent-clean` (MIT) to rewrite the parent log's `subagent/catalog` event into `subagent-delete/retired` (seq/time preserved, ignorable), erasing the row at its append-only source so the host cannot rebuild it. A "Clean residual rows" button (`/scrub`), a single whole-log rewrite for one-click cleanup (`/cleanup`), `ctx.sessions.refresh()` so sidebar residual rows disappear in sync, a `localStorage['yanyan-subagent.deletedIds']` hidden-list fallback and three optional model tools (`yanyan_list_subagents` / `yanyan_delete_subagent` / `yanyan_cleanup_subagents`) complete the integration.
|
|
20
|
+
- **An X at the end of each row in the official subagent list (image 3)**: the official popover rows have no row-level Slot and no `data-session-id`-style DOM anchor, so a stable injection cannot add a per-row delete there. This plugin implements per-row delete inside its own "Agent management" popover (data from `listDescendants`, with explicit ids) and contributes a "Clean all idle" button through `conversation.session.header.actions.subagent.footer`. The in-list X is only an optional, high-risk DOM-injection enhancement and is disabled by default.
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
This package is an npm bundle; its `dsh.bundle.patch` / `cordis.patch.yml` inserts four loader rows at once. Pick one of three ways:
|
|
25
|
+
|
|
26
|
+
From npm (recommended, requires Node `^22.19.0 || >=24.0.0`):
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
dsh plugin add dsh-plugin-yanyan-toolkit
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
From a local directory (for development):
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
dsh plugin add file:<this package directory>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Or choose the local directory / enter the npm package name from the Plugins page's "Add plugin" action.
|
|
39
|
+
|
|
40
|
+
## Distribution and automation
|
|
41
|
+
|
|
42
|
+
- npm: `.github/workflows/publish.yml` publishes with OIDC trusted publishing (`npm publish --provenance --access public`) on push to `main`, `v*` tags, a published Release, or manual dispatch. It runs `npm test` first and polls to skip already-published versions. A one-time trusted publisher configuration for this repo/workflow on npmjs.com is required.
|
|
43
|
+
- Gitee: `.github/workflows/sync-gitee.yml` mirrors `main` and all tags to Gitee on push to `main` or manual dispatch. It requires the repository Secret `GITEE_SSH_PRIVATE_KEY` (a passphrase-less private key registered on the Gitee account).
|
|
44
|
+
- GitHub Releases: each version ships a `dsh-plugin-yanyan-toolkit-<version>.tgz` offline tarball, installable via `npm install <tgz>` and then as a local package.
|
|
45
|
+
|
|
46
|
+
## Activation
|
|
47
|
+
|
|
48
|
+
Changes involve bundle rows and `cordis.patch.yml`, so **the user must restart the DSH service for them to take effect** (this session did not restart the running DSH service on its own). After restarting, refresh the page to see:
|
|
49
|
+
- the "Check updates" button beside the "Installed plugins" heading;
|
|
50
|
+
- the new Session Management section in Settings (provided by the reused package);
|
|
51
|
+
- the new "Agent management" tab in the session header.
|
|
52
|
+
|
|
53
|
+
## Layout
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
dsh-plugin-yanyan-toolkit/ umbrella root (no-op halves, only patch and dependencies)
|
|
57
|
+
cordis.patch.yml 4 insert rows: self + 3 plugins
|
|
58
|
+
lib/index.js, lib/client.js minimal host / client halves
|
|
59
|
+
locale/{zh,en}.json Plugins page display metadata
|
|
60
|
+
packages/
|
|
61
|
+
dsh-plugin-yanyan-update/ Plugin Update (custom)
|
|
62
|
+
dsh-plugin-yanyan-subagent/ Subagent Management (custom)
|
|
63
|
+
(Session Management has no subdirectory; it depends on the npm package directly)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The three real plugins are listed in the root package's `dependencies`; pnpm's hoisted layout lifts them and the npm package into the profile root `node_modules`, so all four loader row `name`s resolve.
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# 研研工具包
|
|
2
|
+
|
|
3
|
+
一个聚合插件包,安装后同时获得三个可独立开关的插件。遵循「有现成的就直接借鉴、不重复造轮子」:会话管理直接复用社区现成包,另外两个自写插件也尽量照抄官方与社区已验证的注入点。
|
|
4
|
+
|
|
5
|
+
## 包含插件
|
|
6
|
+
|
|
7
|
+
| 插件 | 行 id | 作用 |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| 插件更新 | `dsh-plugin-yanyan-update` | 在插件页「已安装插件」标题行右侧提供「检查更新」按钮;点击后展开下拉小面板,逐行列出可更新插件(当前版本 → 最新版本)并带独立「更新」按钮。覆盖 npm registry 源与本地 `link:`/`file:`(git 仓库)源。 |
|
|
10
|
+
| 会话管理 | `session-archive` | 自动归档会话、自动删除已归档会话(默认均关闭,可设 7/15/30/自定义天数)、单条与批量删除。**直接复用** `@linxin666/dsh-session-archive@0.4.18`,其设置页与后端已完整覆盖需求。 |
|
|
11
|
+
| 子智能体管理 | `dsh-plugin-yanyan-subagent` | 会话顶部新增可改名的「智能体管理」标签,浮层列出当前会话所有子智能体、支持行尾逐个删除与底部「清理全部空闲」;提供按空闲时间自动清理(默认关闭、可设分钟数)。 |
|
|
12
|
+
|
|
13
|
+
三个插件各有独立 loader 行,可在插件页分别启停。
|
|
14
|
+
|
|
15
|
+
## 设计取舍
|
|
16
|
+
|
|
17
|
+
- **会话管理零改动复用**:`@linxin666/dsh-session-archive` 已实现按 `updatedAt` 自动归档、按归档台账 `archivedAt` 自动删除、单条/批量物理删除、1~3650 任意天数、Host 端定时调度与设置页 UI。其天数输入框接受任意 1~3650 的整数,已包含 7/15/30/自定义,无需改造。
|
|
18
|
+
- **插件更新复用注入骨架,补齐现成轮子缺失的部分**:官方 `@linxin666/dsh-client-ui-plugin-manager` 的 DOM 座位 `[data-plugin-panel] [data-plugin-group="bundles"]`、「检查更新」按钮、下拉面板与双通道 RPC 契约可直接借鉴;但它的检查逻辑只覆盖 npm registry 源(`source.kind === 'npm'`),对本地 `link:`/`file:` 目录与 GitHub 源一律跳过。本插件在自写 Host 半区里补上 git 本地源的版本比对与 `git merge --ff-only` 更新,并给下拉列表每一行加独立更新按钮。
|
|
19
|
+
- **子智能体「删除」= 物理删除 + 日志根除(不可逆)**:旧版只调用 `archiveSession` 归档,既删不掉 mnemon 虚拟委派(会报 `session/not-found`),也移除不了父会话 `subagentCatalog` 的 append-only 引用,导致列表里仍显示该条目。现改为 host `/purge` 物理删除:真实会话优先复用现成 `/api/dsh-session-archive/delete` 管线,再兜底清会话目录、projcache 与 catalog 引用、workspace 记账、归档台账与归档标记;虚拟 id 直接走兜底。v1.2.0 起进一步借鉴 `dsh-plugin-subagent-clean`(MIT)的「原位退休」把父会话日志 `session.v4.jsonl.zstd` 里的 `subagent/catalog` 事件改写为 `subagent-delete/retired`(保留 seq/time、ignorable),从 append-only 源头根除目录行,宿主不再重建;配合「清理残留行」按钮(`/scrub`)、一键清理只改写一次日志(`/cleanup`)、`ctx.sessions.refresh()` 让侧边栏残留行同步消失,以及 `localStorage['yanyan-subagent.deletedIds']` 隐藏清单兜底。另注册三个可选模型工具(`yanyan_list_subagents` / `yanyan_delete_subagent` / `yanyan_cleanup_subagents`),让 Agent 一句话即可清理空闲 Mnemon review 子代理。
|
|
20
|
+
- **图 3 官方子智能体列表行尾加 X**:官方浮层行没有行级 Slot,也没有 `data-session-id` 之类的 DOM 锚点,无法通过稳定注入实现行尾删除。本插件把「逐个删除」做在自建的「智能体管理」标签浮层里(数据来自 `listDescendants`,id 明确),并在 `conversation.session.header.actions.subagent.footer` 提供「清理全部空闲」按钮。图 3 行内 X 仅作为可选的高风险 DOM 注入增强,默认不启用。
|
|
21
|
+
|
|
22
|
+
## 安装
|
|
23
|
+
|
|
24
|
+
本包是一个 npm 包,通过 `dsh.bundle.patch` 的 `cordis.patch.yml` 一次插入四条 loader 行。三种方式任选其一:
|
|
25
|
+
|
|
26
|
+
从 npm 安装(推荐,需 Node `^22.19.0 || >=24.0.0`):
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
dsh plugin add dsh-plugin-yanyan-toolkit
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
本地目录安装(开发调试):
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
dsh plugin add file:<本包目录>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
或在插件页「添加插件」里选择本地目录 / 输入 npm 包名安装。
|
|
39
|
+
|
|
40
|
+
## 分发与自动化
|
|
41
|
+
|
|
42
|
+
- npm:`.github/workflows/publish.yml` 在 push `main`、`v*` 标签、Release 发布或手动触发时用 OIDC 可信发布执行 `npm publish --provenance --access public`;发布前先跑 `npm test`,并轮询跳过已发布版本。首次使用需在 npmjs.com 为本仓库与该工作流配置 trusted publisher。
|
|
43
|
+
- Gitee:`.github/workflows/sync-gitee.yml` 在 push `main` 或手动触发时把 main 与全部标签镜像到 Gitee;需要在仓库 Secret 配置 `GITEE_SSH_PRIVATE_KEY`(一把已注册在 Gitee 账号下的无口令私钥)。
|
|
44
|
+
- GitHub Release:每个版本附带 `dsh-plugin-yanyan-toolkit-<version>.tgz` 离线分发包,可直接 `npm install <tgz>` 后按本地包方式安装。
|
|
45
|
+
|
|
46
|
+
## 生效说明
|
|
47
|
+
|
|
48
|
+
改动涉及 bundle 行与 `cordis.patch.yml`,**需要用户重启 DSH 服务后生效**(本会话未自行重启正在运行的 DSH 服务)。重启后刷新页面即可看到:
|
|
49
|
+
- 插件页「已安装插件」标题行右侧的「检查更新」按钮;
|
|
50
|
+
- 设置页新增的「会话管理」分区(由复用的包提供);
|
|
51
|
+
- 会话顶部新增的「智能体管理」标签。
|
|
52
|
+
|
|
53
|
+
## 目录结构
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
dsh-plugin-yanyan-toolkit/ 聚合根包(自身行为空壳,只承载 patch 与依赖)
|
|
57
|
+
cordis.patch.yml 4 行 insert:自身 + 3 个插件
|
|
58
|
+
lib/index.js, lib/client.js 极简 host / client 半区
|
|
59
|
+
locale/{zh,en}.json 插件页展示元数据
|
|
60
|
+
packages/
|
|
61
|
+
dsh-plugin-yanyan-update/ 插件更新(自写)
|
|
62
|
+
dsh-plugin-yanyan-subagent/ 子智能体管理(自写)
|
|
63
|
+
(会话管理无子目录,直接依赖 npm 包 @linxin666/dsh-session-archive)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
三个真实插件列在根包 `dependencies` 里,pnpm 的 hoisted 布局会把它们与 npm 包一起提升到 profile 根 `node_modules`,四条 loader 行的 `name` 均可解析。
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# 研研工具包:一个 bundle 挂载四条 loader 行 —— 自身元数据行 + 三个功能插件行。
|
|
2
|
+
# 每行 name 必须互不相同且恰好等于对应包的 package.json name,否则
|
|
3
|
+
# dsh-client-modules 会报 duplicate factory registration。
|
|
4
|
+
# 三个功能插件各有独立 id,安装后可由用户在插件页单独启停。
|
|
5
|
+
- insert:
|
|
6
|
+
# 自身行:只承载健康检查路由与空 client 壳,不贡献任何 UI。
|
|
7
|
+
- id: yanyan-toolkit-compat
|
|
8
|
+
name: 'dsh-plugin-yanyan-toolkit'
|
|
9
|
+
|
|
10
|
+
# 插件一「插件更新」:检查本地插件与线上插件版本,逐个更新。
|
|
11
|
+
- id: dsh-plugin-yanyan-update
|
|
12
|
+
name: 'dsh-plugin-yanyan-update'
|
|
13
|
+
config:
|
|
14
|
+
checkOnOpen: false
|
|
15
|
+
includeLocal: true
|
|
16
|
+
includeRegistry: true
|
|
17
|
+
|
|
18
|
+
# 插件二「会话管理」:直接复用现成 npm 包 @linxin666/dsh-session-archive@0.4.18,
|
|
19
|
+
# 其自动归档 / 自动删除 / 单条与批量删除已覆盖全部需求,零重复造轮子。
|
|
20
|
+
# name 必须恰好等于真实包名,才能同时挂载它的 host 与 browser 半区。
|
|
21
|
+
# id 沿用该包自身 patch 的 session-archive:即使它被单独安装,
|
|
22
|
+
# 同 id 行也只会合并成一条;下面的 name 级防重守卫再兜一层底,
|
|
23
|
+
# 避免同 name 不同 id 的两条行重复挂载(loader 会报 duplicate prefix route)。
|
|
24
|
+
- id: session-archive
|
|
25
|
+
name: '@linxin666/dsh-session-archive'
|
|
26
|
+
disabled: !!js "[...ctx.loader.entries()].some((e) => e.options.name === '@linxin666/dsh-session-archive' && e.options.id !== 'session-archive' && !e.disabled)"
|
|
27
|
+
|
|
28
|
+
# 插件三「子智能体管理」:会话顶部标签 + 行尾删除 + 清理全部空闲 + 自动清理。
|
|
29
|
+
- id: dsh-plugin-yanyan-subagent
|
|
30
|
+
name: 'dsh-plugin-yanyan-subagent'
|
|
31
|
+
config:
|
|
32
|
+
tabLabel: '智能体管理'
|
|
33
|
+
autoCleanEnabled: false
|
|
34
|
+
idleMinutes: 30
|
|
35
|
+
checkIntervalMin: 1
|
package/icon.svg
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64" role="img" aria-label="研研工具包">
|
|
2
|
+
<rect x="4" y="4" width="56" height="56" rx="14" fill="#eef2ff"/>
|
|
3
|
+
<rect x="4" y="4" width="56" height="56" rx="14" fill="none" stroke="#c7d2fe" stroke-width="2"/>
|
|
4
|
+
<path d="M22 30h20M22 38h20M22 46h12" stroke="#4f46e5" stroke-width="3.4" stroke-linecap="round"/>
|
|
5
|
+
<circle cx="22" cy="21" r="4.2" fill="#4f46e5"/>
|
|
6
|
+
<circle cx="32" cy="21" r="4.2" fill="#6366f1"/>
|
|
7
|
+
<circle cx="42" cy="21" r="4.2" fill="#818cf8"/>
|
|
8
|
+
</svg>
|
package/lib/client.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// dsh-plugin-yanyan-toolkit — Client (browser) half.
|
|
2
|
+
//
|
|
3
|
+
// 根包本身不贡献任何 UI:三个功能插件各有独立 loader 行并各自注册 UI。
|
|
4
|
+
// 这里仅完成 __ModuleLoader__ 登记,满足 dsh-client-modules 的装载校验
|
|
5
|
+
// (每个进入 web boot graph 的包必须注册同 id 的 factory)。
|
|
6
|
+
|
|
7
|
+
(function (root) {
|
|
8
|
+
if (!root) return;
|
|
9
|
+
if (!root.__ModuleLoader__ || typeof root.__ModuleLoader__.load !== 'function') return;
|
|
10
|
+
|
|
11
|
+
root.__ModuleLoader__.load({
|
|
12
|
+
id: 'dsh-plugin-yanyan-toolkit',
|
|
13
|
+
factory: () => {
|
|
14
|
+
var module = { exports: {} };
|
|
15
|
+
var exports = module.exports;
|
|
16
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
17
|
+
|
|
18
|
+
var inject = [];
|
|
19
|
+
var apply = function () {};
|
|
20
|
+
|
|
21
|
+
exports.inject = inject;
|
|
22
|
+
exports.apply = apply;
|
|
23
|
+
return module.exports;
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
})(typeof window !== 'undefined' ? window : typeof self !== 'undefined' ? self : null);
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// dsh-plugin-yanyan-toolkit — Host (Node) half.
|
|
2
|
+
//
|
|
3
|
+
// 聚合包的自身 loader 行只用于承载四项 patch 元数据与依赖声明,
|
|
4
|
+
// 三个功能插件各自有独立的 loader 行,由 DSH loader 直接挂载。
|
|
5
|
+
// 因此这里不重复官方全家桶的 dynamic shell(await import + ctx.plugin)
|
|
6
|
+
// 机制,只贡献一条同源回环健康检查路由,便于确认本包已被装载。
|
|
7
|
+
|
|
8
|
+
export const name = 'dsh-plugin-yanyan-toolkit';
|
|
9
|
+
export const VERSION = '1.0.0';
|
|
10
|
+
export const inject = ['webServer', 'connection'];
|
|
11
|
+
|
|
12
|
+
export const HEALTH_ROUTE = '/__dsh-plugin-yanyan-toolkit/health';
|
|
13
|
+
|
|
14
|
+
function respond(res, status, body) {
|
|
15
|
+
const payload = JSON.stringify(body);
|
|
16
|
+
res.writeHead(status, {
|
|
17
|
+
'content-type': 'application/json; charset=utf-8',
|
|
18
|
+
'cache-control': 'no-store',
|
|
19
|
+
'content-length': Buffer.byteLength(payload),
|
|
20
|
+
});
|
|
21
|
+
res.end(payload);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function healthHandler(ctx) {
|
|
25
|
+
return (req, res) => {
|
|
26
|
+
const fence = () => ctx.connection.requestRejection(req) === undefined;
|
|
27
|
+
if (!fence()) {
|
|
28
|
+
respond(res, 403, { ok: false, error: 'forbidden', message: '请求未通过 DSH 准入裁决。' });
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (req.method !== 'GET' && req.method !== 'POST') {
|
|
32
|
+
respond(res, 405, { ok: false, error: 'method-not-allowed', message: '只接受 GET 或 POST 请求。' });
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
respond(res, 200, { ok: true, name, version: VERSION });
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function apply(ctx) {
|
|
40
|
+
ctx.effect(
|
|
41
|
+
() => ctx.webServer.register({ kind: 'exact', path: HEALTH_ROUTE, handler: healthHandler(ctx) }),
|
|
42
|
+
'dsh-plugin-yanyan-toolkit: health route',
|
|
43
|
+
);
|
|
44
|
+
}
|
package/locale/en.json
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"title": "Yanyan Toolkit",
|
|
4
|
+
"description": "One bundle with three independently toggleable plugins: Plugin Update (checks local and registry plugin versions, updates them one by one), Session Management (auto-archive / auto-delete, single and batch delete, reusing the existing dsh-session-archive package), and Subagent Management (session header tab, per-row delete, clean all idle, and idle-time auto-cleanup). Install this one package to get all three."
|
|
5
|
+
}
|
|
6
|
+
}
|
package/locale/zh.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,81 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-yanyan-toolkit",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "研研工具包:聚合「插件更新」「会话管理」「子智能体管理」三个可独立开关的插件。",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"icon": "icon.svg",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./lib/index.js",
|
|
10
|
+
"./client": "./lib/client.js",
|
|
11
|
+
"./package.json": "./package.json",
|
|
12
|
+
"./locale/*.json": "./locale/*.json",
|
|
13
|
+
"./icon": "./icon.svg"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"lib/index.js",
|
|
17
|
+
"lib/client.js",
|
|
18
|
+
"cordis.patch.yml",
|
|
19
|
+
"icon.svg",
|
|
20
|
+
"locale/en.json",
|
|
21
|
+
"locale/zh.json",
|
|
22
|
+
"README.md",
|
|
23
|
+
"README.zh.md",
|
|
24
|
+
"README.i18n.yaml",
|
|
25
|
+
"CHANGELOG.md",
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"scripts/cleanup-ghost-subagents.mjs",
|
|
28
|
+
"packages",
|
|
29
|
+
"!packages/*/test"
|
|
30
|
+
],
|
|
31
|
+
"keywords": [
|
|
32
|
+
"deepseek-harness",
|
|
33
|
+
"dsh-plugin",
|
|
34
|
+
"dsh",
|
|
35
|
+
"plugin-manager",
|
|
36
|
+
"session-archive",
|
|
37
|
+
"subagent",
|
|
38
|
+
"toolkit",
|
|
39
|
+
"cordis",
|
|
40
|
+
"npm-package",
|
|
41
|
+
"gitee-mirror"
|
|
42
|
+
],
|
|
43
|
+
"author": "sweet560",
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "git+https://github.com/sweet560/dsh-plugin-yanyan-toolkit.git"
|
|
51
|
+
},
|
|
52
|
+
"bugs": {
|
|
53
|
+
"url": "https://github.com/sweet560/dsh-plugin-yanyan-toolkit/issues"
|
|
54
|
+
},
|
|
55
|
+
"homepage": "https://github.com/sweet560/dsh-plugin-yanyan-toolkit/blob/main#readme",
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": "^22.19.0 || >=24.0.0"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"test": "node --test test/*.test.mjs",
|
|
61
|
+
"pack": "npm pack --dry-run"
|
|
62
|
+
},
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"dsh-plugin-yanyan-update": "file:./packages/dsh-plugin-yanyan-update",
|
|
65
|
+
"dsh-plugin-yanyan-subagent": "file:./packages/dsh-plugin-yanyan-subagent",
|
|
66
|
+
"@linxin666/dsh-session-archive": "^0.4.18"
|
|
67
|
+
},
|
|
68
|
+
"dsh": {
|
|
69
|
+
"engines": {
|
|
70
|
+
"dsh": ">=0.2.2-rc.2"
|
|
71
|
+
},
|
|
72
|
+
"bundle": {
|
|
73
|
+
"patch": "./cordis.patch.yml"
|
|
74
|
+
},
|
|
75
|
+
"client": {
|
|
76
|
+
"inject": [],
|
|
77
|
+
"platform": "web"
|
|
78
|
+
},
|
|
79
|
+
"manifestVersion": 1
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Subagent Management (dsh-plugin-yanyan-subagent)
|
|
2
|
+
|
|
3
|
+
The third plugin in the "Yanyan Toolkit" aggregate bundle. It lists and manages all subagents of the current session.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Adds a tab to the conversation header actions (default label "Agent Manager", renameable from the overlay; the name is persisted in browser local storage).
|
|
8
|
+
- Clicking the tab opens an overlay. The overlay is mounted through `ReactDOM.createPortal` into `document.body` so the header's overflow clipping cannot cut it off; when `react-dom` is unavailable it falls back to appending a plain DOM overlay to `document.body`.
|
|
9
|
+
- Lists every subagent of the current session with name, status (running / idle), mode and an estimated idle duration.
|
|
10
|
+
- A trailing delete button (U+2715 MULTIPLICATION X, a plain Unicode math symbol, not an emoji) deletes one subagent after a confirmation. Running subagents are stopped before deletion. The root session (lead) is never deleted.
|
|
11
|
+
- The bottom "Clean All Idle" button batch-deletes idle subagents over the idle threshold and reports the count.
|
|
12
|
+
- The "Settings" section enables "Auto-clean idle subagents" (off by default), with "Idle over (minutes)" and "Check interval (minutes)". When enabled, each check only deletes idle subagents that reached the threshold; a failed list read is fail-closed (nothing is deleted).
|
|
13
|
+
|
|
14
|
+
## Delete semantics (physical deletion, irreversible)
|
|
15
|
+
|
|
16
|
+
Deleting **really removes the session data** and erases the catalog row at its source; it no longer archives. Layers:
|
|
17
|
+
|
|
18
|
+
1. Real sessions reuse the existing `@linxin666/dsh-session-archive` physical-delete pipeline (`POST /api/dsh-session-archive/delete`), which cleans the sqlite rdb, session directory, projection cache and archive ledger.
|
|
19
|
+
2. The host `/purge` route then cleans up again: the session directory (both `session-`-prefixed and bare spellings, with realpath checks), projcache files and parent `subagentCatalog` cache references, `workspace.json` `global.archivedSessionIds` and `tables.workspaces[*].sessionIds` accounting, and `archive-ledger.json`; it also removes the archive marker via `workspaceRegistry.unarchiveSession`.
|
|
20
|
+
3. mnemon virtual delegations (ids like `mnemon-run-v1-<uuid>`, not real `session-<uuid>` sessions) go straight to layer 2 — calling `archiveSession` on such an id reports `session/not-found`.
|
|
21
|
+
4. **Catalog root-cause fix (v1.2.0)**: `subagentCatalog` is an **append-only projection** of `subagent/catalog` events in the parent session log (`session.v4.jsonl.zstd`); the official `@deepseek-ai/dsh-chunked-list` has no delete API, so clearing caches or archiving can never remove the source reference and the host rebuilds the row on the next `refreshProjections`. Borrowing the in-place "retirement" idea from `huajiao1998/dsh-plugin-subagent-clean@0.3.0` (MIT, originally `heiheiha798/dsh-plugin-subagent-delete@0.2.3`), `/purge` rewrites the matching `subagent/catalog` event **in place** into `{"type":"subagent-delete/retired","seq":<same>,"time":<same>,"ignorable":true,"data":{"childId","retiredAt"}}`. The `seq`/`time` are preserved (log seq continuity and compaction references stay valid), only `type === "subagent/catalog"` produces directory rows, so the projection never recreates the row. Log decoding/encoding uses `node:zlib` zstd (Node >= 22.15 / 24), with two `stat` checks around an atomic rename for concurrency safety; a failed rewrite is queued to `yanyan-subagent-pending.json` and replayed on next host start.
|
|
22
|
+
5. **Fallback hidden list**: the client still keeps a persistent hidden list in `localStorage['yanyan-subagent.deletedIds']` (core ids with the `session-` prefix stripped). It filters during rendering, batch cleaning, auto-cleaning and the official list row-tail action, so even a concurrent-write / decode failure cannot make a deleted row reappear.
|
|
23
|
+
|
|
24
|
+
**This operation is irreversible**: a confirmation is required; running subagents are stopped first. The root session (lead) is never deleted.
|
|
25
|
+
|
|
26
|
+
### Residual rows, one-click cleanup and sidebar sync
|
|
27
|
+
|
|
28
|
+
- A **"Clean residual rows"** button in the overlay calls the host `/scrub` route, which retires catalog rows whose sub-session directory is already gone (leftovers from older archive / rebuild behaviour, figures 2 and 3). After every delete, batch clean, auto-clean and scrub, the client calls `ctx.sessions.refresh()` in addition to `refreshProjections(rootId)` so the **sidebar residual row disappears in sync**.
|
|
29
|
+
- The host `/cleanup` route (and the model tool) batch-deletes with a **single** whole-library log rewrite (`retireCatalogRowsEverywhere`) instead of one rewrite per row.
|
|
30
|
+
- Host `/catalog` lists the still-live `subagent/catalog` events straight from the log (the reliable source of truth, unlike the rebuildable projcache cache).
|
|
31
|
+
|
|
32
|
+
### Model tools (optional `tools` injection)
|
|
33
|
+
|
|
34
|
+
Three plain-object tools are registered when the profile provides the `tools` service, so an Agent can do the cleanup in one sentence (the `mnemon_forget` idea from the request targets Mnemon memory insights, not subagent sessions, so its "let the Agent trigger cleanup" intent is served here instead):
|
|
35
|
+
|
|
36
|
+
- `yanyan_list_subagents` — list live catalog rows (filter by label).
|
|
37
|
+
- `yanyan_delete_subagent` — permanently delete one subagent (required `subagent_id`).
|
|
38
|
+
- `yanyan_cleanup_subagents` — batch delete (label filter / idle-only / dry-run / limit), matching "list all idle `Mnemon idle checkpoint review` subagents and delete them all".
|
|
39
|
+
|
|
40
|
+
## Data sources and fallbacks
|
|
41
|
+
|
|
42
|
+
- Primary source is the browser session projection: `ctx.sessions.list.getSnapshot().projectionsBySession[rootId].values.subagentCatalog`.
|
|
43
|
+
- The root session id is derived from `retainedBy.mainView > 0`, falling back to the first session in `projectionsBySession` that carries `subagentCatalog`.
|
|
44
|
+
- If the local projection is unavailable, the client falls back to the host route `/__dsh-plugin-yanyan-subagent/list` (which depends on `subagents.listDescendants` and returns `service-unavailable` when that service is missing).
|
|
45
|
+
- The official projection has no idle-origin field (`lastActiveAt` / `idleSince` have zero hits for subagent UI). Idle duration is estimated in memory from the first time an `inactive` row is observed, and resets on page reload.
|
|
46
|
+
|
|
47
|
+
## Limitation of the row-tail delete button in the official subagent list (figure 3, optional enhancement)
|
|
48
|
+
|
|
49
|
+
The plugin makes a best-effort attempt with a `MutationObserver`: when the official `[data-subagent-panel]` overlay appears, it appends a small delete button to every `[role="treeitem"]` row. There is a confirmed hard limitation:
|
|
50
|
+
|
|
51
|
+
- Official list rows have **no row-level Slot and no stable identifier such as `data-id`**; the row `aria-label` is only `[primaryLabel, secondary, metrics].join(' ')`, which cannot be reliably mapped back to a `sessionId`.
|
|
52
|
+
- Clicking that button therefore only does fuzzy matching of row text against the local `subagentCatalog` (name containment plus order alignment). **Deletion runs only for a unique match**; otherwise the user is told to use the Agent Manager tab, where a definite `sessionId` is available.
|
|
53
|
+
- This DOM injection is a fragile enhancement: it can break whenever the official DOM changes, and matching can misfire. Prefer the row-tail delete button inside this plugin's own "Agent Manager" tab.
|
|
54
|
+
|
|
55
|
+
## Configuration
|
|
56
|
+
|
|
57
|
+
`cordis.patch.yml` carries the defaults (the profile override replaces the whole config for the same id without a deep merge, so the host `normalizeConfig()` provides a fallback for every missing key):
|
|
58
|
+
|
|
59
|
+
| Key | Default | Meaning |
|
|
60
|
+
| --- | --- | --- |
|
|
61
|
+
| `tabLabel` | Agent Manager | Header tab label, renameable from the overlay |
|
|
62
|
+
| `autoCleanEnabled` | false | Auto-clean switch, off by default |
|
|
63
|
+
| `idleMinutes` | 30 | Idle threshold in minutes (1..10080) |
|
|
64
|
+
| `checkIntervalMin` | 1 | Auto-clean check interval in minutes (1..1440) |
|
|
65
|
+
|
|
66
|
+
## Tests
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
node --test test/*.test.mjs
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Pure unit tests cover `normalizeConfig`, `parseCatalog`, `idleFilter` and deletion id validation; no DSH service is contacted.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# 子智能体管理(dsh-plugin-yanyan-subagent)
|
|
2
|
+
|
|
3
|
+
「研研工具包」聚合插件包中的第三个插件,用于查看与管理当前会话的所有子智能体。
|
|
4
|
+
|
|
5
|
+
## 功能
|
|
6
|
+
|
|
7
|
+
- 在会话顶部操作区新增一个标签(默认「智能体管理」,可在浮层的「重命名标签」处改名,名称保存在浏览器本地存储)。
|
|
8
|
+
- 点击标签打开浮层(浮层通过 `ReactDOM.createPortal` 挂到 `document.body`,避免被顶部操作区的溢出裁剪裁掉;`react-dom` 不可用时降级为把浮层直接追加到 `document.body` 并用原生 DOM 渲染)。
|
|
9
|
+
- 列出当前会话的所有子智能体,显示名称、状态(运行中 / 空闲)、模式以及估算的空闲时长。
|
|
10
|
+
- 行尾的删除按钮(U+2715 乘号,普通 Unicode 数学符号,非 emoji)可逐个删除,删除前二次确认;正在运行的子智能体删除时会先停止。根会话(lead)本身永不删除。
|
|
11
|
+
- 底部「清理全部空闲」按空闲阈值批量删除空闲子智能体,并显示清理数量。
|
|
12
|
+
- 「设置」区可开启「自动清理空闲子智能体」(默认关闭),设置「空闲超过(分钟)」与「巡检间隔(分钟)」。开启后按间隔巡检,只清理达到阈值且处于空闲的子智能体;读取列表数据失败时保守不删(fail-closed)。
|
|
13
|
+
|
|
14
|
+
## 删除语义(物理删除,不可逆)
|
|
15
|
+
|
|
16
|
+
删除会**真正移除会话数据并从源头根除目录行**,不再是「归档」。做法分层:
|
|
17
|
+
|
|
18
|
+
1. 真实会话优先复用现成包 `@linxin666/dsh-session-archive` 的 `POST /api/dsh-session-archive/delete` 物理删除管线(会清理 sqlite rdb、会话目录、投影缓存与归档台账)。
|
|
19
|
+
2. host 侧 `/purge` 路由再兜底清理一遍:会话目录(含 `session-` 前缀两种拼写、realpath 校验不越界)、projcache 文件与父会话 `subagentCatalog` 缓存引用、`workspace.json` 的 `global.archivedSessionIds` 与 `tables.workspaces[*].sessionIds` 记账、`archive-ledger.json` 台账;并用 `workspaceRegistry.unarchiveSession` 摘掉归档标记。
|
|
20
|
+
3. 对 mnemon 虚拟委派(id 形如 `mnemon-run-v1-<uuid>`,不是真实 `session-<uuid>` 会话)直接走第 2 层——这类 id 调用 `archiveSession` 会报 `session/not-found`。
|
|
21
|
+
4. **catalog 根治(v1.2.0)**:`subagentCatalog` 是父会话日志 `session.v4.jsonl.zstd` 里 `subagent/catalog` 事件的 **append-only 投影**(官方 `@deepseek-ai/dsh-chunked-list` 只追加、无删除 API)。因此清缓存、归档、删目录都移除不了底层日志引用,宿主下次 `refreshProjections` 会把条目重建回来。借鉴 `huajiao1998/dsh-plugin-subagent-clean@0.3.0` 的「原位退休」思路(MIT,原始实现 `heiheiha798/dsh-plugin-subagent-delete@0.2.3`),`/purge` 把命中的 `subagent/catalog` 事件**原位改写**为 `{"type":"subagent-delete/retired","seq":<原值>,"time":<原值>,"ignorable":true,"data":{"childId","retiredAt"}}`:`seq`/`time` 原样保留(日志 seq 连续性与压缩事件引用不受影响),而目录投影只在 `type === "subagent/catalog"` 时生成行,于是投影再也不会重建该行。日志解码/编码用 `node:zlib` 的 zstd(Node >= 22.15 / 24),原子 rename 前后各做一次 stat 并发校验;改写失败会排队到 `yanyan-subagent-pending.json`,宿主下次启动补做。
|
|
22
|
+
5. **隐藏清单兜底**:client 侧仍保留持久化隐藏清单 `localStorage['yanyan-subagent.deletedIds']`(存去 `session-` 前缀后的核心 id),在渲染、批量清理、自动清理与官方列表行尾按钮里持续过滤。即便日志改写遇到并发写入/解码失败,已删条目也不会重新出现。
|
|
23
|
+
|
|
24
|
+
**该操作不可逆**,删除前会二次确认;正在运行的子智能体删除时先停止。根会话(lead)本身永不删除。
|
|
25
|
+
|
|
26
|
+
### 残留行、一键清理与侧边栏同步
|
|
27
|
+
|
|
28
|
+
- 浮层底部新增 **「清理残留行」** 按钮,调用 host `/scrub` 路由,把「子会话目录已不在、catalog 行还在」的引用从父会话日志原位退休(专治旧版归档 / 重建遗留的幽灵行,对应图2 / 图3)。每次单删、批量清理、自动清理与残留清理之后,client 除 `refreshProjections(rootId)` 外还会调用 `ctx.sessions.refresh()`,让**侧边栏残留行同步消失**。
|
|
29
|
+
- host `/cleanup` 路由(与模型工具)批量删除时对齐上游做法:所有命中 id **只全库改写一次日志**(`retireCatalogRowsEverywhere`),不是逐条改写。
|
|
30
|
+
- host `/catalog` 直接从日志真相源列出仍会生成目录行的 `subagent/catalog` 事件(比会被重建的 projcache 缓存可靠)。
|
|
31
|
+
|
|
32
|
+
### 模型工具(可选 `tools` 注入)
|
|
33
|
+
|
|
34
|
+
profile 提供 `tools` 服务时注册三个纯对象工具,让 Agent 一句话就能完成清理(用户建议里的 `mnemon_forget` 删的是 Mnemon 记忆洞察 insight、不是子智能体会话 / catalog 行,按字面调用不对症;其「让 Agent 也能触发清理」的真实意图由此承接):
|
|
35
|
+
|
|
36
|
+
- `yanyan_list_subagents` —— 列出仍存活的目录行(可按标签过滤)。
|
|
37
|
+
- `yanyan_delete_subagent` —— 彻底删除单个子智能体(必填 `subagent_id`)。
|
|
38
|
+
- `yanyan_cleanup_subagents` —— 批量删除(标签过滤 / 仅空闲 / dry-run / 数量上限),对应「列出当前会话里所有空闲的 Mnemonic idle checkpoint review 子代理并全部删掉」。
|
|
39
|
+
|
|
40
|
+
## 数据来源与降级
|
|
41
|
+
|
|
42
|
+
- 主数据源为浏览器侧会话投影:`ctx.sessions.list.getSnapshot().projectionsBySession[rootId].values.subagentCatalog`。
|
|
43
|
+
- 根会话 id 由 `retainedBy.mainView > 0` 推导,找不到时回退到遍历 `projectionsBySession` 中带 `subagentCatalog` 的会话。
|
|
44
|
+
- 本地投影读不到时,回退到 host 侧 `/__dsh-plugin-yanyan-subagent/list`(依赖宿主的 `subagents.listDescendants`,服务缺失时返回 `service-unavailable`)。
|
|
45
|
+
- 官方投影没有空闲起点字段(`lastActiveAt` / `idleSince` 均无命中),空闲时长由本插件在内存中按「首次观察到 inactive 的时刻」估算,刷新页面后会重置。
|
|
46
|
+
|
|
47
|
+
## 官方子智能体列表浮层行尾删除按钮的限制(图3,可选增强)
|
|
48
|
+
|
|
49
|
+
本插件尽力用 `MutationObserver` 在官方子智能体列表浮层 `[data-subagent-panel]` 出现时,给每个 `[role="treeitem"]` 行尾追加一个小删除按钮。但存在已确认的硬限制:
|
|
50
|
+
|
|
51
|
+
- 官方列表行**没有行级 Slot,也没有 `data-id` 等稳定标识**;行的 `aria-label` 只是 `[primaryLabel, secondary, metrics].join(' ')` 的文本拼接,无法可靠反推出 `sessionId`。
|
|
52
|
+
- 因此点击该按钮时只能拿行文本到本地 `subagentCatalog` 做模糊匹配(名称包含匹配 + 顺序对应)。**只有唯一命中时才会执行删除**;无法唯一定位时会提示「无法定位该子智能体,请在智能体管理标签中删除」。
|
|
53
|
+
- 该 DOM 注入是「脆弱增强」:官方 DOM 结构一旦变化即可能失效,且匹配可能误判。它不保证成功,请优先使用本插件自己的「智能体管理」标签里的行尾删除按钮(那里有确定的 `sessionId`)。
|
|
54
|
+
|
|
55
|
+
## 配置
|
|
56
|
+
|
|
57
|
+
`cordis.patch.yml` 提供默认值(profile 覆盖层对同名 id 是整份替换 config、不做深合并,host 的 `normalizeConfig()` 对每个缺省 key 都做了兜底):
|
|
58
|
+
|
|
59
|
+
| 键 | 默认值 | 含义 |
|
|
60
|
+
| --- | --- | --- |
|
|
61
|
+
| `tabLabel` | 智能体管理 | 顶部标签文字,可在浮层内改名 |
|
|
62
|
+
| `autoCleanEnabled` | false | 自动清理开关,默认关闭 |
|
|
63
|
+
| `idleMinutes` | 30 | 空闲阈值分钟数(1..10080) |
|
|
64
|
+
| `checkIntervalMin` | 1 | 自动清理巡检间隔分钟数(1..1440) |
|
|
65
|
+
|
|
66
|
+
## 测试
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
node --test test/*.test.mjs
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
纯单元测试覆盖 `normalizeConfig`、`parseCatalog`、`idleFilter` 与删除 id 安全校验,不连接任何 DSH 服务。
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" role="img" aria-label="Subagent Management">
|
|
2
|
+
<rect x="4" y="4" width="40" height="40" rx="9" fill="#2f6feb"/>
|
|
3
|
+
<rect x="18" y="9" width="12" height="9" rx="2.5" fill="#ffffff"/>
|
|
4
|
+
<rect x="7" y="30" width="10" height="9" rx="2" fill="#ffffff" opacity="0.9"/>
|
|
5
|
+
<rect x="19" y="30" width="10" height="9" rx="2" fill="#ffffff" opacity="0.9"/>
|
|
6
|
+
<rect x="31" y="30" width="10" height="9" rx="2" fill="#ffffff" opacity="0.9"/>
|
|
7
|
+
<path d="M24 18 L24 24 M24 24 L12 30 M24 24 L24 30 M24 24 L36 30" stroke="#ffffff" stroke-width="1.8" fill="none" stroke-linecap="round"/>
|
|
8
|
+
</svg>
|