dsh-subagent-workspace-ui 1.0.1
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/LICENSE +21 -0
- package/README.md +54 -0
- package/README.zh.md +202 -0
- package/cordis.patch.yml +10 -0
- package/lib/client.js +35 -0
- package/lib/index.js +4 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 miuzel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# DSH Subagent Workspace UI
|
|
2
|
+
|
|
3
|
+
A Web client plugin that adds a **子代理管理** button to the conversation-header action row. It opens a searchable panel for the subagents currently discovered by the DSH client runtime.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Compact title-bar trigger shows the active-child count and animated activity dot without opening the panel.
|
|
8
|
+
- Defaults to the main session's current workspace and current session, even when the user is viewing a child session.
|
|
9
|
+
- Workspace and session selectors support current workspace, all workspaces, named workspaces, current session, and named sessions.
|
|
10
|
+
- Sort by recent activity, name, or type; recently running children remain near the top after they finish.
|
|
11
|
+
- Group by session, workspace, category, type, or no grouping. Session headers show the workspace and parent session name.
|
|
12
|
+
- Browser-local classification tabs support custom regular expressions. Built-ins include all, other, review, test, implementation, and planning.
|
|
13
|
+
- One-shot children carry a compact `⚡ 一次性` badge; continuable children remain visually uncluttered.
|
|
14
|
+
- Archive state is local and never deletes a DSH session. Single-row archive actions and a batch mode support shift-selection, select-all, time-based selection (up to 1,000 rows), batch archive, restore, and archive-all.
|
|
15
|
+
- Load catalogs in pages of 40 with an independent wheel-scroll container; batch time selection expands loading up to 1,000 children.
|
|
16
|
+
- Batch mode changes cards into selection targets and hides individual archive/restore actions. The highlighted 完成 button exits batch mode.
|
|
17
|
+
- Open a loaded child at its exact `{ parentSessionId, childSessionId, mode }` address. In normal mode the whole card opens the child; archive controls do not.
|
|
18
|
+
- Show session IDs beside names, compact metadata, token totals, and creation time in the relative-time tooltip.
|
|
19
|
+
- Active children are grouped at the top in a collapsible section. When the runtime exposes conversation snapshots, the panel shows the latest two lines of live output, recent tool calls, context injection, command status, and a gray final snapshot after completion.
|
|
20
|
+
|
|
21
|
+
## Screenshot guide
|
|
22
|
+
|
|
23
|
+
The panel screenshot demonstrates the compact layout:
|
|
24
|
+
|
|
25
|
+
1. **Header** — title, current-session/workspace counts, and close action.
|
|
26
|
+
2. **Search and scope row** — ordinary name/title/workspace search, with `id: xxx` reserved for Session ID search; workspace, session, sorting, and grouping selectors stay on one compact row.
|
|
27
|
+
3. **Classification row** — built-in and custom categories, with custom-category deletion inside the same tab frame.
|
|
28
|
+
4. **Filter row** — hide one-shot, hide stale children, show archived, and reset filters.
|
|
29
|
+
5. **Results** — collapsible active group, workspace/session group headers, Session ID beside each name, relative activity time, and archive status.
|
|
30
|
+
6. **Live activity** — when available, the last two output lines or the latest tool/context status appear at the bottom of the card; the final snapshot remains gray after completion.
|
|
31
|
+
|
|
32
|
+
## Install in the Web profile
|
|
33
|
+
|
|
34
|
+
From this directory:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
dsh plugin --profile web add file:.
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Then append the contents of [`cordis.patch.yml`](cordis.patch.yml) to your Web profile patch (usually `$DSH_HOME/profiles/web/cordis.patch.yml`) and restart the existing `dsh web` process. Refresh `http://127.0.0.1:3080` after the plugin is available.
|
|
41
|
+
|
|
42
|
+
To make this the sole subagent navigation UI, also enable the optional `ui-subagent` disable stanza in the patch. That removes DSH’s stock header lineage dropdown while retaining this manager button.
|
|
43
|
+
|
|
44
|
+
## Runtime data boundary
|
|
45
|
+
|
|
46
|
+
The public DSH Web session store exposes subagent summaries that have been discovered in the current browser runtime. It deliberately does not expose a global historical subagent index or a mode for every unvisited child. Therefore this first plugin version manages the discovered catalog; rows whose type is not yet loaded remain visible and searchable and fall back to DSH's retained session navigation. Exact catalog navigation is used automatically as soon as DSH supplies the address and mode.
|
|
47
|
+
|
|
48
|
+
A full persistent workspace-wide archive view requires a host-side catalog RPC (or an upstream DSH API) that enumerates every child address and its mode. The public `SessionSummary` does not expose the original prompt or provider/model route, so those are intentionally not queried or displayed. Live output and tool/context activity use the documented bound-session conversation snapshot when the session can be opened; if the host does not publish that snapshot, the panel falls back to the durable summary. The UI is isolated in [`lib/client.js`](lib/client.js), so it can switch to a richer source without changing the panel interaction model.
|
|
49
|
+
|
|
50
|
+
## Validation
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pnpm run check
|
|
54
|
+
```
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# DSH 子代理工作区管理器
|
|
2
|
+
|
|
3
|
+
面向 DeepSeek Harness Web 的子代理管理插件。插件在会话标题栏提供一个紧凑的 `🧩 子代理 active/total` 入口,用于搜索、筛选、分组、排序、查看和批量归档当前运行时已发现的子代理。
|
|
4
|
+
|
|
5
|
+
当前发布版本:**v1.0.1**
|
|
6
|
+
|
|
7
|
+
## 主要功能
|
|
8
|
+
|
|
9
|
+
### 会话与工作区范围
|
|
10
|
+
|
|
11
|
+
- 默认选择当前工作区。
|
|
12
|
+
- 默认选择当前主会话;即使当前页面已经进入某个子代理,会话管理器仍然以最上层主会话为基准。
|
|
13
|
+
- 工作区下拉框支持:
|
|
14
|
+
- 当前工作区
|
|
15
|
+
- 全部工作区
|
|
16
|
+
- 具体工作区列表
|
|
17
|
+
- 会话下拉框支持:
|
|
18
|
+
- 全部会话
|
|
19
|
+
- 当前工作区内的会话
|
|
20
|
+
- 当前会话标记
|
|
21
|
+
- 支持通过 `id:` 前缀单独搜索 Session ID,例如:
|
|
22
|
+
|
|
23
|
+
```text
|
|
24
|
+
id: graph:g-a92e1406
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
普通搜索不会把 Session ID 或父会话 ID 当作搜索字段。
|
|
28
|
+
|
|
29
|
+
### 搜索、排序与分组
|
|
30
|
+
|
|
31
|
+
- 普通搜索匹配子代理名称、标题和工作区名称。
|
|
32
|
+
- 支持按以下方式排序:
|
|
33
|
+
- 最近活跃
|
|
34
|
+
- 名字
|
|
35
|
+
- 类型
|
|
36
|
+
- “最近活跃”优先使用 DSH 提供的 `updatedAt`。
|
|
37
|
+
- 最近进入运行状态的子代理会被记录,即使运行结束后仍会保持靠前显示。
|
|
38
|
+
- 支持以下分组:
|
|
39
|
+
- 按会话
|
|
40
|
+
- 按工作区
|
|
41
|
+
- 按分类
|
|
42
|
+
- 按类型
|
|
43
|
+
- 不分组
|
|
44
|
+
- 按会话分组时,分组标题显示“工作区 · 会话名称”,卡片中不再重复显示相同的归属信息。
|
|
45
|
+
- 活跃子代理单独置顶,并可折叠。
|
|
46
|
+
|
|
47
|
+
### 分类标签
|
|
48
|
+
|
|
49
|
+
内置分类包括:
|
|
50
|
+
|
|
51
|
+
- 全部
|
|
52
|
+
- 其他
|
|
53
|
+
- 审计
|
|
54
|
+
- 测试
|
|
55
|
+
- 实现
|
|
56
|
+
- 规划
|
|
57
|
+
|
|
58
|
+
分类标签会显示当前数量,超过 99 个显示为 `99+`。
|
|
59
|
+
|
|
60
|
+
用户可以创建自定义正则分类。自定义分类支持:
|
|
61
|
+
|
|
62
|
+
- 标签内显示数量。
|
|
63
|
+
- 未选中时不显示选中高亮。
|
|
64
|
+
- 删除前确认。
|
|
65
|
+
- 删除按钮与分类名称处于同一个标签框内。
|
|
66
|
+
|
|
67
|
+
一次性子代理名称后显示紧凑标签:
|
|
68
|
+
|
|
69
|
+
```text
|
|
70
|
+
⚡ 一次性
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
可继续子代理不显示额外类型标签。
|
|
74
|
+
|
|
75
|
+
### 过滤与归档
|
|
76
|
+
|
|
77
|
+
支持以下过滤选项:
|
|
78
|
+
|
|
79
|
+
- 隐藏一次性
|
|
80
|
+
- 隐藏长期未活跃
|
|
81
|
+
- 显示已归档
|
|
82
|
+
- 重置筛选
|
|
83
|
+
|
|
84
|
+
归档是插件本地状态,只隐藏子代理,不会删除 DSH 会话或工作区文件。默认情况下:
|
|
85
|
+
|
|
86
|
+
- 列表不显示已归档子代理。
|
|
87
|
+
- 标题栏入口的总数和活跃数扣除已归档子代理。
|
|
88
|
+
- 勾选“隐藏一次性”后,入口按钮统计也会扣除一次性子代理。
|
|
89
|
+
|
|
90
|
+
### 批量操作
|
|
91
|
+
|
|
92
|
+
点击结果统计右侧的“批量操作”进入批量模式。批量模式下:
|
|
93
|
+
|
|
94
|
+
- 整张子代理卡片都变为选择区域。
|
|
95
|
+
- 点击卡片任意位置即可选中或取消选中。
|
|
96
|
+
- 复选框支持普通选择和 Shift 连续选择。
|
|
97
|
+
- 不会跳转到子代理会话。
|
|
98
|
+
- 隐藏单个“归档 / 解除归档”按钮。
|
|
99
|
+
- 已归档项显示 `已归档` 标签。
|
|
100
|
+
- “完成”按钮使用高亮样式,点击后退出批量模式。
|
|
101
|
+
|
|
102
|
+
可用批量操作:
|
|
103
|
+
|
|
104
|
+
- 全选当前筛选结果。
|
|
105
|
+
- 清空选择。
|
|
106
|
+
- 选择 N 小时以前的子代理。
|
|
107
|
+
- 批量归档。
|
|
108
|
+
- 批量解除归档。
|
|
109
|
+
- 一键归档当前筛选出的全部子代理。
|
|
110
|
+
|
|
111
|
+
“选择 N 小时以前”会刷新所有主会话的子代理目录,最多加载和选中 1000 个符合条件的子代理。
|
|
112
|
+
|
|
113
|
+
### 实时活动与流式输出
|
|
114
|
+
|
|
115
|
+
当 DSH 会话 API 提供绑定会话和对话快照时,卡片底部会显示实时活动:
|
|
116
|
+
|
|
117
|
+
- 最新两行模型文本或思考文本。
|
|
118
|
+
- 正在运行的工具调用,例如 `Read`、`Bash`。
|
|
119
|
+
- 最近完成的工具调用及成功/失败状态。
|
|
120
|
+
- 上下文注入,例如 `skill-catalog` 或插件系统提示。
|
|
121
|
+
- 命令状态。
|
|
122
|
+
|
|
123
|
+
实时输出具有金属光泽扫光动效;子代理结束后保留最后显示快照,并变为灰色。输出区域最多显示两行,避免持续滚动导致内容难以阅读。
|
|
124
|
+
|
|
125
|
+
## 界面说明
|
|
126
|
+
|
|
127
|
+
从上到下依次为:
|
|
128
|
+
|
|
129
|
+
1. **标题栏入口**:显示活跃数 / 总数,点击打开管理面板。
|
|
130
|
+
2. **搜索框**:普通文本搜索;使用 `id: xxx` 查询 Session ID。
|
|
131
|
+
3. **范围和排序行**:工作区、会话、排序、分组四个紧凑控件。
|
|
132
|
+
4. **分类标签行**:分类名称和数量。
|
|
133
|
+
5. **筛选行**:一次性、长期未活跃、已归档和重置筛选。
|
|
134
|
+
6. **结果统计**:显示当前结果数量和批量操作入口。
|
|
135
|
+
7. **结果列表**:活跃分组、会话/工作区分组、子代理卡片和实时状态。
|
|
136
|
+
|
|
137
|
+
插件不在左侧全局菜单增加入口按钮;入口仅位于会话标题栏。
|
|
138
|
+
|
|
139
|
+
## 安装
|
|
140
|
+
|
|
141
|
+
在插件目录执行:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
dsh plugin --profile web add file:.
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
升级或重新安装时可以先移除旧版本:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
dsh plugin --profile web remove dsh-subagent-workspace-ui
|
|
151
|
+
dsh plugin --profile web add file:.
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
然后将 [`cordis.patch.yml`](cordis.patch.yml) 中的插件插入配置应用到 Web profile,并重启现有的 `dsh web` 进程。刷新:
|
|
155
|
+
|
|
156
|
+
```text
|
|
157
|
+
http://127.0.0.1:3080
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
如需关闭 DSH 自带的子代理导航 UI,可启用 patch 中可选的 `ui-subagent` disable 配置。
|
|
161
|
+
|
|
162
|
+
## 数据边界与兼容性
|
|
163
|
+
|
|
164
|
+
插件只管理当前 DSH Web 客户端运行时已经发现的子代理目录,不伪造不存在的历史数据。首次加载以 40 条为一页;普通分页可以继续加载,批量时间选择最多扩展到 1000 条。
|
|
165
|
+
|
|
166
|
+
公共 `SessionSummary` 不保证提供原始提示词、provider/model 或全部历史日志,因此插件不会查询或显示 provider/model。实时输出依赖公开的绑定会话接口:
|
|
167
|
+
|
|
168
|
+
```text
|
|
169
|
+
sessions.binding(childId).session
|
|
170
|
+
→ session.open()
|
|
171
|
+
→ session.getSnapshot().chat.legacy
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
如果宿主没有提供对应的对话快照,插件只能显示持久化的会话摘要和统计信息。
|
|
175
|
+
|
|
176
|
+
归档、分类和最近使用顺序保存在浏览器本地 `localStorage` 中,不会写入 DSH 会话日志。
|
|
177
|
+
|
|
178
|
+
## 开发与验证
|
|
179
|
+
|
|
180
|
+
插件的主要客户端实现位于 [`lib/client.js`](lib/client.js),宿主入口位于 [`lib/index.js`](lib/index.js)。
|
|
181
|
+
|
|
182
|
+
执行语法检查:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
pnpm run check
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
当前检查命令覆盖:
|
|
189
|
+
|
|
190
|
+
- `lib/client.js`
|
|
191
|
+
- `lib/index.js`
|
|
192
|
+
|
|
193
|
+
## v1.0.1 发布说明
|
|
194
|
+
|
|
195
|
+
- 增加中文文档。
|
|
196
|
+
- 完善当前主会话、工作区和会话选择逻辑。
|
|
197
|
+
- 增加规划分类和分类数量徽章。
|
|
198
|
+
- 增加批量选择、Shift 连选、时间范围选择和批量归档。
|
|
199
|
+
- 改进批量模式下的卡片交互和完成按钮视觉反馈。
|
|
200
|
+
- 增加活跃子代理置顶、实时工具调用、上下文注入和结束快照。
|
|
201
|
+
- 移除 provider/model 查询和显示。
|
|
202
|
+
- 移除左侧全局菜单中的子代理入口。
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Add this row to $DSH_HOME/profiles/web/cordis.patch.yml after installing this package.
|
|
2
|
+
- insert:
|
|
3
|
+
- id: subagent-workspace-manager
|
|
4
|
+
name: 'dsh-subagent-workspace-ui'
|
|
5
|
+
|
|
6
|
+
# Optional: disable the stock lineage dropdown once this manager is installed.
|
|
7
|
+
# The manager itself uses the standard header-actions slot and does not replace
|
|
8
|
+
# the entire conversation header.
|
|
9
|
+
# - id: ui-subagent
|
|
10
|
+
# disabled: true
|
package/lib/client.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: 'dsh-subagent-workspace-ui',
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
const React = require('react')
|
|
5
|
+
const { jsx, jsxs } = require('react/jsx-runtime')
|
|
6
|
+
const css = `.dsh-sam-trigger{display:inline-flex;align-items:center;gap:6px;border:0;background:transparent;color:var(--dsw-alias-label-secondary);border-radius:7px;padding:5px 7px;font:inherit;font-size:12px;cursor:pointer}.dsh-sam-trigger:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.dsh-sam-pulse{width:7px;height:7px;border-radius:50%;background:#42bd7b;animation:dsh-sam-pulse 1.5s ease-in-out infinite}@keyframes dsh-sam-pulse{0%,100%{box-shadow:0 0 0 0 rgba(66,189,123,.55)}50%{box-shadow:0 0 0 4px rgba(66,189,123,0)}}.dsh-sam-count{font-variant-numeric:tabular-nums}.dsh-sam-icon{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px}.dsh-sam-icon.dsh-sam-pulse{width:18px;height:18px;background:transparent}.dsh-sam-panel mark{background:#d7a83d;color:#171717;border-radius:2px;padding:0 1px}.dsh-sam-backdrop{position:fixed;inset:0;z-index:1000;background:rgba(0,0,0,.38);display:flex;justify-content:flex-end;pointer-events:auto}.dsh-sam-panel{height:100%;width:min(680px,100vw);box-sizing:border-box;display:flex;flex-direction:column;background:var(--dsw-specific-sidebar-fill,var(--dsw-alias-bg-layer-1));color:var(--dsw-alias-label-primary);box-shadow:var(--dsw-shadow-lv3);padding:22px;gap:14px}.dsh-sam-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.dsh-sam-head h2{font-size:20px;margin:0}.dsh-sam-close{border:0;background:transparent;color:inherit;font-size:26px;line-height:24px;cursor:pointer}.dsh-sam-controls{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,1.45fr) minmax(0,1.1fr) minmax(0,1.1fr);gap:8px;align-items:center;order:1}.dsh-sam-controls{position:relative}.dsh-sam-controls input{grid-column:1/-1;grid-row:1}.dsh-sam-controls>select{display:block;grid-row:2;box-sizing:border-box;width:100%;min-width:0;height:30px!important;min-height:30px!important;padding:0 16px 0 8px!important;font:inherit;font-size:11px!important;line-height:normal!important;vertical-align:middle;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dsh-sam-controls option{font:inherit;font-size:12px!important;line-height:normal!important}.dsh-sam-tabs{display:flex;flex-wrap:wrap;align-items:center;gap:6px}.dsh-sam-tab-wrap{display:inline-flex;align-items:center}.dsh-sam-tab-count{display:inline-block;margin-left:5px;min-width:14px;color:var(--dsw-alias-label-tertiary);font-size:10px;font-weight:500;line-height:14px;vertical-align:middle}.dsh-sam-custom-wrap{border-radius:7px;background:transparent;overflow:hidden}.dsh-sam-custom-wrap.dsh-sam-tab-selected{background:var(--dsw-alias-interactive-bg-hover)}.dsh-sam-custom-wrap .dsh-sam-custom-tab,.dsh-sam-custom-wrap .dsh-sam-custom-tab[aria-pressed=true]{background:transparent!important;border-radius:0}.dsh-sam-custom-wrap .dsh-sam-delete-tab{margin-left:0;padding:0 6px 0 2px}.dsh-sam-tab-wrap:has(.dsh-sam-other-tab){order:99}.dsh-sam-new-tab{order:98}.dsh-sam-other-tab{order:0}.dsh-sam-delete-tab{border:0;background:transparent;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1;padding:0 2px;margin-left:-5px;cursor:pointer}.dsh-sam-delete-tab:hover{color:var(--dsw-alias-label-primary)}.dsh-sam-one-shot{display:inline-block;margin-left:6px;padding:1px 4px;border-radius:4px;background:rgba(214,166,70,.18);color:#d6a646;font-size:10px;font-weight:500;vertical-align:middle}.dsh-sam-session-id{display:inline-block;margin-left:8px;color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));font-size:11px;font-weight:400;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:middle;max-width:45%}.dsh-sam-controls>select:nth-of-type(1){grid-column:1}.dsh-sam-controls>select:nth-of-type(2){grid-column:2}.dsh-sam-controls>select:nth-of-type(3){grid-column:4}.dsh-sam-controls>select:nth-of-type(4){grid-column:3}.dsh-sam-filter-btn{display:none}.dsh-sam-tabs{order:2;min-width:0;width:100%;overflow-x:auto;scrollbar-width:none}.dsh-sam-options{order:2}.dsh-sam-panel>.dsh-sam-summary{order:3}.dsh-sam-summary-line{order:3;display:flex;align-items:center;justify-content:space-between;gap:10px}.dsh-sam-batch-btn{border:0;background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);border-radius:5px;padding:4px 8px;font:inherit;font-size:12px;cursor:pointer}.dsh-sam-batch-done{background:#d6a646;color:#171717;font-weight:700;box-shadow:0 0 0 2px rgba(214,166,70,.28)}.dsh-sam-row-batch{cursor:default}.dsh-sam-row-batch:hover{background:rgba(214,166,70,.08)}.dsh-sam-row-selected{background:rgba(214,166,70,.16)}.dsh-sam-archive-btn:disabled{opacity:.4;cursor:not-allowed}.dsh-sam-row-batch .dsh-sam-archive-btn{opacity:.4;cursor:default}.dsh-sam-archived-label{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary);white-space:nowrap}.dsh-sam-batch-bar{order:3;display:flex;flex-wrap:wrap;gap:6px;align-items:center}.dsh-sam-batch-bar button{border:0;background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary);border-radius:5px;padding:4px 7px;font:inherit;font-size:11px;cursor:pointer}.dsh-sam-batch-bar button:disabled{opacity:.45;cursor:not-allowed}.dsh-sam-panel>.dsh-sam-list{order:4}.dsh-sam-filter-popover{display:grid;grid-template-columns:1fr 1fr;gap:10px;padding:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:9px;background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-2));box-shadow:var(--dsw-shadow-lv2)}.dsh-sam-filter-popover label{font-size:11px;color:var(--dsw-alias-label-tertiary);display:flex;flex-direction:column;gap:4px}.dsh-sam-filter-popover select{color:inherit;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;padding:7px;font:inherit}.dsh-sam-filter-popover .dsh-sam-filter-wide{grid-column:1/-1}.dsh-sam-controls>*{height:40px;box-sizing:border-box}.dsh-sam-controls input,.dsh-sam-controls select,.dsh-sam-options select{min-width:0;color:inherit;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:7px;padding:8px;font:inherit}.dsh-sam-tabs{display:flex;gap:6px;overflow-x:auto;padding-bottom:2px}.dsh-sam-tabs button{border:0;background:transparent;color:var(--dsw-alias-label-tertiary);padding:6px 9px;border-radius:6px;white-space:nowrap;cursor:pointer}.dsh-sam-tabs button:hover,.dsh-sam-tabs button[aria-pressed="true"]{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.dsh-sam-tabs button[aria-pressed="true"]{box-shadow:inset 0 -2px 0 var(--dsw-alias-interactive-label-brand)}.dsh-sam-options{display:flex;flex-wrap:wrap;gap:12px;color:var(--dsw-alias-label-secondary);font-size:12px}.dsh-sam-options label{display:flex;align-items:center;gap:5px;cursor:pointer}.dsh-sam-summary{font-size:12px;color:var(--dsw-alias-label-tertiary)}.dsh-sam-list{min-height:0;flex:1;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;display:flex;flex-direction:column;gap:10px;padding:2px 4px 20px 2px;scrollbar-gutter:stable}.dsh-sam-group{border:1px solid var(--dsw-alias-border-l2);border-radius:10px;overflow:hidden;flex:none}.dsh-sam-group-head{width:100%;border:0;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);padding:9px 12px;text-align:left;display:flex;justify-content:space-between;font:inherit;font-size:12px;font-weight:600;cursor:pointer}.dsh-sam-row{width:100%;box-sizing:border-box;min-width:0;border:0;border-top:1px solid var(--dsw-alias-border-l2);background:transparent;color:inherit;text-align:left;padding:10px 12px;cursor:pointer;display:grid;grid-template-columns:minmax(22px,auto) minmax(0,1fr) auto auto;gap:9px;align-items:start}.dsh-sam-row:hover{background:var(--dsw-alias-bg-layer-2)}.dsh-sam-marker{display:flex;align-items:flex-start;gap:6px;min-width:22px}.dsh-sam-marker input{margin:0;width:16px;height:16px}.dsh-sam-dot{width:8px;height:8px;border-radius:50%;margin-top:5px;background:var(--dsw-alias-label-caption)}.dsh-sam-dot.running{background:#42bd7b;box-shadow:0 0 0 3px rgba(66,189,123,.16)}.dsh-sam-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px}.dsh-sam-meta{margin-top:3px;font-size:11px;color:var(--dsw-alias-label-tertiary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dsh-sam-time{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary);white-space:nowrap;align-self:start}.dsh-sam-details{grid-column:2/-1;color:var(--dsw-alias-label-tertiary);font-size:11px}.dsh-sam-details summary{cursor:pointer;color:var(--dsw-alias-label-secondary)}.dsh-sam-live{color:#42bd7b;white-space:pre-wrap;line-height:16px;max-height:32px;overflow:hidden}.dsh-sam-live-running{background:linear-gradient(110deg,#42bd7b 20%,#f2fff7 45%,#42bd7b 70%);background-size:220% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;animation:dsh-sam-live-shine 2.4s linear infinite}@keyframes dsh-sam-live-shine{from{background-position:180% 0}to{background-position:-20% 0}}.dsh-sam-live-final{color:var(--dsw-alias-label-tertiary);opacity:.82}.dsh-sam-archive-btn{border:0;background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer;padding:0 4px;font-size:12px;line-height:16px;white-space:nowrap;align-self:start;min-width:max-content}.dsh-sam-archive-btn:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover);border-radius:5px}.dsh-sam-more{width:100%;border:0;border-top:1px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-secondary);padding:9px 12px;text-align:left;font:inherit;font-size:12px;cursor:pointer}.dsh-sam-more:hover{background:var(--dsw-alias-bg-layer-2)}.dsh-sam-empty{padding:30px 12px;text-align:center;color:var(--dsw-alias-label-tertiary)}@media(max-width:560px){.dsh-sam-panel{padding:16px;max-width:100vw;overflow:hidden}.dsh-sam-controls{grid-template-columns:minmax(0,1fr);width:100%}.dsh-sam-controls>*{grid-column:auto!important;width:100%;box-sizing:border-box}.dsh-sam-controls input{grid-column:1/-1!important}.dsh-sam-row{grid-template-columns:8px minmax(0,1fr) auto 42px;gap:6px}.dsh-sam-meta{max-width:100%;overflow:hidden;text-overflow:ellipsis}.dsh-sam-archive-btn{white-space:nowrap;font-size:11px;padding:2px}.dsh-sam-list{width:100%;box-sizing:border-box}}`
|
|
7
|
+
if (!document.querySelector('style[data-plugin-css="dsh-subagent-workspace-ui"]')) { const tag=document.createElement('style'); tag.dataset.pluginCss='dsh-subagent-workspace-ui'; tag.textContent=css; document.head.appendChild(tag) }
|
|
8
|
+
const DAY=86400000, PAGE=40, key='dsh-subagent-workspace-ui/preferences', defaultTabs=[{id:'all',name:'全部',regex:'__ALL__'},{id:'other',name:'其他',regex:'__OTHER__'},{id:'preset-review',name:'审计',regex:'\\b(?:re[- ]?)?review\\b|\\baudit\\b|\\bverify\\b|\\bverification\\b|\\binspect\\b|审计|评审|复核|核查|验证'},{id:'preset-test',name:'测试',regex:'\\btests?\\b|\\bspecs?\\b|测试'},{id:'preset-implement',name:'实现',regex:'\\bimplement(?:ation)?\\b|\\bfeatures?\\b|\\bdevelop(?:ment)?\\b|\\bfix(?:es|ed)?\\b|\\brepair\\b|实现|开发|修复|修改'},{id:'preset-planning',name:'规划',regex:'\\bplans?\\b|\\bplanning\\b|\\bdesign\\b|\\broadmap\\b|规划|计划|方案|设计'}]
|
|
9
|
+
const load=()=>{try{return JSON.parse(localStorage.getItem(key))||{}}catch{return {}}}
|
|
10
|
+
const age=t=>{const d=Math.max(0,Math.floor((Date.now()-t)/DAY));return d===0?'今天':d===1?'昨天':`${d} 天前`}
|
|
11
|
+
const title=s=>s.title||s.displayTitle||s.id, short=v=>v?v.replace(/^session-/,'').slice(0,8):'未知', workspace=s=>s?.cwd?s.cwd.split(/[\\/]/).filter(Boolean).pop():'未知工作区', modeLabel=m=>m==='one-shot'?'一次性':m==='continuable'?'可继续':'类型待加载', tokenTotal=s=>{const u=s?.projectionValues?.tokenUsage;return u?u.uncachedInputTokens+u.outputTokens+u.cacheReadTokens+u.cacheWriteTokens:undefined},fmt=n=>n==null?'未知':n>=1e6?`${(n/1e6).toFixed(1)}m`:n>=1e3?`${(n/1e3).toFixed(1)}k`:String(n), promptPreview=s=>String(s?.prompt||s?.projectionValues?.prompt||'').slice(0,100),liveOutput=s=>{const p=s?.projectionValues||{};const blocks=p.partialAssistant?.blocks||p.streamingAssistant?.blocks;const text=Array.isArray(blocks)?blocks.map(b=>b?.text||'').join(''):'';return String(p.currentOutput||p.liveOutput||p.streamingOutput||p.assistantOutput||text||'').trim()}
|
|
12
|
+
const category=name=>{const clean=name.trim();const part=clean.split(/[::|—–-]/)[0].trim();return part.length>1&&part.length<28?part:clean.split(/\\s+/).slice(0,2).join(' ')||'未分类'},rootSession=(byId,id)=>{let current=id,seen=new Set();while(current&&byId[current]?.origin==='subagent'&&byId[current].parentId&&!seen.has(current)){seen.add(current);current=byId[current].parentId}return current}
|
|
13
|
+
const modeMap=c=>Object.values(c||{}).flatMap(x=>x.entries||[]).reduce((o,e)=>{if(e.kind==='child')o[e.id]={mode:e.mode,label:e.label};return o},{}), highlight=(text,term)=>{if(!term)return text;const parts=String(text).split(new RegExp(`(${term.replace(/[.*+?^${}()|[\\]\\\\]/g,'\\\\$&')})`,'ig'));return parts.map((part,i)=>i%2?jsx('mark',{children:part},i):part)}
|
|
14
|
+
let sessionsRt=null
|
|
15
|
+
const NOOP=()=>()=>{}
|
|
16
|
+
function LiveOutput({parentId,childId,running}){
|
|
17
|
+
const [,bump]=React.useState(0),[lastText,setLastText]=React.useState(''),session=React.useMemo(()=>{try{return sessionsRt?.binding?.(childId)?.session||null}catch{return null}},[childId])
|
|
18
|
+
React.useEffect(()=>{if(!session)return;let alive=true;const setup=async()=>{try{if(parentId){const entries=sessionsRt?.list?.getSnapshot?.().subagentsByParent?.[parentId]?.entries||[],entry=entries.find(e=>e.kind==='child'&&e.id===childId);if(entry)session.configureSubagent?.({parentSessionId:parentId,childSessionId:childId,mode:entry.mode},true)}await session.open?.()}catch{}if(alive)bump(v=>v+1)};setup();const unsub=session.subscribe?.(()=>alive&&bump(v=>v+1))||NOOP();return()=>{alive=false;unsub()}},[session,parentId,childId])
|
|
19
|
+
if(!session)return null
|
|
20
|
+
const snap=session.getSnapshot?.(),chat=snap?.chat,blocks=chat?.legacy?.partial?.blocks||[],runningCalls=chat?.legacy?.runningCalls||[],nodes=chat?.legacy?.nodes||[];let text='';const activity=[];runningCalls.slice(-2).forEach(c=>activity.push(`🔧 ${c.name||'工具调用'}${c.argsRaw?` · ${String(c.argsRaw).split('\\n')[0].slice(0,80)}`:''}`));if(!activity.length)nodes.slice(-8).filter(n=>n.kind==='context'||n.kind==='tool-result'||n.kind==='command').slice(-2).forEach(n=>{if(n.kind==='context')activity.push(`上下文注入 · ${n.provenance?.label||n.form||'context'}`);else if(n.kind==='tool-result')activity.push(`${n.isError?'✖':'✓'} ${n.call?.name||'工具调用'}`);else activity.push(`⌘ ${n.name||'命令'}`)});if(activity.length)text=activity.join('\\n');else for(let i=blocks.length-1;i>=0;i--){const b=blocks[i];if((b.kind==='text'||b.kind==='reasoning')&&b.text){const lines=b.text.split('\\n').map(x=>x.trim()).filter(Boolean);if(lines.length){text=(b.kind==='reasoning'?'💭 ':'')+lines.slice(-2).join('\\n');break}}else if(b.kind==='tool-call'&&b.name){text='🔧 调用工具 '+b.name;break}}React.useEffect(()=>{if(text)setLastText(text)},[text]);const shown=text||lastText;return shown?jsx('div',{className:`dsh-sam-live ${running?'dsh-sam-live-running':'dsh-sam-live-final'}`,children:`实时输出:${shown.slice(-400)}`}):null
|
|
21
|
+
}
|
|
22
|
+
function Manager({useSessions,openChild,openSession,refresh,setCatalogOpen,sessionId}){
|
|
23
|
+
const state=useSessions(s=>s),[open,setOpen]=React.useState(false),[query,setQuery]=React.useState(''),[grouping,setGrouping]=React.useState('session'),[sort,setSort]=React.useState('recent'),[prefs,setPrefs]=React.useState(load),[limit,setLimit]=React.useState(PAGE),[tab,setTab]=React.useState('all'),[scope,setScope]=React.useState('currentWorkspace'),[filterOpen,setFilterOpen]=React.useState(false),[activeOpen,setActiveOpen]=React.useState(true),[scopeDetail,setScopeDetail]=React.useState(''),[sessionScope,setSessionScope]=React.useState(''),[recentIds,setRecentIds]=React.useState([]),[batchMode,setBatchMode]=React.useState(false),[selectedIds,setSelectedIds]=React.useState(new Set),[lastSelectedIndex,setLastSelectedIndex]=React.useState(-1),[oldHours,setOldHours]=React.useState(null)
|
|
24
|
+
const tabs=[...defaultTabs,...(prefs.tabs||[])].filter(t=>t.id!=='filtered').filter((t,i,a)=>a.findIndex(x=>x.id===t.id)===i), archived=new Set(prefs.archived||[]), archivedParents=prefs.archivedParents||{}, currentId=rootSession(state.byId,sessionId||state.current), current=state.byId[currentId], currentCwd=current?.cwd, parentRows=Object.values(state.byId).filter(s=>s.origin!=='subagent'), scopeOptions=[...new Set(parentRows.map(s=>s.cwd).filter(Boolean))],workspaceKey=scope.startsWith('workspace:')?scope.slice(10):scope==='currentWorkspace'?currentCwd:undefined,availableSessions=parentRows.filter(s=>!workspaceKey||s.cwd===workspaceKey),selectedSession=sessionScope||currentId
|
|
25
|
+
const allRows=React.useMemo(()=>{const modes=modeMap(state.subagentsByParent),rawTerm=query.trim().toLocaleLowerCase(),idOnly=rawTerm.startsWith('id:'),term=idOnly?rawTerm.slice(3).trim():rawTerm;return Object.values(state.byId).filter(s=>s.origin==='subagent').map(s=>({...s,...modes[s.id],name:modes[s.id]?.label||title(s)})).filter(s=>{const parent=s.parentId?state.byId[s.parentId]:undefined;if(workspaceKey&&parent?.cwd!==workspaceKey)return false;if(selectedSession!=='all'&&s.parentId!==selectedSession)return false;const unfiltered=tab==='all';if(!prefs.showArchived&&archived.has(s.id))return false;if(prefs.hideOneShot&&s.mode==='one-shot')return false;if(!unfiltered&&prefs.hideOld&&!s.running&&Date.now()-s.updatedAt>(prefs.oldDays||30)*DAY)return false;const custom=tabs.find(t=>t.id===tab);if(custom?.regex==='__OTHER__'){const matched=tabs.some(t=>!['filtered','all','other'].includes(t.id)&&t.regex&&(()=>{try{return new RegExp(t.regex,'i').test(s.name)}catch{return false}})());if(matched)return false}else if(custom?.regex&&custom.regex!=='__ALL__'){try{if(!new RegExp(custom.regex,'i').test(s.name))return false}catch{return false}}return !term||`${idOnly?`${s.id} ${s.parentId||''}`:`${s.name} ${title(s)} ${workspace(s)}`}`.toLocaleLowerCase().includes(term)}).sort((a,b)=>{const ar=recentIds.indexOf(a.id),br=recentIds.indexOf(b.id);return ar!==br?(ar<0?1:br<0?-1:ar-br):a.running!==b.running?(b.running?1:-1):sort==='title'?a.name.localeCompare(b.name):sort==='type'?modeLabel(a.mode).localeCompare(modeLabel(b.mode)):b.updatedAt-a.updatedAt})},[state,query,sort,prefs,scope,currentCwd,currentId,scopeDetail,workspaceKey,selectedSession,tab,tabs,recentIds])
|
|
26
|
+
const tabCounts=React.useMemo(()=>{const modes=modeMap(state.subagentsByParent),candidates=Object.values(state.byId).filter(s=>s.origin==='subagent').map(s=>({...s,...modes[s.id],name:modes[s.id]?.label||title(s)})).filter(s=>{const parent=s.parentId?state.byId[s.parentId]:undefined;return(!workspaceKey||parent?.cwd===workspaceKey)&&(selectedSession==='all'||s.parentId===selectedSession)&&(prefs.showArchived||!archived.has(s.id))&&(!prefs.hideOneShot||s.mode!=='one-shot')&&(!prefs.hideOld||s.running||Date.now()-s.updatedAt<=(prefs.oldDays||30)*DAY)}),counts={};for(const t of tabs){if(t.id==='all')counts[t.id]=candidates.length;else if(t.regex==='__OTHER__')counts[t.id]=candidates.filter(s=>!tabs.some(x=>!['all','other','filtered'].includes(x.id)&&x.regex&&(()=>{try{return new RegExp(x.regex,'i').test(s.name)}catch{return false}})())).length;else counts[t.id]=candidates.filter(s=>{try{return t.regex&&new RegExp(t.regex,'i').test(s.name)}catch{return false}}).length}return counts},[state,workspaceKey,selectedSession,prefs,tabs]),rows=allRows.slice(0,limit),currentSessionRows=Object.values(state.byId).filter(r=>r.origin==='subagent'&&r.parentId===currentId&&(prefs.showArchived||!archived.has(r.id))&&(!prefs.hideOneShot||r.mode!=='one-shot')),active=currentSessionRows.filter(r=>r.running).length,currentCount=currentSessionRows.length
|
|
27
|
+
const groups=React.useMemo(()=>{const out={};for(const row of rows){const parent=row.parentId?state.byId[row.parentId]:undefined,sameSession=row.parentId===currentId,sameWorkspace=currentCwd&&parent?.cwd===currentCwd;let bucket=row.running?'活跃子代理':grouping==='session'?`${workspace(parent||row)} · ${title(parent||current)}`:grouping==='type'?modeLabel(row.mode):grouping==='category'?(()=>{const hit=tabs.find(t=>t.id!=='filtered'&&t.id!=='all'&&t.regex&&(()=>{try{return new RegExp(t.regex,'i').test(row.name)}catch{return false}})());return hit?hit.name:'其他'})():grouping==='workspace'?workspace(parent||row):grouping==='none'?'全部结果':sameSession?'当前会话':sameWorkspace?`当前工作区 · ${workspace(parent||row)}`:(parent?`${workspace(parent)} · ${title(parent)}`:'其他会话');(out[bucket]||=[]).push(row)}return out},[rows,grouping,state.byId,sessionId,state.current,currentCwd])
|
|
28
|
+
React.useEffect(()=>{localStorage.setItem(key,JSON.stringify(prefs))},[prefs]);React.useEffect(()=>{if(!open)return;const ps=[...new Set(rows.map(r=>r.parentId).filter(Boolean))];ps.forEach(p=>{setCatalogOpen(p,true);refresh(p)});return()=>ps.forEach(p=>setCatalogOpen(p,false))},[open,limit,grouping,sort,query])
|
|
29
|
+
React.useEffect(()=>{const runningIds=allRows.filter(r=>r.running).map(r=>r.id);if(!runningIds.length)return;setRecentIds(prev=>{const next=[...runningIds,...prev.filter(id=>!runningIds.includes(id))].slice(0,100);return next.length===prev.length&&next.every((id,i)=>id===prev[i])?prev:next})},[allRows]);React.useEffect(()=>{if(oldHours==null)return;const cutoff=Date.now()-oldHours*3600000;setSelectedIds(prev=>{const next=new Set(allRows.filter(r=>r.updatedAt<=cutoff).slice(0,1000).map(r=>r.id));return next.size===prev.size&&[...next].every(id=>prev.has(id))?prev:next})},[allRows,oldHours]);const toggleSelected=(row,index,e)=>{setSelectedIds(prev=>{const next=new Set(prev);const from=e.shiftKey&&lastSelectedIndex>=0?Math.min(lastSelectedIndex,index):index,to=e.shiftKey&&lastSelectedIndex>=0?Math.max(lastSelectedIndex,index):index;if(e.shiftKey&&lastSelectedIndex>=0){for(let i=from;i<=to;i++)next.add(rows[i]?.id)}else{next.has(row.id)?next.delete(row.id):next.add(row.id)};return next});setLastSelectedIndex(index)},batchArchive=(ids,restore=false)=>setPrefs(p=>{const current=new Set(p.archived||[]);ids.forEach(id=>restore?current.delete(id):current.add(id));const nextParents={...(p.archivedParents||{})};if(restore)ids.forEach(id=>delete nextParents[id]);else ids.forEach(id=>{const row=state.byId[id];if(row)nextParents[id]=row.parentId});return {...p,archived:[...current],archivedParents:nextParents}}),archiveSelected=(restore=false)=>{const ids=[...selectedIds];if(ids.length){batchArchive(ids,restore);setSelectedIds(new Set)}},selectOld=()=>{const hours=Number(window.prompt('选择多少小时前的子代理?','24'));if(!Number.isFinite(hours)||hours<0)return;setLimit(1000);setOldHours(hours);parentRows.forEach(p=>{setCatalogOpen(p.id,true);refresh(p.id)})},archive=row=>setPrefs(p=>{const was=p.archived?.includes(row.id);const nextArchived=was?p.archived.filter(x=>x!==row.id):[...(p.archived||[]),row.id];const nextParents={...(p.archivedParents||{})};if(was)delete nextParents[row.id];else nextParents[row.id]=row.parentId;return {...p,archived:nextArchived,archivedParents:nextParents}}),openRow=r=>{try{if(r.parentId&&r.mode)openChild({parentSessionId:r.parentId,childSessionId:r.id,mode:r.mode});else openSession(r.id);setOpen(false)}catch(e){console.warn('Unable to open subagent',e)}}
|
|
30
|
+
return jsxs(React.Fragment,{children:[jsx('button',{type:'button',className:'dsh-sam-trigger',onClick:()=>{setLimit(PAGE);setOpen(true)},title:'打开子代理管理',children:[jsx('span',{className:`dsh-sam-icon ${active>0?'dsh-sam-pulse':''}`,children:'🧩'}),jsx('span',{children:`子代理 ${active}/${currentCount}`})]}),open&&jsx('div',{className:'dsh-sam-backdrop',onMouseDown:e=>{if(e.target===e.currentTarget)setOpen(false)},children:jsxs('section',{className:'dsh-sam-panel',role:'dialog','aria-modal':true,children:[jsxs('header',{className:'dsh-sam-head',children:[jsxs('div',{children:[jsx('h2',{children:'子代理管理'}),jsx('div',{className:'dsh-sam-summary',children:`当前会话 ${allRows.filter(r=>r.parentId===currentId).length} 个 · 当前工作区 ${allRows.filter(r=>currentCwd&&state.byId[r.parentId]?.cwd===currentCwd).length} 个 · 活跃 ${active} 个`})]}),jsx('button',{className:'dsh-sam-close',type:'button',onClick:()=>setOpen(false),children:'×'})]}),jsxs('div',{className:'dsh-sam-tabs',children:[tabs.map(t=>jsxs('span',{className:`dsh-sam-tab-wrap ${(prefs.tabs?.some(x=>x.id===t.id)&&!defaultTabs.some(x=>x.id===t.id))?`dsh-sam-custom-wrap ${tab===t.id?'dsh-sam-tab-selected':''}`:''}`,children:[jsx('button',{type:'button',className:`${t.id==='other'?'dsh-sam-other-tab ':''}${(prefs.tabs?.some(x=>x.id===t.id)&&!defaultTabs.some(x=>x.id===t.id))?'dsh-sam-custom-tab':''}`,'aria-pressed':tab===t.id,onClick:()=>{setTab(t.id);setLimit(PAGE)},children:jsxs(React.Fragment,{children:[t.name,jsx('small',{className:'dsh-sam-tab-count',children:tabCounts[t.id]>99?'99+':tabCounts[t.id]||0})]})},`tab-select-${t.id}`),...((prefs.tabs?.some(x=>x.id===t.id)&&!defaultTabs.some(x=>x.id===t.id))?[jsx('button',{type:'button',className:'dsh-sam-delete-tab',title:`删除分类“${t.name}”`,'aria-label':`删除分类“${t.name}”`,onClick:e=>{e.stopPropagation();if(!window.confirm(`确认删除分类“${t.name}”?`))return;setPrefs(p=>({...p,tabs:(p.tabs||[]).filter(x=>x.id!==t.id)}));if(tab===t.id)setTab('all')},children:'×'},`tab-delete-${t.id}`)]:[])]},t.id)),jsx('button',{type:'button',className:'dsh-sam-new-tab',onClick:()=>{const name=window.prompt('分类名称');if(!name)return;const regex=window.prompt('匹配正则(例如:^审计|review)');if(!regex)return;setPrefs(p=>({...p,tabs:[...(p.tabs||defaultTabs),{id:`tab-${Date.now()}`,name,regex}]}))},children:'+ 新建分类'})]}),jsxs('div',{className:'dsh-sam-controls',children:[jsx('button',{type:'button',className:'dsh-sam-filter-btn',onClick:()=>setFilterOpen(v=>!v),children:`⚙ 筛选排序 · ${scope==='currentWorkspace'?'当前工作区':scope==='currentSession'?'当前会话':'自定义范围'} · ${sort==='recent'?'最近活跃':sort==='title'?'名字':'类型'}`}),jsx('select',{value:scope,onChange:e=>{setScope(e.target.value);setSessionScope('all');setLimit(PAGE)},'aria-label':'显示范围',children:[jsx('option',{value:'currentWorkspace',children:'当前工作区'}),jsx('option',{value:'all',children:'全部工作区'}),...scopeOptions.map(c=>jsx('option',{value:`workspace:${c}`,children:workspace({cwd:c})},c))]}),jsx('select',{value:selectedSession,onChange:e=>setSessionScope(e.target.value),children:[jsx('option',{value:'all',children:'全部会话'}),...availableSessions.map(s=>jsx('option',{value:s.id,children:s.id===currentId?`当前会话 · ${title(s)}`:title(s)},s.id))]}),jsx('input',{autoFocus:true,value:query,placeholder:'搜索名称、标题或工作区;按 Session ID 搜索请使用 id: xxx',onChange:e=>{setQuery(e.target.value);setLimit(PAGE)},placeholder:'搜索子代理名字、会话或工作区…'}),jsx('select',{value:grouping,onChange:e=>{setGrouping(e.target.value);setLimit(PAGE)},children:[['session','分组:按会话'],['workspace','分组:按工作区'],['category','分组:按分类'],['type','分组:按类型'],['none','分组:不分组']].map(([v,l])=>jsx('option',{value:v,children:l},v))}),jsx('select',{value:sort,onChange:e=>setSort(e.target.value),children:[['recent','排序:最近活跃'],['title','排序:名字'],['type','排序:类型']].map(([v,l])=>jsx('option',{value:v,children:l},v))})]}),filterOpen&&jsxs('div',{className:'dsh-sam-filter-popover',children:[jsx('label',{children:['范围',jsx('select',{value:scope,onChange:e=>{setScope(e.target.value);setSessionScope('all');setLimit(PAGE)},children:[jsx('option',{value:'currentWorkspace',children:'当前工作区'}),jsx('option',{value:'currentSession',children:'当前会话'}),jsx('option',{value:'all',children:'全部工作区'}),...scopeOptions.map(c=>jsx('option',{value:`workspace:${c}`,children:`工作区 · ${workspace({cwd:c})}`},c)),...parentRows.map(s=>jsx('option',{value:`session:${s.id}`,children:`会话 · ${workspace(s)} / ${title(s)}`},s.id))]})]}),jsx('label',{children:['分组',jsx('select',{value:grouping,onChange:e=>{setGrouping(e.target.value);setLimit(PAGE)},children:[['session','按会话'],['workspace','按工作区'],['category','按分类'],['type','按类型'],['none','不分组']].map(([v,l])=>jsx('option',{value:v,children:l},v))})]}),jsx('label',{children:['排序',jsx('select',{value:sort,onChange:e=>{setSort(e.target.value);setLimit(PAGE)},children:[['recent','最近活跃'],['title','名字'],['type','类型']].map(([v,l])=>jsx('option',{value:v,children:l},v))})]})]}),jsxs('div',{className:'dsh-sam-options',children:[jsx('label',{children:[jsx('input',{type:'checkbox',checked:!!prefs.hideOneShot,onChange:()=>setPrefs(p=>({...p,hideOneShot:!p.hideOneShot}))}),'隐藏一次性']}),jsx('label',{children:[jsx('input',{type:'checkbox',checked:!!prefs.hideOld,onChange:()=>setPrefs(p=>({...p,hideOld:!p.hideOld}))}),'隐藏长期未活跃']}),jsx('label',{children:[jsx('input',{type:'checkbox',checked:!!prefs.showArchived,onChange:()=>setPrefs(p=>({...p,showArchived:!p.showArchived}))}),'显示已归档']}),jsx('button',{type:'button',onClick:()=>{setQuery('');setScope('currentWorkspace');setScopeDetail('');setSessionScope('all');setTab('all');setGrouping('session');setSort('recent');setLimit(PAGE)},children:'重置筛选'})]}),jsxs('div',{className:'dsh-sam-summary-line',children:[jsx('div',{className:'dsh-sam-summary',children:`显示 ${rows.length}/${allRows.length} 个;归档仅隐藏,不删除`}),jsx('button',{type:'button',className:`dsh-sam-batch-btn ${batchMode?'dsh-sam-batch-done':''}`,onClick:()=>{setBatchMode(v=>!v);setSelectedIds(new Set)},children:batchMode?'完成':'批量操作'})]}),batchMode&&jsxs('div',{className:'dsh-sam-batch-bar',children:[jsx('button',{type:'button',onClick:()=>setSelectedIds(new Set(allRows.map(r=>r.id))),children:'全选'}),jsx('button',{type:'button',onClick:()=>setSelectedIds(new Set),children:'清空'}),jsx('button',{type:'button',onClick:selectOld,children:'选择 N 小时前'}),jsx('button',{type:'button',disabled:!selectedIds.size,onClick:()=>archiveSelected(false),children:`批量归档 (${selectedIds.size})`}),jsx('button',{type:'button',disabled:!selectedIds.size,onClick:()=>archiveSelected(true),children:'批量解除归档'}),jsx('button',{type:'button',onClick:()=>{if(!window.confirm('确认归档当前筛选出的全部子代理?'))return;batchArchive(allRows.map(r=>r.id));setSelectedIds(new Set)},children:'一键全部归档'})]}),jsx('div',{className:'dsh-sam-list',onWheel:e=>e.stopPropagation(),children:Object.keys(groups).length===0?jsx('div',{className:'dsh-sam-empty',children:'没有符合条件的子代理。'}):Object.entries(groups).map(([name,entries])=>jsxs('section',{className:'dsh-sam-group',children:[jsx('div',{className:'dsh-sam-group-head',onClick:()=>name==='活跃子代理'&&setActiveOpen(v=>!v),children:[jsx('span',{children:`${name}${name==='活跃子代理'?(activeOpen?' ▾':' ▸'):''}`}),jsx('span',{children:entries.length})]}),(name==='活跃子代理'&&!activeOpen?[]:entries).map(row=>jsx('div',{className:`dsh-sam-row ${batchMode?'dsh-sam-row-batch':''}${selectedIds.has(row.id)?' dsh-sam-row-selected':''}`,onClick:e=>{if(batchMode)toggleSelected(row,rows.indexOf(row),e);else openRow(row)},children:[jsx('span',{className:'dsh-sam-marker',children:[batchMode&&jsx('input',{type:'checkbox',checked:selectedIds.has(row.id),onChange:e=>{e.stopPropagation();toggleSelected(row,rows.indexOf(row),e)},onClick:e=>e.stopPropagation(),'aria-label':`选择 ${row.name}`}),jsx('span',{className:`dsh-sam-dot ${row.running?'running':''}`})]}),jsx('button',{type:'button',className:'dsh-sam-row-open',style:{all:'unset',cursor:batchMode?'default':'pointer',minWidth:0},onClick:e=>{e.stopPropagation();if(batchMode)toggleSelected(row,rows.indexOf(row),e);else openRow(row)},title:`${row.name}\\n${row.id}`,children:jsxs('span',{children:[jsx('div',{className:'dsh-sam-title',children:jsxs('span',{children:[highlight(row.name,query),row.mode==='one-shot'&&jsx('small',{className:'dsh-sam-one-shot',children:'⚡ 一次性'}),jsx('small',{className:'dsh-sam-session-id',children:row.id})]})}),grouping==='session'?null:jsx('div',{className:'dsh-sam-meta',children:highlight(`${modeLabel(row.mode)}${grouping==='session'?'':` · ${row.parentId?`${workspace(state.byId[row.parentId]||row)} / ${title(state.byId[row.parentId]||row)}`:'无父会话'}`}`,query)})]})}),jsx('span',{className:'dsh-sam-time',title:`创建:${new Date(row.createdAt||row.updatedAt).toLocaleString()}`,children:age(row.updatedAt)}),batchMode?(archived.has(row.id)&&jsx('span',{className:'dsh-sam-archived-label',children:'已归档'})):jsx('button',{type:'button',className:'dsh-sam-archive-btn',onClick:e=>{e.stopPropagation();archive(row)},title:archived.has(row.id)?'解除归档':'归档(只隐藏,不删除)','aria-label':`${archived.has(row.id)?'解除归档':'归档'} ${row.name}`,children:archived.has(row.id)?'↩ 解除':'➜ 归档'}),jsxs('div',{className:'dsh-sam-details',children:[jsx('div',{children:`输入 ${fmt(row.projectionValues?.tokenUsage?.uncachedInputTokens)} / 输出 ${fmt(row.projectionValues?.tokenUsage?.outputTokens)} · 缓存命中 ${row.projectionValues?.tokenUsage?.cacheReadTokens!=null&&tokenTotal(row)?Math.round(row.projectionValues.tokenUsage.cacheReadTokens/tokenTotal(row)*100):'未知'}% · 轮数 ${row.projectionValues?.sessionStats?.turns??row.projectionValues?.turns??'未知'} · 步数 ${row.projectionValues?.sessionStats?.steps??row.projectionValues?.steps??'未知'}`}),promptPreview(row)&&jsx('div',{children:`提示词:${promptPreview(row)}`}),jsx(LiveOutput,{parentId:row.parentId,childId:row.id,running:row.running})]})]},row.id)),limit<allRows.length&&jsx('button',{type:'button',className:'dsh-sam-more',onClick:()=>setLimit(n=>n+PAGE),children:`加载下一页(剩余 ${allRows.length-limit} 个)`})]},name))})]})})]})
|
|
31
|
+
}
|
|
32
|
+
function apply(ctx){sessionsRt=ctx.sessions;const actions={openChild:a=>ctx.sessions.openSubagent(a),openSession:id=>ctx.sessions.open(id),refresh:p=>ctx.sessions.refreshSubagents(p),setCatalogOpen:(p,o)=>ctx.sessions.setSubagentCatalogOpen(p,o)},face=()=>actions;ctx.slots.inject('conversation.session.header.actions',()=>ctx.slots.register({name:'conversation.session.header.actions',id:'subagent-workspace-manager',order:100,inject:face},Manager))}
|
|
33
|
+
return {inject:['sessions','slots'],apply}
|
|
34
|
+
}
|
|
35
|
+
})
|
package/lib/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-subagent-workspace-ui",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Searchable workspace subagent manager for DeepSeek Harness Web",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/miuzel/dsh-subagent-ui.git"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"lib",
|
|
13
|
+
"README.md",
|
|
14
|
+
"README.zh.md",
|
|
15
|
+
"LICENSE",
|
|
16
|
+
"cordis.patch.yml"
|
|
17
|
+
],
|
|
18
|
+
"main": "lib/index.js",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": "./lib/index.js",
|
|
21
|
+
"./client": "./lib/client.js",
|
|
22
|
+
"./package.json": "./package.json"
|
|
23
|
+
},
|
|
24
|
+
"dsh": {
|
|
25
|
+
"bundle": {
|
|
26
|
+
"patch": "./cordis.patch.yml"
|
|
27
|
+
},
|
|
28
|
+
"client": {
|
|
29
|
+
"inject": [
|
|
30
|
+
"@deepseek-ai/dsh-client-runtime",
|
|
31
|
+
"@deepseek-ai/dsh-client-locale",
|
|
32
|
+
"@deepseek-ai/dsh-client-ui-conversation",
|
|
33
|
+
"@deepseek-ai/dsh-client-ui-sidebar"
|
|
34
|
+
],
|
|
35
|
+
"platform": "web"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"@deepseek-ai/cordis": "^4.0.1"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"check": "node --check lib/client.js && node --check lib/index.js"
|
|
43
|
+
}
|
|
44
|
+
}
|