dsh-subagent-workspace-ui 1.6.0 → 1.8.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/README.md +36 -15
- package/README.zh.md +61 -13
- package/lib/client.js +62 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,22 +19,24 @@ A Web client plugin that adds a **子代理管理** button to the conversation-h
|
|
|
19
19
|
- Open a loaded child at its exact `{ parentSessionId, childSessionId, mode }` address. In normal mode the whole card opens the child; archive controls do not.
|
|
20
20
|
- Every row also carries a dedicated **open in the sidebar** button (`◫`) that opens the child as a right-sidebar tab, so the main conversation stays where it is. The button stops propagation (it never triggers the row's default navigation and never toggles batch selection), and it is rendered only when the runtime exposes the sidebar capability: without `ctx.sidebarRight` plus a type that claims the address, the button is hidden entirely; for a single row whose subagent address cannot be resolved it stays visible but disabled with a readable reason. The same button is on the active-subagent floating panel.
|
|
21
21
|
- Show session IDs beside names, compact metadata, token totals, and creation time in the relative-time tooltip.
|
|
22
|
-
- Active children are grouped at the top in a collapsible section.
|
|
22
|
+
- Active children are grouped at the top in a collapsible section. The panel shows the latest two lines of live output, recent tool calls, context injection, command status, and a gray final snapshot after completion — for **every** running row, not only the selected child, because the plugin retains its own session binding instead of borrowing whatever the main view happens to hold.
|
|
23
23
|
|
|
24
24
|
## Screenshot guide
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
Both screenshots were taken on the `1.7.0-dev` line (commit `79d9d0f`) against a real dsh **0.1.6-alpha.2** instance in its dark theme. The session had four background subagents at once — three still running and one already finished — so every row could be shown with its real type, model and usage figures. The pair below is the English UI; the Chinese UI pair is in [`README.zh.md`](README.zh.md).
|
|
27
27
|
|
|
28
|
-

|
|
28
|
+

|
|
29
29
|
|
|
30
|
-

|
|
30
|
+

|
|
31
31
|
|
|
32
|
-
1. **Header** — title,
|
|
32
|
+
1. **Header** — panel title, `Current session n · Current workspace n · Active n`, the show-active-float toggle, and the close action.
|
|
33
33
|
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.
|
|
34
|
-
3. **Classification row** — built-in and custom categories
|
|
35
|
-
4. **Filter row** — hide one-shot, hide
|
|
36
|
-
5. **
|
|
37
|
-
6. **
|
|
34
|
+
3. **Classification row** — built-in and custom categories with live counts, custom-category creation and deletion inside the same tab frame.
|
|
35
|
+
4. **Filter row** — hide one-shot, hide long-inactive, subagent background colour (light/dark), and reset filters.
|
|
36
|
+
5. **Summary row** — `Showing n/m`, show details, show hidden, and the batch-mode entry.
|
|
37
|
+
6. **Active subagents group** — pinned to the top and collapsible, with `Pause all`; each row carries the activity dot, the name, the Session ID, the relative activity time, and the `◫` open-in-the-sidebar, `⏸ Pause`, `⊘ Hide` and `🗑 Delete` actions.
|
|
38
|
+
7. **Detail block** — `Type: … · Model: …` straight from the host's read-only projections, then the official usage line `↑ billed (miss …) / ↓ output · Hit n% · n tps · n rnds · n stps`; a running child also shows its latest live-output lines, while a finished child keeps its final snapshot.
|
|
39
|
+
8. **Floating panel** — the running children of the current session in a compact always-on-top card, each with its live output, usage line and stop button; it appears whenever at least one child is running and the manager panel is closed. While it is shown it keeps every running child it lists live, independently of which child the main view has selected.
|
|
38
40
|
|
|
39
41
|
## Install in the Web profile
|
|
40
42
|
|
|
@@ -52,19 +54,21 @@ If you previously disabled `ui-subagent` manually in `$DSH_HOME/profiles/web/cor
|
|
|
52
54
|
|
|
53
55
|
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.
|
|
54
56
|
|
|
55
|
-
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, so prompts are neither queried nor displayed; **type and model** come from the public projections documented in [Type and model](#type-and-model-read-only-projections). Live output and tool/context activity are read from
|
|
57
|
+
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, so prompts are neither queried nor displayed; **type and model** come from the public projections documented in [Type and model](#type-and-model-read-only-projections). Live output and tool/context activity are read from a bound session automatically: on dsh **0.1.2-alpha.2** they are derived from the raw `binding.eventSource` event stream (showing the tool description or target filename), while older hosts (e.g. **0.1.1-rc.2**) fall back to `session.getSnapshot().chat.legacy`. On hosts with the retain contract (**0.1.6-alpha.2** and on) the plugin first **retains** each running child itself — `sessions.binding(id)` merely borrows a binding somebody else retained, so without retaining, only the child the main view had selected ever streamed. Retention is bounded to 8 children (the float first, then the panel's rendered running rows, most recently active first) and is released as soon as a row stops rendering or stops running, when a surface closes, or when the plugin is disposed; a child beyond the cap, and any host without `retain`, falls back to the borrowed binding and then to the durable summary. Capability detection selects the path (never a version check), so the plugin stays forward compatible. 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.
|
|
56
58
|
|
|
57
59
|
### Type and model (read-only projections)
|
|
58
60
|
|
|
59
61
|
A child's type and model come from public host projections. The plugin adds no RPC and writes no state:
|
|
60
62
|
|
|
61
63
|
```text
|
|
62
|
-
# type: one reader,
|
|
63
|
-
ctx.sessions.list.getSnapshot().subagentsByParent[parentId].entries
|
|
64
|
+
# type: one reader, read-only catalog and identity rungs, resolved in this order
|
|
65
|
+
ctx.sessions.list.getSnapshot().subagentsByParent[parentId].entries # dsh ..0.1.6-alpha.2
|
|
64
66
|
→ entry.mode # rung 1: discovered catalog entry (present once that parent's catalog was pulled)
|
|
67
|
+
ctx.sessions.list.getSnapshot().byId[parentId].projectionValues.subagentCatalog # dsh 0.1.7-rc.1..
|
|
68
|
+
→ entry.mode # rung 1': the parent's own catalog projection (same direct children, catalog event order)
|
|
65
69
|
ctx.sessions.list.getSnapshot().byId[childId].projectionValues.subagent
|
|
66
70
|
→ { mode, label, seq } | null # rung 2: the child's own identity projection (pushed on the live-control stream)
|
|
67
|
-
→ displayed value = rung 1 ?? rung 2 #
|
|
71
|
+
→ displayed value = rung 1 ?? rung 1' ?? rung 2 # all silent = the existing `typeLoading` text
|
|
68
72
|
|
|
69
73
|
# model: the session projection modelSelection (probed by key; unreadable = the host does not publish it)
|
|
70
74
|
ctx.sessions.list.getSnapshot().byId[childId].projectionValues.modelSelection
|
|
@@ -82,13 +86,13 @@ The display rules are identical on all three surfaces and differ only in density
|
|
|
82
86
|
|
|
83
87
|
- `reasoningEffort` is appended **only when the host supplies it** (` · high`); no placeholder is rendered otherwise.
|
|
84
88
|
- All three surfaces share one `modelText()` reader, so the source, the precedence (`next ?? lastUsed`), and the fallback are the same everywhere; the float only drops the field labels.
|
|
85
|
-
- The type is resolved by one reader (`childModeOf`) with one fallback order, shared by the detail row, the row badge and the float: the discovered catalog entry first, then the child's own `subagent` identity projection. Rung 2 is what makes the type correct on a freshly loaded page **without** opening the manager panel — the host pushes that projection on the live-control stream and on every session-added summary, while rung 1 only arrives when a parent's catalog is pulled (opening the panel is one such pull). Both rungs silent keeps the existing fallback: the panel and the float show **type loading…** (`类型待加载` in Chinese), never a blank or a `null`.
|
|
89
|
+
- The type is resolved by one reader (`childModeOf`) with one fallback order, shared by the detail row, the row badge and the float: the discovered catalog entry first, then the child's own `subagent` identity projection. The catalog itself has two generations — `subagentsByParent[parentId].entries` up to **0.1.6-alpha.2**, and the parent session's own `projectionValues.subagentCatalog` from **0.1.7-rc.1** on (with `projectionsBySession[parentId].values.subagentCatalog` as a defensive fallback) — and the legacy source wins whenever a host publishes both, so an older host reads exactly what it read before. Rung 2 is what makes the type correct on a freshly loaded page **without** opening the manager panel — the host pushes that projection on the live-control stream and on every session-added summary, while rung 1 only arrives when a parent's catalog is pulled (opening the panel is one such pull). Both rungs silent keeps the existing fallback: the panel and the float show **type loading…** (`类型待加载` in Chinese), never a blank or a `null`.
|
|
86
90
|
- When the host does not publish the projection (e.g. **0.1.1-rc.2**), when the projection value is empty, or when provider/model is an empty string, the plugin shows the explicit fallback **model unknown** (`模型未知` in Chinese) instead of a blank or `null/null`. That fallback has its own i18n key (`modelUnknown`) and never reuses `typeLoading`: on an old host the two fields degrade independently (observed row: `Type: type loading… · Model: model unknown`).
|
|
87
91
|
- **Read-only, no switching**: the plugin calls no model write API such as `selectedModel` and offers no model-switch UI; the official SDK marks model selection as unavailable for addressed subagent sessions (`model selection is unavailable for addressed subagent sessions`).
|
|
88
92
|
|
|
89
93
|
## Compatibility
|
|
90
94
|
|
|
91
|
-
v1.
|
|
95
|
+
v1.8.0 supports dsh **0.1.6-alpha.2** and **0.1.7-rc.1** and stays backward compatible with all DeepSeek Harness versions. Those two hosts differ in the API surface this plugin reads: 0.1.7-rc.1 **removed** `sessions.setSubagentCatalogOpen`, **renamed** `refreshSubagents(parentSessionId)` to `refreshProjections(sessionId)`, and **replaced** `SessionListState.subagentsByParent` with the parent session's own `subagentCatalog` projection. Every call site probes for the capability it needs, so both generations work and no version number is ever compared. Opening a subagent session is capability-detected at runtime and degrades in three tiers:
|
|
92
96
|
|
|
93
97
|
```text
|
|
94
98
|
# dsh 0.1.2-alpha.5 .. 0.1.6-alpha.1: the session controller entry points
|
|
@@ -118,10 +122,27 @@ ctx.sessions.subagentAddress → typeof function (per-row address lookup)
|
|
|
118
122
|
# with a readable reason instead of throwing.
|
|
119
123
|
```
|
|
120
124
|
|
|
125
|
+
Both faces are resolved **per row and per click, never once at activation**: dsh **0.1.7-rc.1** registers `sidebarRight`/`sidebarRightTabs` only *after* a plugin's `apply` has run — on **0.1.6-alpha.2** they are already present when `apply` runs — so a probe performed once at activation hides the whole `◫` column on the newer host while leaving the older one untouched.
|
|
126
|
+
|
|
121
127
|
Adding the sidebar capability does not change the navigation probe above: the row's default click still walks the same three tiers, and the new button never touches them.
|
|
122
128
|
|
|
123
129
|
Opening a subagent in the right sidebar uses DSH's own `subagentchat` right-sidebar tab, so the pane is a *session view*: the subagent session's composer there is DSH's official read-only composer (`一次性子代理记录` / "one-shot subagent record") rather than this plugin's UI. That is expected, and it is the only official way to read a child session without leaving the main conversation.
|
|
130
|
+
## v1.8.0
|
|
131
|
+
|
|
132
|
+
- **Fix**: live output only updated for the **currently selected** subagent. `sessions.binding(id)` only *borrows* a binding somebody else retained, and the stock view retains just the session the main view has selected — so every other child had no live region at all. The plugin now **retains** each running child it is showing itself: the float keeps every running child it displays, the panel keeps the running rows it renders, both share a cap of 8 (float first, most recently active first), and each row that stops rendering or stops running, every closed surface and the plugin's disposal releases in pairs. A host without `retain`, and any child beyond the cap, still falls back to the borrowed binding and then to the durable summary. Landing with it: **Pause now works on a row that is not selected** — it previously called into a binding that did not exist and silently did nothing.
|
|
133
|
+
- **Compatibility with dsh 0.1.7-rc.1**: that host **removed** `sessions.setSubagentCatalogOpen`, **renamed** `refreshSubagents(parentSessionId)` to `refreshProjections(sessionId)`, and **replaced** `SessionListState.subagentsByParent[parent]` with the parent session's own `projectionValues.subagentCatalog` projection (the same direct children in catalog event order; entries lost their `kind` discriminator and gained a third `mode: 'unknown'` arm). Un-guarded, the removed method threw out of the manager panel's open effect and the error boundary took the whole header slot down — **opening the manager crashed the title bar**. Every host call is now capability-probed and never version-checked: one reader resolves the catalog from the parent's `subagentCatalog` on 0.1.7-rc.1+ and from `subagentsByParent` up to 0.1.6-alpha.2 (the legacy source wins when a host publishes both, so older hosts read exactly what they read before), `refresh` prefers `refreshProjections` and falls back to `refreshSubagents`, and a missing `setSubagentCatalogOpen` is skipped. An entry whose `mode` is `unknown` keeps its label and yields the mode to the child's own identity projection instead of trusting that arm.
|
|
134
|
+
- **Fix**: on 0.1.7-rc.1 the per-row **open in the sidebar** action (`◫`) disappeared from every row. That host registers `sidebarRight`/`sidebarRightTabs` *after* this plugin's `apply` runs, so the one-time probe at activation judged both absent for good. Both faces are now resolved per row and per click: a host that never publishes them still renders no button (byte-for-byte the previous surface), while an available face whose row address does not validate keeps the existing disabled button and its readable reason.
|
|
135
|
+
- **Docs**: the type/model ladder documents both catalog generations, and the compatibility section records the 0.1.7-rc.1 API changes above.
|
|
136
|
+
- **Tweak**: the active float pins its background at **exactly 95% opacity**. dsh **0.1.7-rc.1** redefined `--dsw-specific-menu` as a translucent glass colour — `#30313680` in the dark theme (alpha `0x80` = 50%) and `#f8f9fa94` in the light one (58%) — where 0.1.6 pointed that same token at the opaque `--dsw-alias-bg-layer-3`, so the float turned see-through on the newer host. It now composites the host's **opaque** surface token at 95% (`color-mix`, with the previous declaration kept ahead of it as the fallback for a host without `color-mix`), which also makes the float's density identical on 0.1.6 and 0.1.7 — no version check, and the tone still comes from the host's own theme tokens.
|
|
137
|
+
|
|
138
|
+
Verified on dsh **0.1.6-alpha.2** and **0.1.7-rc.1**: `pnpm run check` green (bundle freshness in sync, 98,439 bytes). On 0.1.7-rc.1 the panel opens on a live session with all 7 of its subagents, rows carrying their catalog labels, `Type: continuable`, model and usage figures, `◫` on every row, and **no console error after the click**. On 0.1.6-alpha.2 the panel object is byte-for-byte identical to the pre-change build, and the same batch of data shows the same unknowns it showed before (a child whose projections the host never published stays `model unknown`, exactly as it does on 0.1.6). The float's background was then measured on a live 0.1.7-rc.1 instance with a running child: it renders `rgb(51,52,54)`, the exact value `#353638` at 95% predicts over the `rgb(21,21,23)` header backdrop behind it, where the previous 50% glass would have rendered `rgb(34,35,38)`; sampling the same float over a backdrop 22 levels lighter (`rgb(43,43,45)`) moved it by at most one level, which is what a 95% surface does and a 50% one cannot (it would have moved 11 levels). The host's own `dsh-client-ui-open-in-app` logs a load-time `inactive context` error on 0.1.7-rc.1; it is unrelated to this plugin and deliberately left alone.
|
|
139
|
+
|
|
140
|
+
## v1.7.0
|
|
141
|
+
|
|
142
|
+
- **Fix**: the **open in the sidebar** button (`◫`) — on a panel row and on the active float — was a far smaller target than the 24×24 CSS px minimum: 18.03×16 and 17.14×17, i.e. only the glyph itself was clickable. Both now measure **24.03×24** and **24.14×25**, while the layout box, the glyph position, the row heights and every neighbouring button's coordinates stay **pixel-identical**: the padding growth is cancelled by negative margins, and the hover pill moved into an `::after` pseudo-element whose insets reproduce the previous border box exactly (18.03×16 / 17.14×17). The one rendering difference is compositing order — the hover overlay now paints over the glyph instead of under it (≲1.3/255 per channel in the dark theme, ≲0.7/255 in the light one), which is imperceptible. Disabled rows keep their opacity and `not-allowed` cursor, and the larger area cannot turn a row press into a float drag: `closest('button,input,label')` is DOM ancestry, not geometry.
|
|
143
|
+
- **Docs**: the dshmarket screenshots were re-shot on a real dsh **0.1.6-alpha.2** instance of the `1.7.0-dev` line (commit `79d9d0f`) — the manager panel and the active-subagents float, each in Chinese and in English (`*.en.png`, the host locale actually switched), dark theme, 1280×900 CSS at DPR 1.75. The captured session ran four background subagents at once (three running, one finished), so every row carries its real type, model and the official usage line instead of fallback text, with the `◫`, pause, hide and delete actions visible. `screenshots.json` now lists all four paths (dshmarket's carousel cap is 6), each README points at its own language pair instead of both sharing one, and the screenshot guide was rewritten — it still described controls (`show archived`, `→ archive`) that no longer exist.
|
|
124
144
|
|
|
145
|
+
Verified on dsh **0.1.6-alpha.2**: `pnpm run check` green — the bundle's only change in this release is the hit-area CSS, and the screenshot work touched no source at all (four PNGs, `screenshots.json` and the two READMEs). The hit area was checked geometrically (padding / margin / `::after` insets against the previous border box) and by a pixel profile of the button column before and after; `./test.sh` smoke runs on an isolated `DSH_HOME` clicked the newly added edge on both the panel row and the float, exercised a disabled orphan row, and A/B-tested that pressing the enlarged area still opens the sidebar without starting a float drag or misfiring the neighbouring pause button.
|
|
125
146
|
|
|
126
147
|
## v1.6.0
|
|
127
148
|
|
package/README.zh.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
面向 DeepSeek Harness Web 的子代理管理插件。插件在会话标题栏提供一个紧凑的 `🧩 子代理 active/total` 入口,用于搜索、筛选、分组、排序、查看和批量归档当前运行时已发现的子代理。
|
|
4
4
|
|
|
5
|
-
当前发布版本:**v1.
|
|
5
|
+
当前发布版本:**v1.8.0**
|
|
6
6
|
|
|
7
7
|
## 主要功能
|
|
8
8
|
|
|
@@ -46,13 +46,22 @@
|
|
|
46
46
|
|
|
47
47
|
## 界面截图
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
两张截图拍摄于 `1.7.0-dev` 分支(commit `79d9d0f`)上真实运行的 dsh **0.1.6-alpha.2** 实例,深色主题。拍摄时同一个会话里并存 4 个后台子代理——3 个仍在运行、1 个已完成,因此每行都能展示真实的类型、模型与用量数字。这里是中文界面;英文界面两张见 [`README.md`](README.md)。
|
|
50
50
|
|
|
51
|
-

|
|
52
52
|
|
|
53
|
-
|
|
53
|
+

|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
管理面板自上而下依次是:
|
|
56
|
+
|
|
57
|
+
1. **标题栏** —— 面板标题、`当前会话 n 个 · 当前工作区 n 个 · 活跃 n 个`、活跃浮窗开关与关闭按钮。
|
|
58
|
+
2. **搜索与范围行** —— 名称/标题/工作区搜索,`id: xxx` 专用于 Session ID 搜索;工作区、会话、排序、分组四个选择器同处一行。
|
|
59
|
+
3. **分类行** —— 内置分类与自定义分类(带实时数量),自定义分类的新建与删除都在同一标签框内。
|
|
60
|
+
4. **筛选行** —— 隐藏一次性、隐藏长期未活跃、子代理背景色(浅色/深色)与重置筛选。
|
|
61
|
+
5. **汇总行** —— `显示 n/m 个`、显示详情、显示已隐藏,以及批量操作入口。
|
|
62
|
+
6. **活跃子代理分组** —— 置顶且可折叠,带「一键暂停」;每行包含活跃圆点、名称、Session ID、相对活动时间,以及 `◫` 在侧边栏打开、`⏸ 暂停`、`⊘ 隐藏`、`🗑 删除`。
|
|
63
|
+
7. **详情区** —— 直接来自宿主只读投影的 `类型:… · 模型:…`,以及官方口径的用量行 `↑ 计费输入 (未缓存…) / ↓ 输出 · 命中 n% · n tps · n 轮 · n 步`;运行中的子代理都会显示最新的实时输出(不只当前选中的那一个),已完成的保留最终快照。
|
|
64
|
+
8. **活跃浮窗** —— 当前会话里运行中的子代理以紧凑浮窗常驻,每行带实时输出、用量行与暂停按钮;面板关闭且存在运行中子代理时自动出现。浮窗显示期间会为其中每个运行中的子代理各自保留实时会话,与主视图当前选中哪一个无关。
|
|
56
65
|
|
|
57
66
|
### 分类标签
|
|
58
67
|
|
|
@@ -142,12 +151,22 @@
|
|
|
142
151
|
|
|
143
152
|
实时输出具有金属光泽扫光动效;子代理结束后保留最后显示快照,并变为灰色。输出区域最多显示两行,避免持续滚动导致内容难以阅读。
|
|
144
153
|
|
|
154
|
+
**每个运行中的子代理都有自己的实时流(dsh 0.1.6-alpha.2 起)**:宿主 `sessions.binding(id)` 的契约是「借用已被保留的绑定,不创建保留」,而官方客户端里唯一保留会话的是主会话视图——它只保留**当前选中**的那一个。因此插件改为自己成为保留者:`sessions.retain(target, { source: 'subagentWorkspaceUi' })` 拿到 `SessionReference`,用 `reference.binding` 取 `session` / `eventSource`,不再依赖「是否被选中」。
|
|
155
|
+
|
|
156
|
+
保留是有界且成对释放的:
|
|
157
|
+
|
|
158
|
+
- **浮窗常驻**:浮窗显示期间,对其中每个运行中的子代理各保留一个引用。
|
|
159
|
+
- **面板仅可见行**:管理面板只对当前筛选/分组后**实际渲染出来**的运行中行保留,打开面板不会去加载全部历史子代理。
|
|
160
|
+
- **上限 8**:浮窗优先,其次按最近活动优先;超出上限的运行中行按快照显示(仍有用量等持久信息)。
|
|
161
|
+
- **成对释放**:行不再渲染、子代理转为非运行态、浮窗/面板关闭或插件卸载时调用 `release()`;重复释放幂等。
|
|
162
|
+
|
|
145
163
|
**兼容两个 API 代际(自动探测)**:
|
|
146
164
|
|
|
147
|
-
- dsh **0.1.
|
|
165
|
+
- dsh **0.1.6-alpha.2** 起:自己 `retain`(`reference.binding.eventSource` 原始 `SessionEvent` 事件流);宿主未提供 `retain` 时回退借用式 `binding(id)`。
|
|
166
|
+
- dsh **0.1.2-alpha.2**:`binding.eventSource`(原始 `SessionEvent` 事件流)推导实时输出。
|
|
148
167
|
- dsh **0.1.1-rc.2** 及更早:回退 `session.getSnapshot().chat.legacy`(对话快照)路径。
|
|
149
168
|
|
|
150
|
-
|
|
169
|
+
按能力探测自动切换(不判版本号),向前兼容,老版本行为不变;两条路径都拿不到绑定时,界面回退到持久化摘要。
|
|
151
170
|
|
|
152
171
|
## 界面说明
|
|
153
172
|
|
|
@@ -190,15 +209,23 @@ http://127.0.0.1:3080
|
|
|
190
209
|
|
|
191
210
|
## 数据边界与兼容性
|
|
192
211
|
|
|
212
|
+
v1.8.0 支持 dsh **0.1.6-alpha.2** 与 **0.1.7-rc.1**,并对所有 DeepSeek Harness 版本保持向后兼容。这两代宿主的接口面不同:0.1.7-rc.1 删除了 `sessions.setSubagentCatalogOpen`、把 `refreshSubagents(parentSessionId)` 改名为 `refreshProjections(sessionId)`、并把 `SessionListState.subagentsByParent` 换成父会话自身的 `subagentCatalog` 投影。插件对每一项都做能力探测,因此两代宿主各自走可用路径,**从不比较版本号**。
|
|
213
|
+
|
|
193
214
|
插件只管理当前 DSH Web 客户端运行时已经发现的子代理目录,不伪造不存在的历史数据。首次加载以 40 条为一页;普通分页可以继续加载,批量时间选择最多扩展到 1000 条。
|
|
194
215
|
|
|
195
216
|
公共 `SessionSummary` 不保证提供原始提示词或全部历史日志,因此插件不查询、不显示提示词。**类型与模型**改由宿主的公开投影提供,见下一节;实时输出按能力探测自动切换两种公开接口:
|
|
196
217
|
|
|
197
218
|
```text
|
|
198
|
-
# dsh 0.1.
|
|
219
|
+
# dsh 0.1.6-alpha.2 起(retain 契约):插件自己创建并持有绑定
|
|
220
|
+
sessions.retain({ parentSessionId, childSessionId, mode }, { source: 'subagentWorkspaceUi' })
|
|
221
|
+
→ reference.binding.eventSource # 引用存续期间始终可用
|
|
222
|
+
→ getSnapshot().entries # 原始 SessionEvent:assistant/chunk、tool/call、tool/result…
|
|
223
|
+
→ release() # 行不再渲染 / 停止 / 浮窗·面板关闭 / 插件卸载
|
|
224
|
+
|
|
225
|
+
# 宿主没有 retain(能力探测):回退到借用他人保留的绑定(0.1.2-alpha.2 新 API)
|
|
199
226
|
sessions.binding(childId).eventSource
|
|
200
227
|
→ open()
|
|
201
|
-
→ getSnapshot().entries
|
|
228
|
+
→ getSnapshot().entries
|
|
202
229
|
|
|
203
230
|
# dsh 0.1.1-rc.2 及更早(旧 API):对话快照
|
|
204
231
|
sessions.binding(childId).session
|
|
@@ -206,19 +233,21 @@ sessions.binding(childId).session
|
|
|
206
233
|
→ session.getSnapshot().chat.legacy
|
|
207
234
|
```
|
|
208
235
|
|
|
209
|
-
|
|
236
|
+
如果在当前宿主拿不到对应的实时数据(没有 `retain`、也借不到绑定,或该行超出 8 个保留上限),插件只显示持久化的会话摘要和统计信息。
|
|
210
237
|
|
|
211
238
|
### 类型与模型(只读投影)
|
|
212
239
|
|
|
213
240
|
子代理的「类型」与「模型 provider/id」来自宿主的公开投影,不新增任何 RPC、也不写入任何状态:
|
|
214
241
|
|
|
215
242
|
```text
|
|
216
|
-
#
|
|
217
|
-
ctx.sessions.list.getSnapshot().subagentsByParent[parentId].entries
|
|
243
|
+
# 类型:同一个取值函数,只读的目录来源与身份来源,按此顺序回退
|
|
244
|
+
ctx.sessions.list.getSnapshot().subagentsByParent[parentId].entries # dsh ..0.1.6-alpha.2
|
|
218
245
|
→ entry.mode # 第 1 级:已发现的目录条目(该父会话的目录被拉取后才存在)
|
|
246
|
+
ctx.sessions.list.getSnapshot().byId[parentId].projectionValues.subagentCatalog # dsh 0.1.7-rc.1..
|
|
247
|
+
→ entry.mode # 第 1' 级:父会话自身的目录投影(同一批直接子代理,目录事件顺序)
|
|
219
248
|
ctx.sessions.list.getSnapshot().byId[childId].projectionValues.subagent
|
|
220
249
|
→ { mode, label, seq } | null # 第 2 级:子会话自身的身份投影(随实时控制流下发)
|
|
221
|
-
→ 显示值 = 第 1 级 ?? 第 2 级
|
|
250
|
+
→ 显示值 = 第 1 级 ?? 第 1' 级 ?? 第 2 级 # 全部拿不到 = 沿用既有 `typeLoading` 兜底文案
|
|
222
251
|
|
|
223
252
|
# 模型:会话投影 modelSelection(按 key 能力探测,读不到即视为宿主不提供)
|
|
224
253
|
ctx.sessions.list.getSnapshot().byId[childId].projectionValues.modelSelection
|
|
@@ -273,6 +302,8 @@ ctx.sessions.subagentAddress → 是函数(按行取地址)
|
|
|
273
302
|
# 验证为空 → 该行按钮渲染为禁用态并给出可读说明,绝不抛异常给用户。
|
|
274
303
|
```
|
|
275
304
|
|
|
305
|
+
两个能力面**按行、按次解析,绝不在激活时只探测一次**:dsh **0.1.7-rc.1** 在本插件 `apply` **之后**才注册 `sidebarRight`/`sidebarRightTabs`(**0.1.6-alpha.2** 在 `apply` 时已就绪),因此激活时的一次性探测会让新宿主上的整列 `◫` 消失,而旧宿主毫无异样。
|
|
306
|
+
|
|
276
307
|
要点:
|
|
277
308
|
|
|
278
309
|
- **默认点击不变**:点条目本身仍然把子代理打开到主对话区(与 v1.5.0 一致);新按钮 `stopPropagation`,既不触发默认跳转,也不会误触批量选择。
|
|
@@ -310,6 +341,23 @@ DSH_PLUGIN_DIR=.worktrees/x ./test.sh # 冒烟其它 checkout 的产物
|
|
|
310
341
|
- UI 本地化(zh/en)由 [@Marcuss2](https://github.com/Marcuss2) 在 [PR #1](https://github.com/miuzel/dsh-subagent-ui/pull/1) 中贡献,特此致谢!
|
|
311
342
|
- 子代理永久删除、会话生命周期清理及快照刷新机制的设计参考并致谢开源项目:[@heiheiha798/dsh-plugin-subagent-delete](https://github.com/heiheiha798/dsh-plugin-subagent-delete)。
|
|
312
343
|
|
|
344
|
+
## v1.8.0 发布说明
|
|
345
|
+
|
|
346
|
+
- **修复:只有当前被选中的子代理才更新实时输出**。`sessions.binding(id)` 只是**借用**别人已经保留的绑定,而官方视图只保留主视图当前选中的那一个会话——于是其余子代理根本没有实时区域。现改为插件**自己成为保留者**:浮窗保留它显示的每个运行中子代理、面板保留它渲染的运行中行,两者共用上限 8(浮窗优先、最近活跃优先),行停止渲染或停止运行、界面关闭、插件卸载时成对释放;宿主没有 `retain`、以及超出上限的行,仍回落到借用式绑定、再回落到持久化摘要。随之落地的一项修复:**未选中行的「暂停」现在真正生效**——此前它调用的是一个并不存在的绑定,属于静默空操作。
|
|
347
|
+
- **兼容 dsh 0.1.7-rc.1**:该宿主**删除**了 `sessions.setSubagentCatalogOpen`、把 `refreshSubagents(parentSessionId)` **改名**为 `refreshProjections(sessionId)`、并把 `SessionListState.subagentsByParent[parent]` **换成**父会话自身的 `projectionValues.subagentCatalog` 投影(同一批直接子代理,目录事件顺序;条目去掉了 `kind` 判别字段,并多出一个 `mode: 'unknown'` 取值)。在缺少防护的旧实现里,被删除的方法从管理器面板的打开副作用里抛出,错误边界随即带走整个标题栏 slot——**点开管理器会让标题栏直接崩掉**。现在所有宿主调用一律能力探测、**从不比较版本号**:同一个取值函数在 0.1.7-rc.1 及以上读父会话的 `subagentCatalog`、在 0.1.6-alpha.2 及以前读 `subagentsByParent`(宿主两者都发布时旧来源优先,因此旧宿主读到的与改动前逐字相同),刷新优先 `refreshProjections`、回落 `refreshSubagents`,`setSubagentCatalogOpen` 缺失则跳过;`mode` 为 `unknown` 的条目保留 label,并把类型让给子会话自身的身份投影,而不是直接采信该取值。
|
|
348
|
+
- **修复:0.1.7-rc.1 上每行的 `◫`「在侧边栏打开」整列消失**。该宿主在本插件 `apply` **之后**才注册 `sidebarRight`/`sidebarRightTabs`,激活时的一次性探测便把两者永久判为不存在。现改为**按行、按次**解析:从不发布这两个能力的宿主依旧完全不渲染按钮(与改动前逐字相同),而能力可用但该行地址校验不通过时,仍保留既有的禁用按钮与可读原因。
|
|
349
|
+
- **文档**:类型/模型阶梯补上两代目录来源;兼容性小节记录上述 0.1.7-rc.1 接口变化。
|
|
350
|
+
- **微调:活跃浮窗背景固定为 95% 不透明度**。dsh **0.1.7-rc.1** 把 `--dsw-specific-menu` 改成了带透明度的「玻璃」色——深色主题 `#30313680`(alpha `0x80` = 50%)、浅色 `#f8f9fa94`(58%)——而 0.1.6 把同一个令牌指向不透明的 `--dsw-alias-bg-layer-3`,于是浮窗在新宿主上变得半透明。现在浮窗改用宿主**不透明**的表面色令牌按**恰好 95%** 合成(`color-mix`,并把旧声明保留在其前,作为不具备 `color-mix` 的宿主的回落),顺带让浮窗在 0.1.6 与 0.1.7 上的观感一致——不判断版本号,色调仍来自宿主自己的主题令牌。
|
|
351
|
+
|
|
352
|
+
已在 dsh **0.1.6-alpha.2** 与 **0.1.7-rc.1** 上验证:`pnpm run check` 全绿(bundle 新鲜度一致,98,439 字节)。0.1.7-rc.1 上打开一个真实运行中的会话,面板列出全部 7 个子代理,行名回到目录标签、`Type: continuable`、模型与用量齐全、每行都有 `◫`,且**点击后控制台没有任何错误**;0.1.6-alpha.2 上面板对象与改动前逐字节相同,同一批数据仍显示与改动前相同的未知项(宿主从未发布过投影的子代理依旧是 `model unknown`,与 0.1.6 上的表现完全一致)。浮窗背景随后在一个真实运行中的 0.1.7-rc.1 实例上做了像素测量:它渲染为 `rgb(51,52,54)`,正是 `#353638` 按 95% 合成、叠在其身后的 `rgb(21,21,23)` 标题栏背景上的精确预测值,而此前的 50% 玻璃色在该处应渲染成 `rgb(34,35,38)`;把同一个浮窗放到亮 22 级的背景(`rgb(43,43,45)`)上,它最多只变化 1 级——这正是 95% 表面的行为,50% 表面不可能如此(它会变化 11 级)。0.1.7-rc.1 宿主自带的 `dsh-client-ui-open-in-app` 会在加载期报一条 `inactive context`,与本插件无关,本次刻意不动。
|
|
353
|
+
|
|
354
|
+
## v1.7.0 发布说明
|
|
355
|
+
|
|
356
|
+
- **修复:`◫`「在侧边栏打开」按钮的点击面积远小于 24×24 CSS px 下限**:面板行实测 18.03×16、活跃浮窗 17.14×17,等于只有字形本身可点。现分别为 **24.03×24** 与 **24.14×25**,而**布局盒、字形位置、行高与相邻按钮坐标逐像素不变**——padding 的增量被等量负 margin 抵消,hover 药丸改由 `::after` 伪元素承载、其 inset 恰好还原原边框盒(18.03×16 / 17.14×17)。唯一的渲染差异是合成顺序:hover 叠加层现在画在字形**之上**而非之下(深色主题每通道 ≲1.3/255、浅色 ≲0.7/255,肉眼不可分辨)。禁用行仍保持透明度与 `not-allowed` 光标;扩大后的区域不会把「按行」变成「拖浮窗」——`closest('button,input,label')` 判定的是 DOM 祖先而非几何。
|
|
357
|
+
- **文档:dshmarket 截图重拍**:在 `1.7.0-dev` 分支(commit `79d9d0f`)上真实运行的 dsh **0.1.6-alpha.2** 实例拍摄,管理器面板与活跃浮窗各出中英两张(`*.en.png`,**真正切换宿主语言**而非给中文图配英文题注),深色主题、1280×900 CSS @ DPR 1.75。拍摄时同一会话并存 4 个后台子代理(3 个运行中、1 个已完成),因此每行都带**真实**类型、模型与官方口径用量行(不再是兜底文案),`◫`、暂停、隐藏、删除四个动作齐全。`screenshots.json` 现列出全部四条路径(dshmarket 轮播上限 6),两个 README 各自引用对应语言的图(不再共用同一对),且「界面截图」导览已按新 UI 重写——旧导览仍在描述本版已不存在的控件(「显示已归档」「→ 归档」等)。
|
|
358
|
+
|
|
359
|
+
已在 dsh **0.1.6-alpha.2** 上实机验证:`pnpm run check` 全绿——本版产物只有命中区这一处 CSS 改动,截图工作未触碰任何源码(只改 4 张 PNG + `screenshots.json` + 两个 README)。命中区另经几何核算(padding / margin / `::after` inset 与原边框盒的算式)与按钮列改造前后的像素剖面对照;并用隔离 `DSH_HOME` 跑 `./test.sh` 冒烟:在面板行与浮窗上点击**新增的边缘**内侧都能打开侧边栏、孤儿禁用行行为正确,且 A/B 验证了按按钮不会变成拖浮窗、也不会误触邻居的暂停按钮。
|
|
360
|
+
|
|
313
361
|
## v1.6.0 发布说明
|
|
314
362
|
|
|
315
363
|
- **特性:在右侧边栏打开子代理(`◫` 按钮)**:列表行与活跃浮窗都新增 `◫`,把子代理打开为 DSH 自带的 `subagentchat` 右侧栏标签页,主对话不跳转。按钮按**能力探测、不按版本号判断**:运行时没有 `sidebarRight` / `sidebarRightTabs` 能力、或没有标签页类型认领该行地址时,按钮整体隐藏;单行地址解析不出来时保持可见但禁用,并给出可读原因。点击不会触发行的默认跳转,也不会误触批量选择。由于该标签页正是自带 `ui-subagent` 提供的,bundle patch 不再整体禁用该插件:现在只以 `priority: -1` 遮蔽 `conversation.session.header.lineage`(**标题位遮蔽**,子代理会话标题因此不会消失),`ui-subagent` 本身保持启用;卸载时用户自己的 `cordis.patch.yml` 配置依旧原样保留。
|
package/lib/client.js
CHANGED
|
@@ -20,11 +20,14 @@ body[data-ds-dark-theme] .dsh-sam-row-hidden{background:rgba(255,255,255,0.03)!i
|
|
|
20
20
|
.dsh-sam-search-clear{border:0;background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer;padding:0 4px;font-size:15px;line-height:1;border-radius:4px}
|
|
21
21
|
.dsh-sam-search-clear:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}
|
|
22
22
|
.dsh-sam-filter-toggle{border:0;background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer;padding:6px 10px;font-size:11px;line-height:1;border-radius:6px;display:inline-flex;align-items:center;gap:2px}
|
|
23
|
-
.dsh-sam-filter-toggle:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.dsh-sam-hide-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;display:inline-flex;align-items:center;gap:2px}.dsh-sam-hide-btn:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover);border-radius:5px}.dsh-sam-hidden-badge{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary);white-space:nowrap}.dsh-sam-delete-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-delete-btn:hover{color:var(--dsw-alias-state-error-primary,#ef4444);background:var(--dsw-alias-interactive-bg-hover-danger,rgba(236,19,19,.12));border-radius:5px}.dsh-sam-batch-delete-btn{color:var(--dsw-alias-state-error-primary,#ef4444)!important}.dsh-sam-batch-delete-btn:hover{background:var(--dsw-alias-interactive-bg-hover-danger,rgba(236,19,19,.12))!important}.dsh-sam-trigger{display:inline-flex;align-items:center;gap:0;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-trigger-wrap{position:relative;display:inline-flex;align-items:center}.dsh-sam-active-float{position:fixed;top:56px;right:12px;z-index:999;box-sizing:border-box;width:min(360px,calc(100vw - 24px));max-height:calc(100vh - 24px);overflow:auto;padding:6px 8px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-2));box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-primary)}.dsh-sam-active-head{display:flex;align-items:center;justify-content:space-between;gap:6px;padding:1px 2px 5px;color:var(--dsw-alias-label-secondary);font-size:11px;font-weight:600;user-select:none}.dsh-sam-active-drag-handle{cursor:move}.dsh-sam-active-drag-handle:active{cursor:grabbing}.dsh-sam-active-actions{display:inline-flex;align-items:center;gap:6px;margin-left:auto}.dsh-sam-active-live-toggle{display:inline-flex;align-items:center;gap:3px;color:var(--dsw-alias-label-tertiary);font-size:10px;font-weight:400;cursor:pointer}.dsh-sam-active-live-toggle input{width:12px;height:12px;margin:0;accent-color:#42bd7b}.dsh-sam-active-close{border:0;background:transparent;color:var(--dsw-alias-label-tertiary);font-size:17px;line-height:17px;cursor:pointer;padding:0 2px}.dsh-sam-active-close:hover{color:var(--dsw-alias-label-primary)}.dsh-sam-active-item{padding:7px 4px;border-top:1px solid var(--dsw-alias-border-l2);cursor:pointer}.dsh-sam-active-item:hover{background:var(--dsw-alias-interactive-bg-hover);border-radius:6px}.dsh-sam-active-title{display:flex;align-items:center;gap:5px;min-width:0;font-size:12px;line-height:17px;font-weight:600}.dsh-sam-active-icon{flex:none;color:#42bd7b;font-size:12px}.dsh-sam-active-elapsed{margin-left:auto;flex:none;color:var(--dsw-alias-label-tertiary);font-size:10px;font-weight:400}.dsh-sam-stop-btn{border:0;background:transparent;color:var(--dsw-alias-label-tertiary);border-radius:5px;padding:2px 5px;font:inherit;font-size:10px;line-height:15px;white-space:nowrap;cursor:pointer}.dsh-sam-stop-btn:hover{background:var(--dsw-alias-interactive-bg-hover-danger,rgba(236,19,19,.12));color:var(--dsw-alias-state-error-primary,#ef4444)}.dsh-sam-stop-btn:disabled{opacity:.45;cursor:wait}.dsh-sam-stop-all-btn{border:0;background:transparent;color:var(--dsw-alias-state-warn-label,var(--dsw-alias-label-tertiary));border-radius:4px;padding:1px 5px;font:inherit;font-size:10px;line-height:14px;white-space:nowrap;cursor:pointer}.dsh-sam-stop-all-btn:hover{background:var(--dsw-alias-interactive-bg-hover-danger,rgba(236,19,19,.12));color:var(--dsw-alias-state-error-primary,#ef4444)}.dsh-sam-stop-all-btn:disabled{opacity:.45;cursor:wait}.dsh-sam-row-actions{display:inline-flex;align-items:center;gap:2px;white-space:nowrap}.dsh-sam-active-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dsh-sam-active-item .dsh-sam-live{margin:3px 0 3px 17px;max-height:32px;font-size:11px;line-height:15px;pointer-events:none;user-select:none}.dsh-sam-active-stats{margin-left:17px;color:var(--dsw-alias-label-tertiary);font-size:10px;line-height:15px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dsh-sam-active-model{margin-left:17px;color:var(--dsw-alias-label-tertiary);font-size:10px;line-height:15px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dsh-sam-new-agent{position:absolute;top:calc(100% + 6px);left:0;z-index:1001;max-width:280px;padding:7px 10px;border:1px solid var(--dsw-alias-interactive-label-brand);border-radius:8px;background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-2));box-shadow:var(--dsw-shadow-lv2);color:var(--dsw-alias-label-primary);font-size:12px;white-space:nowrap;pointer-events:none;transform-origin:top left;animation:dsh-sam-new-agent 5s ease-out both}@keyframes dsh-sam-new-agent{0%{opacity:0;transform:translateY(-5px) scale(.96)}8%,78%{opacity:1;transform:translateY(0) scale(1)}100%{opacity:0;transform:translateY(-3px) scale(.98)}}.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;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:hover{box-shadow:inset 0 0 0 1px var(--dsw-alias-interactive-label-brand)}.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;align-items:center;color:var(--dsw-alias-label-secondary);font-size:12px}.dsh-sam-options label{display:flex;align-items:center;gap:5px;cursor:pointer}.dsh-sam-bg-config{display:inline-flex;align-items:center;gap:6px;padding:2px 6px;border-radius:6px;background:var(--dsw-alias-interactive-bg-hover)}.dsh-sam-color-input{width:18px;height:18px;padding:0;border:0;border-radius:4px;cursor:pointer;background:transparent;vertical-align:middle}.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;align-items:center;font:inherit;font-size:12px;font-weight:600;cursor:pointer}.dsh-sam-group-head-actions{display:inline-flex;align-items:center;gap:8px}.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:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:18px;height:18px;align-self:center}.dsh-sam-marker input{margin:0;width:16px;height:16px}.dsh-sam-dot{width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-label-caption);display:inline-block;flex:none}.dsh-sam-dot.running{background:#42bd7b;box-shadow:0 0 0 3px rgba(66,189,123,.16)}.dsh-sam-dot.hidden-dot{background:transparent!important;border-radius:0!important;font-size:13px;line-height:1;width:auto;height:auto;box-shadow:none!important;display:inline-flex;align-items:center;justify-content:center}.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;box-sizing:border-box;width:100%;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-old{display:block;opacity:.48}.dsh-sam-live-latest{display:block;opacity:1;font-weight:500}.dsh-sam-live-running{color:#42bd7b}.dsh-sam-live-running .dsh-sam-live-latest{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-load-live-btn{border:0;background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer;padding:0 4px;font-size:11px;line-height:16px;border-radius:4px;display:inline-flex;align-items:center;gap:3px;margin:2px 0;opacity:.85}.dsh-sam-load-live-btn:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover);opacity:1}.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}}.dsh-sam-thinking{display:inline-flex;align-items:center;gap:5px;font-size:11px;line-height:15px;color:var(--dsw-alias-label-secondary);font-weight:500}.dsh-sam-thinking::before{content:"";width:9px;height:9px;border-radius:50%;border:2px solid transparent;border-top-color:currentColor;border-right-color:currentColor;animation:dsh-sam-spin .7s linear infinite;flex:none}@keyframes dsh-sam-spin{to{transform:rotate(360deg)}}
|
|
24
|
-
.dsh-sam-side-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:
|
|
25
|
-
.dsh-sam-side-btn:
|
|
23
|
+
.dsh-sam-filter-toggle:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.dsh-sam-hide-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;display:inline-flex;align-items:center;gap:2px}.dsh-sam-hide-btn:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover);border-radius:5px}.dsh-sam-hidden-badge{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary);white-space:nowrap}.dsh-sam-delete-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-delete-btn:hover{color:var(--dsw-alias-state-error-primary,#ef4444);background:var(--dsw-alias-interactive-bg-hover-danger,rgba(236,19,19,.12));border-radius:5px}.dsh-sam-batch-delete-btn{color:var(--dsw-alias-state-error-primary,#ef4444)!important}.dsh-sam-batch-delete-btn:hover{background:var(--dsw-alias-interactive-bg-hover-danger,rgba(236,19,19,.12))!important}.dsh-sam-trigger{display:inline-flex;align-items:center;gap:0;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-trigger-wrap{position:relative;display:inline-flex;align-items:center}.dsh-sam-active-float{position:fixed;top:56px;right:12px;z-index:999;box-sizing:border-box;width:min(360px,calc(100vw - 24px));max-height:calc(100vh - 24px);overflow:auto;padding:6px 8px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-2));background:color-mix(in srgb,var(--dsw-alias-bg-layer-3,var(--dsw-alias-bg-layer-2)) 95%,transparent);box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-primary)}.dsh-sam-active-head{display:flex;align-items:center;justify-content:space-between;gap:6px;padding:1px 2px 5px;color:var(--dsw-alias-label-secondary);font-size:11px;font-weight:600;user-select:none}.dsh-sam-active-drag-handle{cursor:move}.dsh-sam-active-drag-handle:active{cursor:grabbing}.dsh-sam-active-actions{display:inline-flex;align-items:center;gap:6px;margin-left:auto}.dsh-sam-active-live-toggle{display:inline-flex;align-items:center;gap:3px;color:var(--dsw-alias-label-tertiary);font-size:10px;font-weight:400;cursor:pointer}.dsh-sam-active-live-toggle input{width:12px;height:12px;margin:0;accent-color:#42bd7b}.dsh-sam-active-close{border:0;background:transparent;color:var(--dsw-alias-label-tertiary);font-size:17px;line-height:17px;cursor:pointer;padding:0 2px}.dsh-sam-active-close:hover{color:var(--dsw-alias-label-primary)}.dsh-sam-active-item{padding:7px 4px;border-top:1px solid var(--dsw-alias-border-l2);cursor:pointer}.dsh-sam-active-item:hover{background:var(--dsw-alias-interactive-bg-hover);border-radius:6px}.dsh-sam-active-title{display:flex;align-items:center;gap:5px;min-width:0;font-size:12px;line-height:17px;font-weight:600}.dsh-sam-active-icon{flex:none;color:#42bd7b;font-size:12px}.dsh-sam-active-elapsed{margin-left:auto;flex:none;color:var(--dsw-alias-label-tertiary);font-size:10px;font-weight:400}.dsh-sam-stop-btn{border:0;background:transparent;color:var(--dsw-alias-label-tertiary);border-radius:5px;padding:2px 5px;font:inherit;font-size:10px;line-height:15px;white-space:nowrap;cursor:pointer}.dsh-sam-stop-btn:hover{background:var(--dsw-alias-interactive-bg-hover-danger,rgba(236,19,19,.12));color:var(--dsw-alias-state-error-primary,#ef4444)}.dsh-sam-stop-btn:disabled{opacity:.45;cursor:wait}.dsh-sam-stop-all-btn{border:0;background:transparent;color:var(--dsw-alias-state-warn-label,var(--dsw-alias-label-tertiary));border-radius:4px;padding:1px 5px;font:inherit;font-size:10px;line-height:14px;white-space:nowrap;cursor:pointer}.dsh-sam-stop-all-btn:hover{background:var(--dsw-alias-interactive-bg-hover-danger,rgba(236,19,19,.12));color:var(--dsw-alias-state-error-primary,#ef4444)}.dsh-sam-stop-all-btn:disabled{opacity:.45;cursor:wait}.dsh-sam-row-actions{display:inline-flex;align-items:center;gap:2px;white-space:nowrap}.dsh-sam-active-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dsh-sam-active-item .dsh-sam-live{margin:3px 0 3px 17px;max-height:32px;font-size:11px;line-height:15px;pointer-events:none;user-select:none}.dsh-sam-active-stats{margin-left:17px;color:var(--dsw-alias-label-tertiary);font-size:10px;line-height:15px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dsh-sam-active-model{margin-left:17px;color:var(--dsw-alias-label-tertiary);font-size:10px;line-height:15px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dsh-sam-new-agent{position:absolute;top:calc(100% + 6px);left:0;z-index:1001;max-width:280px;padding:7px 10px;border:1px solid var(--dsw-alias-interactive-label-brand);border-radius:8px;background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-2));box-shadow:var(--dsw-shadow-lv2);color:var(--dsw-alias-label-primary);font-size:12px;white-space:nowrap;pointer-events:none;transform-origin:top left;animation:dsh-sam-new-agent 5s ease-out both}@keyframes dsh-sam-new-agent{0%{opacity:0;transform:translateY(-5px) scale(.96)}8%,78%{opacity:1;transform:translateY(0) scale(1)}100%{opacity:0;transform:translateY(-3px) scale(.98)}}.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;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:hover{box-shadow:inset 0 0 0 1px var(--dsw-alias-interactive-label-brand)}.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;align-items:center;color:var(--dsw-alias-label-secondary);font-size:12px}.dsh-sam-options label{display:flex;align-items:center;gap:5px;cursor:pointer}.dsh-sam-bg-config{display:inline-flex;align-items:center;gap:6px;padding:2px 6px;border-radius:6px;background:var(--dsw-alias-interactive-bg-hover)}.dsh-sam-color-input{width:18px;height:18px;padding:0;border:0;border-radius:4px;cursor:pointer;background:transparent;vertical-align:middle}.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;align-items:center;font:inherit;font-size:12px;font-weight:600;cursor:pointer}.dsh-sam-group-head-actions{display:inline-flex;align-items:center;gap:8px}.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:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:18px;height:18px;align-self:center}.dsh-sam-marker input{margin:0;width:16px;height:16px}.dsh-sam-dot{width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-label-caption);display:inline-block;flex:none}.dsh-sam-dot.running{background:#42bd7b;box-shadow:0 0 0 3px rgba(66,189,123,.16)}.dsh-sam-dot.hidden-dot{background:transparent!important;border-radius:0!important;font-size:13px;line-height:1;width:auto;height:auto;box-shadow:none!important;display:inline-flex;align-items:center;justify-content:center}.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;box-sizing:border-box;width:100%;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-old{display:block;opacity:.48}.dsh-sam-live-latest{display:block;opacity:1;font-weight:500}.dsh-sam-live-running{color:#42bd7b}.dsh-sam-live-running .dsh-sam-live-latest{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-load-live-btn{border:0;background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer;padding:0 4px;font-size:11px;line-height:16px;border-radius:4px;display:inline-flex;align-items:center;gap:3px;margin:2px 0;opacity:.85}.dsh-sam-load-live-btn:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover);opacity:1}.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}}.dsh-sam-thinking{display:inline-flex;align-items:center;gap:5px;font-size:11px;line-height:15px;color:var(--dsw-alias-label-secondary);font-weight:500}.dsh-sam-thinking::before{content:"";width:9px;height:9px;border-radius:50%;border:2px solid transparent;border-top-color:currentColor;border-right-color:currentColor;animation:dsh-sam-spin .7s linear infinite;flex:none}@keyframes dsh-sam-spin{to{transform:rotate(360deg)}}
|
|
24
|
+
.dsh-sam-side-btn{border:0;background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer;padding:4px 4px 4px 10px;margin:-4px 0 -4px -6px;font-size:12px;line-height:16px;white-space:nowrap;align-self:start;min-width:24px;display:inline-flex;align-items:center;gap:2px;position:relative}
|
|
25
|
+
.dsh-sam-side-btn::after{content:"";position:absolute;left:6px;right:0;top:4px;bottom:4px;border-radius:5px;pointer-events:none}
|
|
26
|
+
.dsh-sam-side-btn:hover:not(:disabled){color:var(--dsw-alias-label-primary)}
|
|
27
|
+
.dsh-sam-side-btn:hover:not(:disabled)::after{background:var(--dsw-alias-interactive-bg-hover)}
|
|
26
28
|
.dsh-sam-side-btn:disabled{opacity:.4;cursor:not-allowed}
|
|
27
|
-
.dsh-sam-side-btn-compact{font-size:11px;line-height:15px;padding:
|
|
29
|
+
.dsh-sam-side-btn-compact{font-size:11px;line-height:15px;padding:5px 7px 5px 8px;margin:-4px -3px -4px -4px;border-radius:5px}
|
|
30
|
+
.dsh-sam-side-btn-compact::after{left:4px;right:3px;top:4px;bottom:4px}
|
|
28
31
|
.dsh-sam-lineage-title{max-width:220px;color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;background:0 0;border:none;border-radius:12px;padding:4px 8px;font-family:inherit;font-size:12px;line-height:18px;overflow:hidden;cursor:pointer}
|
|
29
32
|
.dsh-sam-lineage-current{color:var(--dsw-alias-label-primary);cursor:default;font-weight:500}
|
|
30
33
|
`
|
|
@@ -93,8 +96,11 @@ statsLine=(tr,row)=>{const u=usageStats(tr,row),parts=[tr('stats.usage',{input:f
|
|
|
93
96
|
statsUsageTitle=(tr,row)=>{const u=usageStats(tr,row),lines=[tr('stats.titleLine'),tr('stats.f.total',{value:fmt(tr,u.total)}),tr('stats.f.billed',{value:fmt(tr,u.billed)}),tr('stats.f.uncached',{value:fmt(tr,u.uncached)}),tr('stats.f.cacheRead',{value:fmt(tr,u.cacheRead)}),tr('stats.f.cacheWrite',{value:fmt(tr,u.cacheWrite)}),tr('stats.f.output',{value:fmt(tr,u.output)}),tr('stats.f.hit',{value:u.hit!=null?`${u.hit}%`:tr('unknown')}),tr('stats.f.tps',{value:u.tps!=null?tr('stats.tps',{tps:u.tps}):tr('unknown')}),tr('stats.f.turns',{value:u.turns}),tr('stats.f.steps',{value:u.steps})];if(u.llmMs!=null)lines.push(tr('stats.f.llm',{value:durationText(u.llmMs)}));if(u.toolMs!=null)lines.push(tr('stats.f.tool',{value:durationText(u.toolMs)}));if(u.ttftMs!=null)lines.push(tr('stats.f.ttft',{value:durationText(u.ttftMs)}));return lines.join('\n')}
|
|
94
97
|
const category=(tr,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(' ')||tr('uncategorized')},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}
|
|
95
98
|
const projectionMode=(s)=>{const raw=s?.projectionValues?.subagent,identity=raw?.identity,mode=identity?identity.mode:raw?.mode;return mode==='one-shot'||mode==='continuable'?mode:undefined}
|
|
99
|
+
const knownMode=mode=>mode==='one-shot'||mode==='continuable'?mode:undefined
|
|
100
|
+
const catalogEntriesOf=(state,parentId)=>{const legacy=state?.subagentsByParent?.[parentId]?.entries;if(Array.isArray(legacy))return legacy.filter(e=>e.kind==='child').map(e=>({id:e.id,mode:knownMode(e.mode),label:e.label}));const projected=state?.byId?.[parentId]?.projectionValues?.subagentCatalog??state?.projectionsBySession?.[parentId]?.values?.subagentCatalog;return Array.isArray(projected)?projected.filter(e=>e&&e.id).map(e=>({id:e.id,mode:knownMode(e.mode),label:e.label})):[]}
|
|
101
|
+
const catalogEntryOf=(state,parentId,childId)=>catalogEntriesOf(state,parentId).find(e=>e.id===childId)
|
|
96
102
|
const childModeOf=(s,catalog)=>catalog?.mode??projectionMode(s)
|
|
97
|
-
const modeMap=
|
|
103
|
+
const modeMap=state=>{const parents=new Set();Object.keys(state?.subagentsByParent||{}).forEach(id=>parents.add(id));Object.keys(state?.byId||{}).forEach(id=>{if(state?.byId?.[id]?.projectionValues?.subagentCatalog)parents.add(id)});Object.keys(state?.projectionsBySession||{}).forEach(id=>{if(state?.projectionsBySession?.[id]?.values?.subagentCatalog)parents.add(id)});const map={};parents.forEach(parentId=>{catalogEntriesOf(state,parentId).forEach(entry=>{if(map[entry.id]===undefined)map[entry.id]=entry})});return map}, 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)}
|
|
98
104
|
const modelText=(row)=>{const sel=row?.projectionValues?.modelSelection,shown=sel?.next??sel?.lastUsed;if(!shown||!shown.provider||!shown.model)return '';return shown.reasoningEffort?`${shown.provider}/${shown.model} · ${shown.reasoningEffort}`:`${shown.provider}/${shown.model}`}
|
|
99
105
|
const modelLabel=(tr,row)=>{const text=modelText(row);return text?tr('model.label',{model:text}):tr('modelUnknown')}
|
|
100
106
|
const modelCompact=(tr,row)=>modelText(row)||tr('modelUnknown')
|
|
@@ -106,15 +112,39 @@ const asideButton=(options)=>{
|
|
|
106
112
|
const tr=options.tr
|
|
107
113
|
let address=null
|
|
108
114
|
try{address=addressOf(options.row)}catch(error){console.warn('subagent-workspace-ui: sidebar address lookup failed',error);address=null}
|
|
115
|
+
if(address===undefined)return null
|
|
109
116
|
return jsx('button',{type:'button',className:options.compact?'dsh-sam-side-btn dsh-sam-side-btn-compact':'dsh-sam-side-btn',disabled:!address,title:address?tr('openInSidebar'):tr('openInSidebarUnavailable'),'aria-label':address?tr('openInSidebar.named',{name:options.row.name}):tr('openInSidebarUnavailable'),onClick:event=>{event.stopPropagation();if(!address)return;if(typeof options.open==='function'&&options.open(address)===true){if(typeof options.onOpened==='function')options.onOpened();return}window.alert(tr('openInSidebarFailed'))},children:'◫'})
|
|
110
117
|
}
|
|
111
118
|
|
|
112
119
|
let sessionsRt=null
|
|
113
120
|
const NOOP=()=>()=>{}
|
|
114
121
|
|
|
122
|
+
const RETAIN_SOURCE='subagentWorkspaceUi'
|
|
123
|
+
const RETAIN_LIMIT=8
|
|
124
|
+
const retainOrder=['float','panel']
|
|
125
|
+
const retainWants=new Map()
|
|
126
|
+
const retainEntries=new Map()
|
|
127
|
+
const retainListeners=new Set()
|
|
128
|
+
const retainSignature=(want)=>`${want.parentId||''}|${want.mode||''}`
|
|
129
|
+
const retainTarget=(want)=>want.parentId&&want.mode?{parentSessionId:want.parentId,childSessionId:want.childId,mode:want.mode}:want.childId
|
|
130
|
+
const retainNotify=()=>{retainListeners.forEach(listener=>{try{listener()}catch(error){console.warn('subagent-workspace-ui: retain listener failed',error)}})}
|
|
131
|
+
const retainDispose=(reference)=>{if(!reference)return;try{if(typeof reference.release==='function')reference.release();else if(typeof reference.dispose==='function')reference.dispose()}catch(error){console.warn('subagent-workspace-ui: unable to release a retained session',error)}}
|
|
132
|
+
const retainBindingOf=(reference)=>{if(!reference)return null;try{const binding=reference.binding;return binding&&(binding.session||binding.eventSource)?binding:null}catch{return null}}
|
|
133
|
+
const retainDrop=(childId)=>{const entry=retainEntries.get(childId);if(!entry)return false;retainEntries.delete(childId);retainDispose(entry.reference);return true}
|
|
134
|
+
const retainAcquire=(want)=>{const runtime=sessionsRt;if(!runtime||typeof runtime.retain!=='function')return null;let reference=null;try{reference=runtime.retain(retainTarget(want),{source:RETAIN_SOURCE})}catch(error){console.warn('subagent-workspace-ui: unable to retain a live session',want.childId,error);return null}const binding=retainBindingOf(reference);if(!binding){retainDispose(reference);return null}return {reference,binding,signature:retainSignature(want)}}
|
|
135
|
+
const retainSync=()=>{const ranked=[];for(const owner of retainOrder){const group=retainWants.get(owner);if(!group)continue;const list=[...group.values()].filter(want=>want&&want.childId);list.sort((a,b)=>(b.updatedAt||0)-(a.updatedAt||0));for(const want of list)ranked.push(want)}const kept=[],seen=new Set();for(const want of ranked){if(kept.length>=RETAIN_LIMIT)break;if(seen.has(want.childId))continue;seen.add(want.childId);kept.push(want)}let changed=false;for(const childId of [...retainEntries.keys()]){const want=kept.find(item=>item.childId===childId);if(!want||retainSignature(want)!==retainEntries.get(childId)?.signature){if(retainDrop(childId))changed=true}}for(const want of kept){if(retainEntries.has(want.childId))continue;const entry=retainAcquire(want);if(entry){retainEntries.set(want.childId,entry);changed=true}}if(changed)retainNotify()}
|
|
136
|
+
const retainDeclare=(owner,wants)=>{const group=new Map();for(const want of wants)if(want&&want.childId)group.set(want.childId,want);retainWants.set(owner,group);retainSync()}
|
|
137
|
+
const retainClear=(owner)=>{if(retainWants.delete(owner))retainSync()}
|
|
138
|
+
const retainReleaseAll=()=>{retainWants.clear();let changed=false;for(const childId of [...retainEntries.keys()])if(retainDrop(childId))changed=true;if(changed)retainNotify()}
|
|
139
|
+
const liveBindingFor=(childId)=>{const entry=retainEntries.get(childId);if(entry){const own=retainBindingOf(entry.reference);if(own)return own}try{return sessionsRt?.binding?.(childId)||null}catch{return null}}
|
|
140
|
+
const retainOwned=(childId)=>retainEntries.has(childId)
|
|
141
|
+
const retainSettled=async(childId)=>{const entry=retainEntries.get(childId);if(!entry)return;try{await entry.reference.ready}catch{}}
|
|
142
|
+
const retainSubscribe=(listener)=>{retainListeners.add(listener);return()=>{retainListeners.delete(listener)}}
|
|
143
|
+
|
|
115
144
|
function LiveOutput({t:tr,parentId,childId,running,prefix=true,autoLoad=true,onLoad,live=true}){
|
|
116
|
-
const [loading,setLoading]=React.useState(false),[,bump]=React.useState(0),[lastText,setLastText]=React.useState(''),wantLive=live&&(running||autoLoad),binding=React.useMemo(()=>
|
|
117
|
-
React.useEffect(()=>{if(!binding)return;let alive=true,timer=null,scheduled=false;const notify=()=>{if(!alive||scheduled)return;scheduled=true;timer=setTimeout(()=>{scheduled=false;timer=null;if(alive)bump(v=>v+1)},100)};const setup=async()=>{try{if(parentId){const
|
|
145
|
+
const [loading,setLoading]=React.useState(false),[,bump]=React.useState(0),[retainTick,setRetainTick]=React.useState(0),[lastText,setLastText]=React.useState(''),wantLive=live&&(running||autoLoad),binding=React.useMemo(()=>wantLive?liveBindingFor(childId):null,[childId,wantLive,retainTick]),session=binding?.session||null,feed=binding?.eventSource||null
|
|
146
|
+
React.useEffect(()=>{if(!binding)return;let alive=true,timer=null,scheduled=false;const notify=()=>{if(!alive||scheduled)return;scheduled=true;timer=setTimeout(()=>{scheduled=false;timer=null;if(alive)bump(v=>v+1)},100)};const setup=async()=>{try{if(retainOwned(childId)){await retainSettled(childId)}else{if(parentId){const entry=catalogEntryOf(sessionsRt?.list?.getSnapshot?.(),parentId,childId);if(entry?.mode)session?.configureSubagent?.({parentSessionId:parentId,childSessionId:childId,mode:entry.mode})}await session?.open?.()}}catch{}finally{if(alive){setLoading(false);bump(v=>v+1)}}};setup();const source=feed||session,unsub=source?.subscribe?.(notify)||NOOP();return()=>{alive=false;if(timer)clearTimeout(timer);unsub()}},[binding,session,feed,parentId,childId])
|
|
147
|
+
React.useEffect(()=>retainSubscribe(()=>setRetainTick(v=>v+1)),[])
|
|
118
148
|
let text='',thinking=false;if(feed){const win=feed.getSnapshot?.();if(win&&win.entries){const out=liveFromEvents(tr,win.entries,running);thinking=!!out.thinking;const parts=[];if(out.activity.length)parts.push(out.activity.join('\n'));if(running&&out.streamText&&!out.thinking)parts.push(out.streamText);text=parts.length?parts.join('\n'):out.finalText}}else{const snap=session?.getSnapshot?.(),chat=snap?.chat,blocks=chat?.legacy?.partial?.blocks||[],runningCalls=chat?.legacy?.runningCalls||[],nodes=chat?.legacy?.nodes||[];const activity=[];runningCalls.slice(-2).forEach(c=>activity.push(`▶ ${c.name||tr('toolCall')}${c.argsRaw?` · ${toolDetail(c.argsRaw)}`:''}`));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(tr('contextInjection',{name:`${n.provenance?.label||n.form||'context'}`}));else if(n.kind==='tool-result')activity.push(`${n.isError?'✖':'✓'} ${n.call?.name||tr('toolCall')}${n.call?.argsRaw?` · ${toolDetail(n.call.argsRaw)}`:''}`);else activity.push(tr('command.named',{name:`${n.name||tr('command')}`}))});let streamText='';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){streamText=(b.kind==='reasoning'?'💭 ':'')+lines.slice(-2).join('\n');break}}}if(runningCalls.length)text=activity.join('\n');else if(running&&streamText)text=streamText;else 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=tr('callTool')+b.name;break}}}
|
|
119
149
|
React.useEffect(()=>{if(text)setLastText(text)},[text])
|
|
120
150
|
if(!wantLive){return jsx('button',{type:'button',className:'dsh-sam-load-live-btn',onClick:e=>{e.stopPropagation();setLoading(true);onLoad?.()},title:tr('loadHistoryTitle'),children:loading?tr('loading'):tr('loadLatest')})}
|
|
@@ -126,7 +156,7 @@ const elapsed=(tr,stamp)=>{const seconds=Math.max(0,Math.floor((Date.now()-(stam
|
|
|
126
156
|
|
|
127
157
|
const liveFromEvents=(tr,entries,running)=>{const tail=s=>{const lines=String(s||'').split('\n').map(x=>x.trim()).filter(Boolean);return lines.length?lines.slice(-2).join('\n'):''},finalOf=blocks=>{const list=Array.isArray(blocks)?blocks:[];for(let i=list.length-1;i>=0;i--){const b=list[i];if(b?.type==='text'||b?.type==='reasoning'){const t=tail(b.text);if(t)return (b.type==='reasoning'?'💭 ':'')+t}if(b?.type==='tool-call'&&b.name)return tr('callTool')+b.name}return ''};let text='',reasoning='',finalText='',hasStream=false,lastKind='text';const pending=[],done=[],context=[],byCall=new Map();for(const entry of entries){if(!entry||entry.type!=='event')continue;const e=entry.event;if(!e||typeof e.type!=='string')continue;switch(e.type){case 'assistant/chunk':{const c=e.data?.chunk;if(!c)break;if(c.type==='text-delta'){text+=c.text||'';hasStream=true;lastKind='text'}else if(c.type==='reasoning-delta'){reasoning+=c.text||'';hasStream=true;lastKind='reasoning'}else if(c.type==='tool-call-delta'&&c.id){let rec=byCall.get(c.id);if(!rec){rec={name:c.name||tr('toolCall'),args:'',complete:false,running:true};byCall.set(c.id,rec);pending.push(rec)}if(c.name)rec.name=c.name;rec.args+=c.argumentsDelta||''}else if(c.type==='block-end'&&c.block?.type==='tool-call'){let rec=byCall.get(c.block.id);if(!rec){rec={name:c.block.name||tr('toolCall'),args:'',complete:false,running:true};byCall.set(c.block.id,rec);pending.push(rec)}if(c.block.name)rec.name=c.block.name;rec.args=c.block.arguments||rec.args;rec.complete=true}break}case 'user/message':{const src=e.data?.source;if(src?.kind==='plugin'||src?.kind==='relay'||src?.kind==='recall')context.push(tr('contextInjection',{name:`${src?.form||src?.plugin||src?.kind}`}));break}case 'assistant/message':{finalText=finalOf(e.data?.message?.content);text='';reasoning='';hasStream=false;break}case 'tool/call':{let rec=byCall.get(e.data?.callId);if(!rec){rec={name:e.data?.name||tr('toolCall'),args:'',complete:false,running:true};byCall.set(e.data?.callId,rec);pending.push(rec)}if(e.data?.name)rec.name=e.data?.name;if(e.data?.arguments!=null){rec.args=e.data?.arguments;rec.complete=true}rec.running=true;break}case 'tool/result':{const rec=byCall.get(e.data?.message?.source?.callId),block=e.data?.message?.content?.[0];if(rec){rec.running=false;done.push({name:rec.name,args:rec.args,complete:rec.complete,isError:block?.isError})}break}default:break}}const open=pending.filter(r=>r.running!==false);return{pendingCount:open.length,activity:[...open.slice(-2).map(r=>`▶ ${r.name||tr('toolCall')}${r.complete&&r.args?` · ${toolDetail(r.args)}`:''}`),...done.slice(-2).map(r=>`${r.isError?'✖':'✓'} ${r.name||tr('toolCall')}${r.complete&&r.args?` · ${toolDetail(r.args)}`:''}`),...context.slice(-2)],streamText:hasStream?((lastKind==='reasoning'?'💭 ':'')+tail(lastKind==='reasoning'?reasoning:text)):'',thinking:hasStream&&lastKind==='reasoning',finalText}}
|
|
128
158
|
|
|
129
|
-
function ActiveFloat({t:tr,rows,openRow,onHide,stopAgent,stopAllAgents,stoppingIds,liveEnabled,onLiveChange,liveCap,asideAddressOf,openAside}){const [,tick]=React.useState(0),[position,setPosition]=React.useState(null),drag=React.useRef(null);React.useEffect(()=>{const timer=setInterval(()=>tick(v=>v+1),1000);return()=>clearInterval(timer)},[]);const move=e=>{if(!drag.current)return;const {offsetX,offsetY,width,height}=drag.current;setPosition({x:Math.max(8,Math.min(window.innerWidth-width-8,e.clientX-offsetX)),y:Math.max(8,Math.min(window.innerHeight-height-8,e.clientY-offsetY))})},startDrag=e=>{if(e.button!==0||e.target.closest('button,input,label'))return;const rect=e.currentTarget.parentElement.getBoundingClientRect();drag.current={offsetX:e.clientX-rect.left,offsetY:e.clientY-rect.top,width:rect.width,height:rect.height};e.currentTarget.setPointerCapture?.(e.pointerId)},stopDrag=()=>{drag.current=null},dragStyle=position?{left:position.x,top:position.y,right:'auto'}:undefined;const continuableRows=rows.filter(r=>r.mode!=='one-shot');return jsx('aside',{className:'dsh-sam-active-float',style:dragStyle,onPointerMove:move,onPointerUp:stopDrag,onPointerCancel:stopDrag,role:'status',children:[jsxs('div',{className:'dsh-sam-active-head dsh-sam-active-drag-handle',onPointerDown:startDrag,children: [jsx('span',{children:tr('activeFloat.title',{count:rows.length})}),jsxs('div',{className:'dsh-sam-active-actions',children:[continuableRows.length>0&&jsx('button',{type:'button',className:'dsh-sam-stop-all-btn',title:tr('pauseAllActive'),disabled:continuableRows.some(r=>stoppingIds.has(r.id)),onClick:e=>{e.stopPropagation();stopAllAgents(continuableRows)},children:continuableRows.some(r=>stoppingIds.has(r.id))?tr('pausing'):tr('pauseAll')}),jsx('label',{className:'dsh-sam-active-live-toggle',children:[jsx('input',{type:'checkbox',checked:liveEnabled,onChange:e=>onLiveChange(e.target.checked)}),tr('liveOutput')]}),jsx('button',{type:'button',className:'dsh-sam-active-close',onClick:e=>{e.stopPropagation();onHide()},'aria-label':tr('hideActiveFloat'),children:'×'})]})]}),rows.map((row,idx)=>jsxs('div',{className:'dsh-sam-active-item',onClick:()=>openRow(row),children:[jsxs('div',{className:'dsh-sam-active-title',children:[jsx('span',{className:'dsh-sam-active-icon',children:'🧩'}),jsx('span',{className:'dsh-sam-active-name',children:row.name}),jsx('span',{className:'dsh-sam-active-elapsed',children:elapsed(tr,row.createdAt||row.updatedAt)}),asideButton({tr,row,addressOf:asideAddressOf,open:openAside,compact:true}),row.mode!=='one-shot'&&jsx('button',{type:'button',className:'dsh-sam-stop-btn',disabled:stoppingIds.has(row.id),onClick:e=>{e.stopPropagation();stopAgent(row)},'aria-label':tr('pause.named',{name:row.name}),'title':tr('pauseSubagent'),children:stoppingIds.has(row.id)?tr('pausing'):tr('pause')})]}),liveEnabled&&(liveCap<=0||idx<liveCap)&&jsx(LiveOutput,{t:tr,parentId:row.parentId,childId:row.id,running:true,prefix:false}),jsx('div',{className:'dsh-sam-active-model',children:modeModelCompact(tr,row)}),jsx('div',{className:'dsh-sam-active-stats',title:statsUsageTitle(tr,row),children:statsLine(tr,row)})]},row.id))]})}
|
|
159
|
+
function ActiveFloat({t:tr,rows,openRow,onHide,stopAgent,stopAllAgents,stoppingIds,liveEnabled,onLiveChange,liveCap,asideAddressOf,openAside}){const [,tick]=React.useState(0),[position,setPosition]=React.useState(null),drag=React.useRef(null);React.useEffect(()=>{const timer=setInterval(()=>tick(v=>v+1),1000);return()=>clearInterval(timer)},[]);React.useEffect(()=>{retainDeclare('float',rows.filter(r=>r.running).map(r=>({childId:r.id,parentId:r.parentId,mode:r.mode,updatedAt:r.updatedAt})))},[rows]);React.useEffect(()=>()=>retainClear('float'),[]);const move=e=>{if(!drag.current)return;const {offsetX,offsetY,width,height}=drag.current;setPosition({x:Math.max(8,Math.min(window.innerWidth-width-8,e.clientX-offsetX)),y:Math.max(8,Math.min(window.innerHeight-height-8,e.clientY-offsetY))})},startDrag=e=>{if(e.button!==0||e.target.closest('button,input,label'))return;const rect=e.currentTarget.parentElement.getBoundingClientRect();drag.current={offsetX:e.clientX-rect.left,offsetY:e.clientY-rect.top,width:rect.width,height:rect.height};e.currentTarget.setPointerCapture?.(e.pointerId)},stopDrag=()=>{drag.current=null},dragStyle=position?{left:position.x,top:position.y,right:'auto'}:undefined;const continuableRows=rows.filter(r=>r.mode!=='one-shot');return jsx('aside',{className:'dsh-sam-active-float',style:dragStyle,onPointerMove:move,onPointerUp:stopDrag,onPointerCancel:stopDrag,role:'status',children:[jsxs('div',{className:'dsh-sam-active-head dsh-sam-active-drag-handle',onPointerDown:startDrag,children: [jsx('span',{children:tr('activeFloat.title',{count:rows.length})}),jsxs('div',{className:'dsh-sam-active-actions',children:[continuableRows.length>0&&jsx('button',{type:'button',className:'dsh-sam-stop-all-btn',title:tr('pauseAllActive'),disabled:continuableRows.some(r=>stoppingIds.has(r.id)),onClick:e=>{e.stopPropagation();stopAllAgents(continuableRows)},children:continuableRows.some(r=>stoppingIds.has(r.id))?tr('pausing'):tr('pauseAll')}),jsx('label',{className:'dsh-sam-active-live-toggle',children:[jsx('input',{type:'checkbox',checked:liveEnabled,onChange:e=>onLiveChange(e.target.checked)}),tr('liveOutput')]}),jsx('button',{type:'button',className:'dsh-sam-active-close',onClick:e=>{e.stopPropagation();onHide()},'aria-label':tr('hideActiveFloat'),children:'×'})]})]}),rows.map((row,idx)=>jsxs('div',{className:'dsh-sam-active-item',onClick:()=>openRow(row),children:[jsxs('div',{className:'dsh-sam-active-title',children:[jsx('span',{className:'dsh-sam-active-icon',children:'🧩'}),jsx('span',{className:'dsh-sam-active-name',children:row.name}),jsx('span',{className:'dsh-sam-active-elapsed',children:elapsed(tr,row.createdAt||row.updatedAt)}),asideButton({tr,row,addressOf:asideAddressOf,open:openAside,compact:true}),row.mode!=='one-shot'&&jsx('button',{type:'button',className:'dsh-sam-stop-btn',disabled:stoppingIds.has(row.id),onClick:e=>{e.stopPropagation();stopAgent(row)},'aria-label':tr('pause.named',{name:row.name}),'title':tr('pauseSubagent'),children:stoppingIds.has(row.id)?tr('pausing'):tr('pause')})]}),liveEnabled&&(liveCap<=0||idx<liveCap)&&jsx(LiveOutput,{t:tr,parentId:row.parentId,childId:row.id,running:true,prefix:false}),jsx('div',{className:'dsh-sam-active-model',children:modeModelCompact(tr,row)}),jsx('div',{className:'dsh-sam-active-stats',title:statsUsageTitle(tr,row),children:statsLine(tr,row)})]},row.id))]})}
|
|
130
160
|
|
|
131
161
|
let chatViewPinGen=0
|
|
132
162
|
function pinChatViewTab(sessionId){
|
|
@@ -148,12 +178,12 @@ function pinChatViewTab(sessionId){
|
|
|
148
178
|
setTimeout(tryPin,0)
|
|
149
179
|
}
|
|
150
180
|
function Manager({t:tr,useSessions,openChild,openSession,refresh,setCatalogOpen,sessionId,actions,asideAddressOf,openAside}){
|
|
151
|
-
const state=useSessions(s=>s,(a,b)=>a?.byId===b?.byId&&a?.subagentsByParent===b?.subagentsByParent&&a?.current===b?.current),[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),[lastSelectedAction,setLastSelectedAction]=React.useState(null),[oldHours,setOldHours]=React.useState(null),[newAgents,setNewAgents]=React.useState([]),[floatHidden,setFloatHidden]=React.useState(false),[stoppingIds,setStoppingIds]=React.useState(new Set),[manualLoadedIds,setManualLoadedIds]=React.useState(new Set),runningSeen=React.useRef(false),runningIdsSeen=React.useRef(new Set),noticeTimers=React.useRef(new Map)
|
|
181
|
+
const state=useSessions(s=>s,(a,b)=>a?.byId===b?.byId&&a?.subagentsByParent===b?.subagentsByParent&&a?.projectionsBySession===b?.projectionsBySession&&a?.current===b?.current),[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),[lastSelectedAction,setLastSelectedAction]=React.useState(null),[oldHours,setOldHours]=React.useState(null),[newAgents,setNewAgents]=React.useState([]),[floatHidden,setFloatHidden]=React.useState(false),[stoppingIds,setStoppingIds]=React.useState(new Set),[manualLoadedIds,setManualLoadedIds]=React.useState(new Set),runningSeen=React.useRef(false),runningIdsSeen=React.useRef(new Set),noticeTimers=React.useRef(new Map)
|
|
152
182
|
const nameOf=tab=>tab.nameKey?tr(tab.nameKey):tab.name,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
|
|
153
|
-
const modes=React.useMemo(()=>modeMap(state
|
|
183
|
+
const modes=React.useMemo(()=>modeMap(state),[state]),subagentRows=React.useMemo(()=>{const byId=state.byId;return Object.values(byId).filter(s=>s.origin==='subagent').map(s=>{const catalog=modes[s.id];return {...s,...catalog,mode:childModeOf(s,catalog),name:catalog?.label||title(s),parentCwd:byId[s.parentId]?.cwd}})},[state.byId,modes]),allRows=React.useMemo(()=>{if(!open)return [];const rawTerm=query.trim().toLocaleLowerCase(),idOnly=rawTerm.startsWith('id:'),term=idOnly?rawTerm.slice(3).trim():rawTerm;return subagentRows.filter(s=>{if(workspaceKey&&s.parentCwd!==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(tr,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(tr,a.mode).localeCompare(modeLabel(tr,b.mode)):b.updatedAt-a.updatedAt})},[open,subagentRows,query,sort,prefs,scope,currentCwd,currentId,scopeDetail,workspaceKey,selectedSession,tab,tabs,recentIds,tr])
|
|
154
184
|
const deferredSubagentRows=React.useDeferredValue(subagentRows),activeRows=React.useMemo(()=>subagentRows.filter(s=>s.running).filter(s=>(!currentCwd||s.parentCwd===currentCwd)&&(prefs.showArchived||!archived.has(s.id))&&(!prefs.hideOneShot||s.mode!=='one-shot')).sort((a,b)=>b.updatedAt-a.updatedAt),[subagentRows,currentCwd,prefs,archived])
|
|
155
|
-
const stopAgent=async row=>{if(row.mode==='one-shot'||stoppingIds.has(row.id))return;if(!window.confirm(tr('confirm.pauseNamed',{name:row.name})))return;setStoppingIds(prev=>new Set(prev).add(row.id));try{const session=
|
|
156
|
-
const stopAllAgents=async rowsToStop=>{const targetRows=rowsToStop.filter(r=>r.mode!=='one-shot'&&!stoppingIds.has(r.id));if(!targetRows.length)return;if(!window.confirm(tr('confirm.pauseSelected',{count:targetRows.length})))return;const ids=targetRows.map(r=>r.id);setStoppingIds(prev=>new Set([...prev,...ids]));try{await Promise.allSettled(targetRows.map(async row=>{try{const session=
|
|
185
|
+
const stopAgent=async row=>{if(row.mode==='one-shot'||stoppingIds.has(row.id))return;if(!window.confirm(tr('confirm.pauseNamed',{name:row.name})))return;setStoppingIds(prev=>new Set(prev).add(row.id));try{const session=liveBindingFor(row.id)?.session;if(session){if(row.parentId&&row.mode){session.configureSubagent?.({parentSessionId:row.parentId,childSessionId:row.id,mode:row.mode})}await session.open?.();await session.cancel?.();if(row.parentId){refresh(row.parentId)}}}catch(e){console.warn('Unable to stop subagent',e)}finally{setStoppingIds(prev=>{const next=new Set(prev);next.delete(row.id);return next})}}
|
|
186
|
+
const stopAllAgents=async rowsToStop=>{const targetRows=rowsToStop.filter(r=>r.mode!=='one-shot'&&!stoppingIds.has(r.id));if(!targetRows.length)return;if(!window.confirm(tr('confirm.pauseSelected',{count:targetRows.length})))return;const ids=targetRows.map(r=>r.id);setStoppingIds(prev=>new Set([...prev,...ids]));try{await Promise.allSettled(targetRows.map(async row=>{try{const session=liveBindingFor(row.id)?.session;if(session){if(row.parentId&&row.mode){session.configureSubagent?.({parentSessionId:row.parentId,childSessionId:row.id,mode:row.mode})}await session.open?.();await session.cancel?.();if(row.parentId){refresh(row.parentId)}}}catch(e){console.warn('Unable to stop subagent',row.id,e)}}))}finally{setStoppingIds(prev=>{const next=new Set(prev);ids.forEach(id=>next.delete(id));return next})}}
|
|
157
187
|
|
|
158
188
|
const deleteSubagent=async row=>{
|
|
159
189
|
if(row.running){
|
|
@@ -168,7 +198,7 @@ function Manager({t:tr,useSessions,openChild,openSession,refresh,setCatalogOpen,
|
|
|
168
198
|
})
|
|
169
199
|
const data=await resp.json()
|
|
170
200
|
if(data.ok){
|
|
171
|
-
if(row.parentId){
|
|
201
|
+
if(row.parentId){refresh(row.parentId)}
|
|
172
202
|
sessionsRt?.refresh?.()
|
|
173
203
|
setPrefs(p=>({
|
|
174
204
|
...p,
|
|
@@ -195,7 +225,7 @@ function Manager({t:tr,useSessions,openChild,openSession,refresh,setCatalogOpen,
|
|
|
195
225
|
const data=await resp.json()
|
|
196
226
|
if(data.ok){
|
|
197
227
|
const parents=new Set(items.map(x=>x.parentSessionId).filter(Boolean))
|
|
198
|
-
parents.forEach(pid=>
|
|
228
|
+
parents.forEach(pid=>refresh(pid))
|
|
199
229
|
sessionsRt?.refresh?.()
|
|
200
230
|
setPrefs(p=>({
|
|
201
231
|
...p,
|
|
@@ -218,6 +248,8 @@ function Manager({t:tr,useSessions,openChild,openSession,refresh,setCatalogOpen,
|
|
|
218
248
|
currentGroupName=grouping==='session'?tr('grouping.bySession'):grouping==='workspace'?tr('grouping.byWorkspace'):grouping==='category'?tr('grouping.byCategory'):grouping==='type'?tr('grouping.byType'):tr('grouping.none')
|
|
219
249
|
|
|
220
250
|
const tabCounts=React.useMemo(()=>{if(!open)return {};const candidates=deferredSubagentRows.filter(s=>(!workspaceKey||s.parentCwd===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},[open,deferredSubagentRows,workspaceKey,selectedSession,prefs,tabs]),rows=allRows.slice(0,limit),currentSessionRows=subagentRows.filter(r=>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,liveCap=Math.max(0,prefs.liveCap??3),runningLiveIds=liveCap>0?new Set(rows.filter(r=>r.running).slice(0,liveCap).map(r=>r.id)):null
|
|
251
|
+
const panelLiveKey=prefs.showDetails===false?'':rows.filter(r=>r.running).map(r=>`${r.id}:${r.updatedAt}`).join(','),panelLiveWants=React.useMemo(()=>panelLiveKey?rows.filter(r=>r.running).map(r=>({childId:r.id,parentId:r.parentId,mode:r.mode,updatedAt:r.updatedAt})):[],[panelLiveKey])
|
|
252
|
+
React.useEffect(()=>{retainDeclare('panel',panelLiveWants)},[panelLiveWants]);React.useEffect(()=>()=>retainClear('panel'),[])
|
|
221
253
|
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?tr('group.active'):grouping==='session'?`${workspace(tr,parent||row)} · ${title(parent||current)}`:grouping==='type'?modeLabel(tr,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?nameOf(hit):tr('group.other')})():grouping==='workspace'?workspace(tr,parent||row):grouping==='none'?tr('group.allResults'):sameSession?tr('scope.currentSession'):sameWorkspace?tr('session.currentWorkspaceDot',{name:workspace(tr,parent||row)}):(parent?`${workspace(tr,parent)} · ${title(parent)}`:tr('group.otherSessions'));(out[bucket]||=[]).push(row)}return out},[rows,grouping,state.byId,sessionId,state.current,currentCwd,tr])
|
|
222
254
|
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])
|
|
223
255
|
React.useEffect(()=>{const runningIds=subagentRows.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})},[subagentRows]);React.useEffect(()=>{if(!subagentRows.length)return;const runningIds=subagentRows.filter(r=>r.running).map(r=>r.id),current=new Set(runningIds);if(!runningSeen.current){runningSeen.current=true;runningIdsSeen.current=current;return}const fresh=runningIds.filter(id=>!runningIdsSeen.current.has(id));runningIdsSeen.current=current;if(!fresh.length)return;const names=fresh.map(id=>({id,name:subagentRows.find(r=>r.id===id)?.name})).filter(r=>r.name).slice(0,5);if(!names.length)return;setFloatHidden(false);setNewAgents(prev=>[...prev,...names].slice(-5));names.forEach(item=>{const timer=setTimeout(()=>{setNewAgents(prev=>prev.filter(x=>x.id!==item.id));noticeTimers.current.delete(item.id)},5000);if(noticeTimers.current.has(item.id))clearTimeout(noticeTimers.current.get(item.id));noticeTimers.current.set(item.id,timer)})},[subagentRows]);React.useEffect(()=>()=>{noticeTimers.current.forEach(timer=>clearTimeout(timer));noticeTimers.current.clear()},[]);const toggleSelected=(row,index,e)=>{const isShift=Boolean(e?.shiftKey&&lastSelectedIndex>=0);setSelectedIds(prev=>{const next=new Set(prev);if(isShift){const from=Math.min(lastSelectedIndex,index),to=Math.max(lastSelectedIndex,index);const shouldSelect=lastSelectedAction!==false;for(let i=from;i<=to;i++){const id=rows[i]?.id;if(!id)continue;if(shouldSelect)next.add(id);else next.delete(id)}}else{const willSelect=!next.has(row.id);if(willSelect)next.add(row.id);else next.delete(row.id);setLastSelectedAction(willSelect)};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(tr('selectHoursPrompt'),'24'));if(!Number.isFinite(hours)||hours<0)return;const cutoff=Date.now()-hours*3600000;setSelectedIds(new Set(allRows.filter(r=>r.updatedAt&&r.updatedAt<=cutoff).slice(0,1000).map(r=>r.id)));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{const opened=r.parentId&&r.mode?openChild({parentSessionId:r.parentId,childSessionId:r.id,mode:r.mode}):openSession(r.id);if(opened===false)window.alert(tr('openUnsupported'));else if(actions?.setView)actions.setView('chat');else pinChatViewTab(r.id);setOpen(false);setBatchMode(false)}catch(e){console.warn('Unable to open subagent',e);window.alert(tr('openUnsupported'))}}
|
|
@@ -243,20 +275,27 @@ const subagentChatAddressOf=address=>`${SUBAGENT_CHAT_PREFIX}${encodeURIComponen
|
|
|
243
275
|
function apply(ctx){
|
|
244
276
|
sessionsRt=ctx.sessions
|
|
245
277
|
ctx.effect(()=>ctx.locale.register(NS,{zh,en}),'subagent-workspace-ui: dictionaries')
|
|
246
|
-
|
|
247
|
-
const
|
|
248
|
-
|
|
278
|
+
ctx.effect(()=>()=>retainReleaseAll(),'subagent-workspace-ui: retained live bindings')
|
|
279
|
+
const fireAndForget=pending=>{if(pending&&typeof pending.catch==='function')pending.catch(error=>console.warn('subagent-workspace-ui: unable to refresh session projections',error))},
|
|
280
|
+
refreshCatalog=p=>{if(typeof ctx.sessions?.refreshProjections==='function'){fireAndForget(ctx.sessions.refreshProjections(p));return}if(typeof ctx.sessions?.refreshSubagents==='function')fireAndForget(ctx.sessions.refreshSubagents(p))}
|
|
281
|
+
const actions={openChild:a=>{if(typeof ctx.sessions?.openSubagent==='function'){ctx.sessions.openSubagent(a);return true}const ws=uiWorkspaceOf(ctx);if(ws){ws.openSession(a);return true}if(a?.childSessionId)return actions.openSession(a.childSessionId);return false},openSession:id=>{if(typeof ctx.sessions?.open==='function'){ctx.sessions.open(id);return true}const ws=uiWorkspaceOf(ctx);if(ws){ws.openSession(id);return true}return false},refresh:refreshCatalog,setCatalogOpen:(p,o)=>{if(typeof ctx.sessions?.setSubagentCatalogOpen==='function')ctx.sessions.setSubagentCatalogOpen(p,o)}}
|
|
282
|
+
const sidebarServices=()=>{const sidebarRight=serviceOf(ctx,'sidebarRight'),tabs=serviceOf(ctx,'sidebarRightTabs'),sessions=ctx.sessions;return typeof sidebarRight?.openResource==='function'&&typeof tabs?.candidates==='function'&&typeof sessions?.subagentAddress==='function'?{sidebarRight,tabs,sessions}:null},
|
|
283
|
+
asideAddressOf=row=>{
|
|
284
|
+
const services=sidebarServices()
|
|
285
|
+
if(!services)return undefined
|
|
249
286
|
try{
|
|
250
|
-
const address=
|
|
287
|
+
const address=services.sessions.subagentAddress(row.id)
|
|
251
288
|
if(!address||!address.parentSessionId||!address.childSessionId||!address.mode)return null
|
|
252
289
|
const value=subagentChatAddressOf(address)
|
|
253
|
-
if(tabs.candidates(value).length===0)return null
|
|
290
|
+
if(services.tabs.candidates(value).length===0)return null
|
|
254
291
|
return value
|
|
255
292
|
}catch(error){console.warn('subagent-workspace-ui: unable to resolve a sidebar address',error);return null}
|
|
256
|
-
}
|
|
257
|
-
openAside=
|
|
258
|
-
|
|
259
|
-
|
|
293
|
+
},
|
|
294
|
+
openAside=address=>{
|
|
295
|
+
const services=sidebarServices()
|
|
296
|
+
if(!services)return false
|
|
297
|
+
try{services.sidebarRight.openResource(address,{preferNewPane:true});return true}catch(error){console.warn('subagent-workspace-ui: unable to open the sidebar resource',address,error);return false}
|
|
298
|
+
},
|
|
260
299
|
face=()=>({...actions,asideAddressOf,openAside})
|
|
261
300
|
ctx.slots.inject('conversation.session.header.actions',()=>ctx.slots.register({name:'conversation.session.header.actions',id:'subagent-workspace-manager',order:100,locale:NS,inject:face},Manager))
|
|
262
301
|
ctx.slots.inject('conversation.session.header.lineage',()=>ctx.slots.register({name:'conversation.session.header.lineage',priority:-1},LineageShadow))
|