feat(subagent): open a child's chat in the native right sidebar
CI / ci (pull_request) Canceled after 0s
CI / ci-windows (pull_request) Canceled after 0s
CI / plugin-mount (pull_request) Canceled after 0s

The host's own catalog puts an "open beside" action on every non-current
child row; this port had never wired it. Add it: the row grows a hover
button that opens the child's chat as a `subagentchat` resource in the
native right Sidebar, so a user can watch a child work without giving up
the pane he is in (the in-pane jump stays the plain row click).

- src/client/native/subagent-chat.ts: the resource-address codec, copied
  from @deepseek-ai/dsh-client-ui-subagent rather than imported (the
  package exports no helpers and the plugin must not reach into it).
- native/surface.ts: carry `kind` / `preferNewPane` on an open.
- SubagentView: `openChildAside` via `ctx.get?.('sidebarRight')` — an
  optional probe, since a fake ctx without `get` otherwise crashes the
  whole render (9 specs caught it) — threaded through all three
  CatalogRows call sites.
- locales: `subagentOpenAside` in zh/en plus all 20 third-language
  dictionaries (tests/locales.spec.ts enforces key-set parity).
- docs: the conventions page now documents the resource-open recipe.

Gates: typecheck / lint / build / test 132 files 1390 passed /
check:consumer-types, plus the repo's runtime-contract symbol check.
This commit is contained in:
figmar
2026-10-14 09:32:16 +08:00
parent 35d33c7f65
commit 6b4a29ba24
27 changed files with 220 additions and 7 deletions
+1 -1
View File
@@ -124,7 +124,7 @@ const catalogActions = (_parentSessionId) => ({
⇒ **缺口 2/3 的正确诉求不是「补 API」,而是「文档化这两条官方路径」**:
① 切到子会话 = `sessions.open(childSessionId)`(等价于官方的 `openSession(target)`);
② 让宿主 hydrate 某个 parent 的 catalog = `sessions.refreshProjections(parentId)`(**唯一**的 kick,旧名 `refreshSubagents`/`setSubagentCatalogOpen` 已删除且不报错)。
另外官方还有一条我们**未接入**的能力:把子会话聊天作为原生右侧栏资源打开(`sidebarRight.openResource(..., {kind:'subagentchat'})`),建议一并文档化。
另外官方还有一条能力:把子会话聊天作为原生右侧栏资源打开(`sidebarRight.openResource(..., {kind:'subagentchat', preferNewPane:true})`)——**本插件已按官方形式接入**(子代理行上的「在侧栏打开」按钮),同样建议文档化。
---
+17
View File
@@ -54,6 +54,23 @@ sed -n '945,1005p' "$A/dsh-client-ui-subagent/lib/client.js"
```
`dsh-client-ui-subagent` 是**宿主自己的子代理 UI**,是找「官方正确姿势」的最权威样板。
## 二·补、把「另一个会话的聊天」开到侧栏(官方做法)
不想切走主面板、又想盯着某个子会话时,用官方资源式打开(宿主自己的子代理 UI 就是这么做的):
```ts
import { SUBAGENT_CHAT_KIND, subagentChatAddress } from './native/subagent-chat.ts' // 本插件提供的编解码
const column = (ctx as { get?: (n: string) => unknown }).get?.('sidebarRight') as
{ openResource?: (a: string, o?: { kind?: string; preferNewPane?: boolean }) => void } | undefined
column?.openResource?.(subagentChatAddress(address), { kind: SUBAGENT_CHAT_KIND, preferNewPane: true })
```
- 地址格式(必须逐字节一致,否则宿主解析不到):`dsh-resource://subagentchat/session/<encodeURIComponent(childSessionId)>?parent=<parentSessionId>&mode=<mode>`
- `kind: 'subagentchat'` + `preferNewPane: true` 是官方那两个参数,别省。
- 读取服务必须**可选探测**(`ctx.get?.()`):测试里的假 ctx 没有 `get`,直接 `ctx.get(...)` 会在渲染期崩掉整棵子树(我们踩过:9 个渲染测试全挂)。
- 交互:官方只在「该行不是当前会话」时显示按钮,并给 `title` + `aria-label` 同一份文案。
## 三、注册/生命周期
- 服务注册必须等服务、不能等槽声明:`ctx.inject(['sidebarRightTabs'], cb)` 驱动类型注册(槽声明早于服务 provide,按槽注册会静默什么都不注册且不重试);槽只用来挂 tab 体。