dsh-subagent-workspace-ui 1.7.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 CHANGED
@@ -19,7 +19,7 @@ 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. When the runtime exposes conversation snapshots, the panel shows the latest two lines of live output, recent tool calls, context injection, command status, and a gray final snapshot after completion.
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
 
@@ -36,7 +36,7 @@ Both screenshots were taken on the `1.7.0-dev` line (commit `79d9d0f`) against a
36
36
  5. **Summary row** — `Showing n/m`, show details, show hidden, and the batch-mode entry.
37
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
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.
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.
40
40
 
41
41
  ## Install in the Web profile
42
42
 
@@ -54,19 +54,21 @@ If you previously disabled `ui-subagent` manually in `$DSH_HOME/profiles/web/cor
54
54
 
55
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.
56
56
 
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 the 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`. Capability detection selects the path, so the plugin stays forward compatible. If the host publishes neither, the panel falls back to the durable summary. The UI is isolated in [`lib/client.js`](lib/client.js), so it can switch to a richer source without changing the panel interaction model.
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.
58
58
 
59
59
  ### Type and model (read-only projections)
60
60
 
61
61
  A child's type and model come from public host projections. The plugin adds no RPC and writes no state:
62
62
 
63
63
  ```text
64
- # type: one reader, two read-only rungs, resolved in this order
65
- 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
66
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)
67
69
  ctx.sessions.list.getSnapshot().byId[childId].projectionValues.subagent
68
70
  → { mode, label, seq } | null # rung 2: the child's own identity projection (pushed on the live-control stream)
69
- → displayed value = rung 1 ?? rung 2 # both silent = the existing `typeLoading` text
71
+ → displayed value = rung 1 ?? rung 1' ?? rung 2 # all silent = the existing `typeLoading` text
70
72
 
71
73
  # model: the session projection modelSelection (probed by key; unreadable = the host does not publish it)
72
74
  ctx.sessions.list.getSnapshot().byId[childId].projectionValues.modelSelection
@@ -84,13 +86,13 @@ The display rules are identical on all three surfaces and differ only in density
84
86
 
85
87
  - `reasoningEffort` is appended **only when the host supplies it** (` · high`); no placeholder is rendered otherwise.
86
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.
87
- - 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`.
88
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`).
89
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`).
90
92
 
91
93
  ## Compatibility
92
94
 
93
- v1.7.0 supports dsh **0.1.6-alpha.2** and stays backward compatible with all DeepSeek Harness versions. Opening a subagent session is capability-detected at runtime and degrades in three tiers:
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:
94
96
 
95
97
  ```text
96
98
  # dsh 0.1.2-alpha.5 .. 0.1.6-alpha.1: the session controller entry points
@@ -120,10 +122,20 @@ ctx.sessions.subagentAddress → typeof function (per-row address lookup)
120
122
  # with a readable reason instead of throwing.
121
123
  ```
122
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
+
123
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.
124
128
 
125
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.
126
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.
127
139
 
128
140
  ## v1.7.0
129
141
 
package/README.zh.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  面向 DeepSeek Harness Web 的子代理管理插件。插件在会话标题栏提供一个紧凑的 `🧩 子代理 active/total` 入口,用于搜索、筛选、分组、排序、查看和批量归档当前运行时已发现的子代理。
4
4
 
5
- 当前发布版本:**v1.7.0**
5
+ 当前发布版本:**v1.8.0**
6
6
 
7
7
  ## 主要功能
8
8
 
@@ -60,8 +60,8 @@
60
60
  4. **筛选行** —— 隐藏一次性、隐藏长期未活跃、子代理背景色(浅色/深色)与重置筛选。
61
61
  5. **汇总行** —— `显示 n/m 个`、显示详情、显示已隐藏,以及批量操作入口。
62
62
  6. **活跃子代理分组** —— 置顶且可折叠,带「一键暂停」;每行包含活跃圆点、名称、Session ID、相对活动时间,以及 `◫` 在侧边栏打开、`⏸ 暂停`、`⊘ 隐藏`、`🗑 删除`。
63
- 7. **详情区** —— 直接来自宿主只读投影的 `类型:… · 模型:…`,以及官方口径的用量行 `↑ 计费输入 (未缓存…) / ↓ 输出 · 命中 n% · n tps · n 轮 · n 步`;运行中的子代理还会显示最新的实时输出,已完成的保留最终快照。
64
- 8. **活跃浮窗** —— 当前会话里运行中的子代理以紧凑浮窗常驻,每行带实时输出、用量行与暂停按钮;面板关闭且存在运行中子代理时自动出现。
63
+ 7. **详情区** —— 直接来自宿主只读投影的 `类型:… · 模型:…`,以及官方口径的用量行 `↑ 计费输入 (未缓存…) / ↓ 输出 · 命中 n% · n tps · n 轮 · n 步`;运行中的子代理都会显示最新的实时输出(不只当前选中的那一个),已完成的保留最终快照。
64
+ 8. **活跃浮窗** —— 当前会话里运行中的子代理以紧凑浮窗常驻,每行带实时输出、用量行与暂停按钮;面板关闭且存在运行中子代理时自动出现。浮窗显示期间会为其中每个运行中的子代理各自保留实时会话,与主视图当前选中哪一个无关。
65
65
 
66
66
  ### 分类标签
67
67
 
@@ -151,12 +151,22 @@
151
151
 
152
152
  实时输出具有金属光泽扫光动效;子代理结束后保留最后显示快照,并变为灰色。输出区域最多显示两行,避免持续滚动导致内容难以阅读。
153
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
+
154
163
  **兼容两个 API 代际(自动探测)**:
155
164
 
156
- - dsh **0.1.2-alpha.2**:使用 `binding.eventSource`(原始 `SessionEvent` 事件流)推导实时输出。
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` 事件流)推导实时输出。
157
167
  - dsh **0.1.1-rc.2** 及更早:回退 `session.getSnapshot().chat.legacy`(对话快照)路径。
158
168
 
159
- 按能力探测自动切换,向前兼容,老版本行为不变。
169
+ 按能力探测自动切换(不判版本号),向前兼容,老版本行为不变;两条路径都拿不到绑定时,界面回退到持久化摘要。
160
170
 
161
171
  ## 界面说明
162
172
 
@@ -199,15 +209,23 @@ http://127.0.0.1:3080
199
209
 
200
210
  ## 数据边界与兼容性
201
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
+
202
214
  插件只管理当前 DSH Web 客户端运行时已经发现的子代理目录,不伪造不存在的历史数据。首次加载以 40 条为一页;普通分页可以继续加载,批量时间选择最多扩展到 1000 条。
203
215
 
204
216
  公共 `SessionSummary` 不保证提供原始提示词或全部历史日志,因此插件不查询、不显示提示词。**类型与模型**改由宿主的公开投影提供,见下一节;实时输出按能力探测自动切换两种公开接口:
205
217
 
206
218
  ```text
207
- # dsh 0.1.2-alpha.2(新 API):绑定的事件源
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)
208
226
  sessions.binding(childId).eventSource
209
227
  → open()
210
- → getSnapshot().entries # 原始 SessionEvent:assistant/chunk、tool/call、tool/result…
228
+ → getSnapshot().entries
211
229
 
212
230
  # dsh 0.1.1-rc.2 及更早(旧 API):对话快照
213
231
  sessions.binding(childId).session
@@ -215,19 +233,21 @@ sessions.binding(childId).session
215
233
  → session.getSnapshot().chat.legacy
216
234
  ```
217
235
 
218
- 如果在当前宿主拿不到对应的实时数据,插件只能显示持久化的会话摘要和统计信息。
236
+ 如果在当前宿主拿不到对应的实时数据(没有 `retain`、也借不到绑定,或该行超出 8 个保留上限),插件只显示持久化的会话摘要和统计信息。
219
237
 
220
238
  ### 类型与模型(只读投影)
221
239
 
222
240
  子代理的「类型」与「模型 provider/id」来自宿主的公开投影,不新增任何 RPC、也不写入任何状态:
223
241
 
224
242
  ```text
225
- # 类型:同一个取值函数、两级只读来源,按此顺序回退
226
- ctx.sessions.list.getSnapshot().subagentsByParent[parentId].entries
243
+ # 类型:同一个取值函数,只读的目录来源与身份来源,按此顺序回退
244
+ ctx.sessions.list.getSnapshot().subagentsByParent[parentId].entries # dsh ..0.1.6-alpha.2
227
245
  → entry.mode # 第 1 级:已发现的目录条目(该父会话的目录被拉取后才存在)
246
+ ctx.sessions.list.getSnapshot().byId[parentId].projectionValues.subagentCatalog # dsh 0.1.7-rc.1..
247
+ → entry.mode # 第 1' 级:父会话自身的目录投影(同一批直接子代理,目录事件顺序)
228
248
  ctx.sessions.list.getSnapshot().byId[childId].projectionValues.subagent
229
249
  → { mode, label, seq } | null # 第 2 级:子会话自身的身份投影(随实时控制流下发)
230
- → 显示值 = 第 1 级 ?? 第 2 级 # 两级都拿不到 = 沿用既有 `typeLoading` 兜底文案
250
+ → 显示值 = 第 1 级 ?? 第 1' 级 ?? 第 2 级 # 全部拿不到 = 沿用既有 `typeLoading` 兜底文案
231
251
 
232
252
  # 模型:会话投影 modelSelection(按 key 能力探测,读不到即视为宿主不提供)
233
253
  ctx.sessions.list.getSnapshot().byId[childId].projectionValues.modelSelection
@@ -282,6 +302,8 @@ ctx.sessions.subagentAddress → 是函数(按行取地址)
282
302
  # 验证为空 → 该行按钮渲染为禁用态并给出可读说明,绝不抛异常给用户。
283
303
  ```
284
304
 
305
+ 两个能力面**按行、按次解析,绝不在激活时只探测一次**:dsh **0.1.7-rc.1** 在本插件 `apply` **之后**才注册 `sidebarRight`/`sidebarRightTabs`(**0.1.6-alpha.2** 在 `apply` 时已就绪),因此激活时的一次性探测会让新宿主上的整列 `◫` 消失,而旧宿主毫无异样。
306
+
285
307
  要点:
286
308
 
287
309
  - **默认点击不变**:点条目本身仍然把子代理打开到主对话区(与 v1.5.0 一致);新按钮 `stopPropagation`,既不触发默认跳转,也不会误触批量选择。
@@ -319,6 +341,16 @@ DSH_PLUGIN_DIR=.worktrees/x ./test.sh # 冒烟其它 checkout 的产物
319
341
  - UI 本地化(zh/en)由 [@Marcuss2](https://github.com/Marcuss2) 在 [PR #1](https://github.com/miuzel/dsh-subagent-ui/pull/1) 中贡献,特此致谢!
320
342
  - 子代理永久删除、会话生命周期清理及快照刷新机制的设计参考并致谢开源项目:[@heiheiha798/dsh-plugin-subagent-delete](https://github.com/heiheiha798/dsh-plugin-subagent-delete)。
321
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
+
322
354
  ## v1.7.0 发布说明
323
355
 
324
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 祖先而非几何。
package/lib/client.js CHANGED
@@ -20,7 +20,7 @@ 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)}}
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
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
25
  .dsh-sam-side-btn::after{content:"";position:absolute;left:6px;right:0;top:4px;bottom:4px;border-radius:5px;pointer-events:none}
26
26
  .dsh-sam-side-btn:hover:not(:disabled){color:var(--dsw-alias-label-primary)}
@@ -96,8 +96,11 @@ statsLine=(tr,row)=>{const u=usageStats(tr,row),parts=[tr('stats.usage',{input:f
96
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')}
97
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}
98
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)
99
102
  const childModeOf=(s,catalog)=>catalog?.mode??projectionMode(s)
100
- const modeMap=c=>Object.values(c||{}).flatMap(x=>x.entries||[]).reduce((o,e)=>{if(e.kind==='child')o[e.id]={mode:e.mode,label:e.label};return o},{}), highlight=(text,term)=>{if(!term)return text;const parts=String(text).split(new RegExp(`(${term.replace(/[.*+?^${}()|[\\]\\\\]/g,'\\\\$&')})`,'ig'));return parts.map((part,i)=>i%2?jsx('mark',{children:part},i):part)}
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)}
101
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}`}
102
105
  const modelLabel=(tr,row)=>{const text=modelText(row);return text?tr('model.label',{model:text}):tr('modelUnknown')}
103
106
  const modelCompact=(tr,row)=>modelText(row)||tr('modelUnknown')
@@ -109,15 +112,39 @@ const asideButton=(options)=>{
109
112
  const tr=options.tr
110
113
  let address=null
111
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
112
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:'◫'})
113
117
  }
114
118
 
115
119
  let sessionsRt=null
116
120
  const NOOP=()=>()=>{}
117
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
+
118
144
  function LiveOutput({t:tr,parentId,childId,running,prefix=true,autoLoad=true,onLoad,live=true}){
119
- const [loading,setLoading]=React.useState(false),[,bump]=React.useState(0),[lastText,setLastText]=React.useState(''),wantLive=live&&(running||autoLoad),binding=React.useMemo(()=>{if(!wantLive)return null;try{return sessionsRt?.binding?.(childId)||null}catch{return null}},[childId,wantLive]),session=binding?.session||null,feed=binding?.eventSource||null
120
- 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 entries=sessionsRt?.list?.getSnapshot?.().subagentsByParent?.[parentId]?.entries||[],entry=entries.find(e=>e.kind==='child'&&e.id===childId);if(entry)session?.configureSubagent?.({parentSessionId:parentId,childSessionId:childId,mode:entry.mode})}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])
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)),[])
121
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}}}
122
149
  React.useEffect(()=>{if(text)setLastText(text)},[text])
123
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')})}
@@ -129,7 +156,7 @@ const elapsed=(tr,stamp)=>{const seconds=Math.max(0,Math.floor((Date.now()-(stam
129
156
 
130
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}}
131
158
 
132
- 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))]})}
133
160
 
134
161
  let chatViewPinGen=0
135
162
  function pinChatViewTab(sessionId){
@@ -151,12 +178,12 @@ function pinChatViewTab(sessionId){
151
178
  setTimeout(tryPin,0)
152
179
  }
153
180
  function Manager({t:tr,useSessions,openChild,openSession,refresh,setCatalogOpen,sessionId,actions,asideAddressOf,openAside}){
154
- 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)
155
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
156
- const modes=React.useMemo(()=>modeMap(state.subagentsByParent),[state.subagentsByParent]),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])
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])
157
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])
158
- 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=sessionsRt?.binding?.(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){sessionsRt?.refreshSubagents?.(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})}}
159
- 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=sessionsRt?.binding?.(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){sessionsRt?.refreshSubagents?.(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})}}
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})}}
160
187
 
161
188
  const deleteSubagent=async row=>{
162
189
  if(row.running){
@@ -171,7 +198,7 @@ function Manager({t:tr,useSessions,openChild,openSession,refresh,setCatalogOpen,
171
198
  })
172
199
  const data=await resp.json()
173
200
  if(data.ok){
174
- if(row.parentId){sessionsRt?.refreshSubagents?.(row.parentId)}
201
+ if(row.parentId){refresh(row.parentId)}
175
202
  sessionsRt?.refresh?.()
176
203
  setPrefs(p=>({
177
204
  ...p,
@@ -198,7 +225,7 @@ function Manager({t:tr,useSessions,openChild,openSession,refresh,setCatalogOpen,
198
225
  const data=await resp.json()
199
226
  if(data.ok){
200
227
  const parents=new Set(items.map(x=>x.parentSessionId).filter(Boolean))
201
- parents.forEach(pid=>sessionsRt?.refreshSubagents?.(pid))
228
+ parents.forEach(pid=>refresh(pid))
202
229
  sessionsRt?.refresh?.()
203
230
  setPrefs(p=>({
204
231
  ...p,
@@ -221,6 +248,8 @@ function Manager({t:tr,useSessions,openChild,openSession,refresh,setCatalogOpen,
221
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')
222
249
 
223
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'),[])
224
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])
225
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])
226
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'))}}
@@ -246,20 +275,27 @@ const subagentChatAddressOf=address=>`${SUBAGENT_CHAT_PREFIX}${encodeURIComponen
246
275
  function apply(ctx){
247
276
  sessionsRt=ctx.sessions
248
277
  ctx.effect(()=>ctx.locale.register(NS,{zh,en}),'subagent-workspace-ui: dictionaries')
249
- 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:p=>ctx.sessions.refreshSubagents(p),setCatalogOpen:(p,o)=>ctx.sessions.setSubagentCatalogOpen(p,o)}
250
- const sidebarRight=serviceOf(ctx,'sidebarRight'),tabs=serviceOf(ctx,'sidebarRightTabs'),
251
- asideAddressOf=(sidebarRight&&typeof sidebarRight.openResource==='function'&&tabs&&typeof tabs.candidates==='function'&&typeof ctx.sessions?.subagentAddress==='function')?row=>{
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
252
286
  try{
253
- const address=ctx.sessions.subagentAddress(row.id)
287
+ const address=services.sessions.subagentAddress(row.id)
254
288
  if(!address||!address.parentSessionId||!address.childSessionId||!address.mode)return null
255
289
  const value=subagentChatAddressOf(address)
256
- if(tabs.candidates(value).length===0)return null
290
+ if(services.tabs.candidates(value).length===0)return null
257
291
  return value
258
292
  }catch(error){console.warn('subagent-workspace-ui: unable to resolve a sidebar address',error);return null}
259
- }:null,
260
- openAside=asideAddressOf?address=>{
261
- try{sidebarRight.openResource(address,{preferNewPane:true});return true}catch(error){console.warn('subagent-workspace-ui: unable to open the sidebar resource',address,error);return false}
262
- }:null,
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
+ },
263
299
  face=()=>({...actions,asideAddressOf,openAside})
264
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))
265
301
  ctx.slots.inject('conversation.session.header.lineage',()=>ctx.slots.register({name:'conversation.session.header.lineage',priority:-1},LineageShadow))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-subagent-workspace-ui",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "type": "module",
5
5
  "description": "Searchable workspace subagent manager for DeepSeek Harness Web",
6
6
  "license": "MIT",