# dsh-plugins

> 为 DeepSeek Harness 增加会话管理工具与侧边聊天：列出/读取/创建/发送会话，并能开一个挂载在主会话旁、保持可继续对话的子会话面板。

## Metadata

- Author: [@Ephemeral-AI-Lab](https://github.com/Ephemeral-AI-Lab)
- Repo: <https://github.com/Ephemeral-AI-Lab/dsh-plugins.git>
- GitHub: [Ephemeral-AI-Lab/dsh-plugins](https://github.com/Ephemeral-AI-Lab/dsh-plugins)
- Stars: 44
- Language: Python
- License: [MIT](https://spdx.org/licenses/MIT.html)
- Topics: `dsh-plugin`, `dsh-plugin-market`, `dsh-plugins`
- Forks: 4
- Open Issues: 1
- Last push: 2026-08-20T20:04:56.000Z
- Added: 2026-08-20T00:00:00.000Z

## Install

```bash
dsh plugin --profile web add github:Ephemeral-AI-Lab/dsh-plugins/sessions
```

## Wiki

> 落地页对应的仓库子路径是 `sessions`，仓库中的承载目录也叫 `sessions`，npm 包名 `dsh-sessions`（`package.json:3`），是 `Ephemeral-AI-Lab/dsh-plugins` monorepo 下的子包之一。本百科聚焦 `sessions` 路径对应的能力。

## 一句话定位
为 DeepSeek Harness 增加"会话管理"与"侧边聊天"能力：让 agent 和用户能够列出历史会话、按窗口读取对话内容、创建新会话、把消息投给已有会话（steer 或 followup），以及在不离开主会话的情况下开一个挂载在旁的、可继续对话的子会话面板。

## 核心能力
- 列出最近会话或查看单个会话状态：`session_status({ session_id?, recent_n? })` 默认按最近更新时间倒序返回 50 条，状态分 running / idle / cold / missing（`src/service.ts:22-60`、`SPEC §2 session_status`）。
- 按窗口读取会话历史：`session_read({ session_id, offset?, limit? })` 只读、不恢复、不重启生成，跳过 token 流、生命周期事件等 trace-only 数据（`src/service.ts:85-102`）。
- 创建新会话并排队初始 prompt：`session_create({ prompt, preset?, model?, cwd? })` 返回新 session_id 和 `status: "queued"`，不等待生成结束（`src/creation-service.ts:26-68`）。
- 向已有会话投递消息：`session_send({ session_id, message, mode? })` 支持 steer（唤醒/插入下一个步骤）和 followup（排队成下一轮），冷会话会被本插件恢复后投消息（`src/send-service.ts:18-61`）。
- 开启一个真实可继续的子会话：`session_open_sidechat({ prompt })` 返回 subagent_id（不是另造的 ID），复用 DSH 现有的 send_message 继续对话（`src/sidechat/sidechat-service.ts:29-49`、`SIDE_CHAT_SPEC §2`）。
- 等价的用户命令 `/sessions` 加 Web 可视化：命令覆盖 status / read / create / send / sidechat 五个动作（`/sessions create` 支持 `--preset/--provider/--model/--effort/--cwd` 与 JSON 两种形式，`src/commands.ts:11,72-118,212-325`）；Web 端把侧边聊天渲染成标签化、状态/常驻分离、关闭再打开恢复标签的独立 panel，并把 `/sessions` 命令结果以浮层 popup 形式呈现（`src/ui/SideChatPanel.tsx`、`src/ui/index.ts:42-91`）。

## 技术实现
- **语言**: TypeScript（`tsc -p tsconfig.json` 编译到 `lib/`，`package.json:11` 声明 `"type": "module"`）
- **关键依赖**:
  - `@deepseek-ai/cordis`（>=4.0.0）：宿主插件框架，`apply(ctx)` 走标准 Cordis effect
  - `@deepseek-ai/dsh-tools` / `@deepseek-ai/dsh-commands`：用 `defineTool` 注册五个工具、用 `ctx.commands.register` 注册 `/sessions` 命令
  - `@deepseek-ai/dsh-agent` + `@deepseek-ai/dsh-subagent` + `@deepseek-ai/dsh-session-persistence` + `@deepseek-ai/dsh-session-query` + `@deepseek-ai/dsh-workspace` + `@deepseek-ai/dsh-llm`：分别负责创建/恢复 Agent、subagent 启动、会话存储/恢复、标题快照、workspace 解析、消息构造
  - `@deepseek-ai/dsh-client-ui-*` + `@deepseek-ai/dsh-client-runtime` + `react`（>=18.2.0）：Web 端把侧边聊天 panel 注入 `shell.overlay` 槽位、命令结果浮层注入 `conversation.input.overlay` 槽位
- **架构模式**: 双端 Cordis 插件。Server 端 `apply(ctx)` 在 `sessions/src/index.ts:15-29` 注入 `tools / commands / agents / subagents / llm / sessionPersistence / sessionQuery / workspaceRegistry` 八个服务，组合四个 Service（`SessionsService`、`SessionCreationService`、`SessionSendService`、`SideChatService`）并通过 `registerSessionTools` + `registerSessionsCommand` 暴露能力；Client 端 `apply(ctx)` 在 `src/ui/index.ts:18-102` 监听 `command/executed` 事件、把侧边聊天注入 `shell.overlay` 槽位、把 `/sessions` 命令的瞬态结果用 `conversation.input.overlay` 槽位渲染成浮层 popup。`cordis.patch.yml` 给宿主注入一个名为 `dsh-sessions` 的 bundle patch，让宿主在加载时挂载这个插件。
- **入口文件**: `sessions/src/index.ts`（服务端）+ `sessions/src/ui/index.ts`（客户端），均通过 `package.json#dsh` 字段 `bundle.patch` + `client.inject` 注入 DSH。

## 适用场景
当用户希望 agent 把"管多个会话"这件事做得正式一些——比如在长项目里随时跳回 3 天前的会话读上下文、临时拉一个并行 agent 出去查资料而不打断当前主任务、在不离开当前聊天窗口的情况下并排看两个子任务的进度——这个插件提供标准化的工具/命令和 Web 面板，让"会话列表、读取、创建、发送、并行子会话"成为 agent 可以直接调用的能力。

## 前置依赖与兼容性
| 依赖 | 最低版本 | 说明 |
|---|---|---|
| `@deepseek-ai/dsh-tools` | `>=0.1.0-rc.5` | peerDependencies，注册五个 agent 工具依赖 |
| `@deepseek-ai/dsh-commands` | `>=0.1.0-rc.5` | peerDependencies，注册 `/sessions` 命令依赖 |
| `@deepseek-ai/dsh-agent` | `>=0.1.0-rc.5` | peerDependencies，`SessionCreationService` / `SessionSendService` 直接持有 `Agent` |
| `@deepseek-ai/dsh-subagent` | `>=0.1.0-rc.7` | peerDependencies，侧边聊天走 `ctx.subagents.startContinuable` |
| `@deepseek-ai/dsh-session-persistence` | `>=0.1.0-rc.5` | peerDependencies，`session_status` 列举冷会话、`session_send` 恢复冷会话都依赖 |
| `@deepseek-ai/dsh-session-query` | `>=0.1.0-rc.5` | peerDependencies，标题快照通过 `readTitleSnapshots` 读 |
| `@deepseek-ai/dsh-workspace` | `>=0.1.0-rc.7` | peerDependencies，`cwd` 通过 `workspaceRegistry.resolveByPath` 解析 |
| `@deepseek-ai/dsh-agent-presets` | `>=0.1.0-rc.7` | peerDependencies，`session_create` 解析 preset 依赖 |
| `@deepseek-ai/dsh-agent-default-model` | `>=0.1.0-rc.7` | peerDependencies，根调用无显式 model 时回落部署默认依赖 |
| `@deepseek-ai/dsh-llm` | `>=0.1.0-rc.5` | peerDependencies，`createUserMessage` + `ReasoningEffortId` 来源 |
| `@deepseek-ai/dsh-client-runtime` + `@deepseek-ai/dsh-client-ui-commands` + `@deepseek-ai/dsh-client-ui-conversation` + `@deepseek-ai/dsh-client-ui-primitives` + `@deepseek-ai/dsh-client-ui-tool` + `@deepseek-ai/dsh-client-ui-slots` + `@deepseek-ai/dsh-api-remotes` + `@deepseek-ai/dsh-client-connection` | 各 `>=0.1.0-rc.5` | peerDependencies，Web 端侧边聊天 panel 与浮层 popup 必需；`package.json:19-26` 列入 `dsh.client.inject`，缺一加载不到 Web UI |
| `@deepseek-ai/cordis` | `>=4.0.0` | peerDependencies，宿主插件框架 |
| `react` | `>=18.2.0` | peerDependencies，侧边聊天 panel 是 React 组件 |
| 平台 | DSH Web | `package.json:27` 显式声明 `dsh.client.platform: "web"`，命令行/桌面端未声明 |
| Node.js | 未声明 | package.json 没有 `engines` 字段；只有 devDependencies 里有 `@types/node ^22.20.0` |
| 原生模块 | 无 | 全部是纯 JS 依赖，无 node-gyp 构建项；插件本身只用到 `node:crypto` / `node:fs/promises` / `node:path` 这些 Node 内置模块 |

## 安装方式
```bash
dsh plugin --profile web add github:Ephemeral-AI-Lab/dsh-plugins/sessions
```

## 配置项
本插件无需额外配置。安装后重启 DSH 并新建会话即可使用，会话相关参数（prompt、session_id、cwd、model、preset、reasoningEffort、recent_n、offset、limit、mode、subagent_id 等）全部在运行时通过工具调用或 `/sessions` 命令传递，源码中无 `.env` / settings 配置文件读取逻辑（grep 全文未发现 `process.env` / `config.get` / `Schema` 等运行时配置入口）。

唯一会被校验的字段是工具/命令入参的硬约束：session_id/message/prompt/provider/model/preset/reasoningEffort/cwd 都必须是非空字符串、recent_n/offset/limit 必须是正整数、limit 上限 200（`src/service.ts:16,134-151`、`src/creation-service.ts:267-274`、`src/sidechat/sidechat-service.ts:79-83`），这些是入参校验，不算可配置项。

## 常见问题
**Q: 装完之后怎么用？**

A: 重启 DSH 并新建会话即可。Agent 可在自然语言里调用 `session_status` / `session_read` / `session_create` / `session_send` / `session_open_sidechat` 五个工具；用户也可直接在输入框打 `/sessions status`、 `/sessions read <ID>`、 `/sessions create <prompt>`、 `/sessions send <ID> <msg>`、 `/sessions sidechat "PROMPT"` 这几条命令，操作的是同一套后端（`src/commands.ts:21-70`）。无需新建 preset 或开关任何宿主配置。

**Q: `session_create` 会等模型生成完才返回吗？**

A: 不会。`README §5` 与 `SPEC §2 session_create` 都明确说明它只创建新会话并把初始 prompt 投到收件箱就立即返回，返回的 `status: "queued"` 表示"已入队、未完成"；想看生成结果请用 `session_status` 查当前状态，或 `session_read` 读窗口消息。`followup()` 在失败时会回滚 handle 并重新抛错（`src/creation-service.ts:52-60`），所以要么拿到 session_id 要么抛错，不会留下半成品。

**Q: `session_send` 的 steer 和 followup 有什么区别？**

A: `steer`（默认）会唤醒空闲 agent，并对运行中 agent 投到最近一个步骤边界；`followup` 则排队成普通的下一轮（`src/send-service.ts:50-51`、 `SPEC §2 session_send`）。两者都不会打断 agent 当前正在跑的步骤，返回的 `message_id` 只是收件箱里的接受凭证，不代表模型已处理完。

**Q: 侧边聊天 `/ sessions sidechat` 跟 `session_create` 是一回事吗？**

A: 不是。`session_open_sidechat` 走 Harness 自带的 continuable subagent 服务（`ctx.subagents.startContinuable`，`src/sidechat/sidechat-service.ts:33-49`），返回的是真实可继续的 `subagent_id`，跟 `list_agents({ scope: "children" })` 看到的子 agent 是同一套；`session_create` 返回的是新的顶层 `session_id`。侧边聊天在 Web 端有专门的标签化 panel（`src/ui/SideChatPanel.tsx`），关闭再打开标签保留，主会话不会被导航走；普通 `session_create` 出来的子会话没有这个 panel。

**Q: 怎么选择 `session_create` 的模型或工作目录？**

A: 在创建时显式传 `model: { provider, model, reasoningEffort? }`、`preset`、`cwd` 即可。模型解析顺序：显式 > 调用方 agent 路由 > 部署默认；preset 解析顺序：显式 > 调用方 preset > 部署默认；cwd 缺省时子会话继承调用方 cwd，根调用则不绑工作区（`SPEC §2 session_create` 的解析表）。`cwd` 必须是已存在的绝对目录，会被 `realpath` canonicalize，否则抛 `cwd must be an absolute path` / `cwd 'X' is not a directory`（`src/creation-service.ts:267-274`）。

**Q: `session_read` 能读整段历史吗？**

A: 不能，是有界读取。`limit` 默认且上限 200 条 message block（`src/service.ts:16 READ_SESSION_LIMIT = 200`、`src/service.ts:144` 校验），`offset` 是 1-based，超出会被 `offset X is out of range` 拒掉。读冷会话走 `sessionPersistence.inspect` 不恢复、不重启生成（`src/service.ts:88-90`）；只投影会话规范消息块，跳过原始 stream chunk、token delta、lifecycle 等 trace-only 事件（`src/service.ts:153-162`、`SPEC §2 session_read`）。

**Q: 跟 `dsh-loop` 是什么关系？**

A: 职责互补。`dsh-sessions` 拥有跨会话的发现/读取/创建/发送（`SPEC §1`）；`dsh-loop` 拥有当前会话内的循环闹钟（详见 `_wiki/Ephemeral-AI-Lab_dsh-plugins_loop.md`）。`dsh-loop` 用本插件的会话列表能力填它面板上的"已有会话"选项（`SPEC §5`），两者不会重复实现同一份会话状态。

**Q: 怎么卸载？**

A: 用 `dsh plugin --profile web remove` 对应的安装源即可。卸载后这五个工具名（`session_status` / `session_read` / `session_create` / `session_send` / `session_open_sidechat`）和 `/sessions` 命令从新会话的工具表中消失（`src/index.ts:23-28` 提供的 dispose 会撤销注册）；已创建/已发送的会话和侧边聊天仍保留在 DSH 会话存储里，不会被删除。

## 上手难度
**入门** — 装好就能用，没有配置文件、没有原生模块、没有特殊 preset 要建；最快 30 秒就能在输入框打 `/sessions status` 看到最近会话列表，或 `/sessions sidechat "帮我查一下"` 开一个侧边聊天面板。

## 已知问题与限制
- v1 没有"会话修改/删除/重命名"工具：`session_status` 是只读的，`session_send` 只投递消息，`session_create` 只创建新会话，没有提供 delete / rename / archive 这一类操作（`README §5` 与 `SPEC §1` 把"create + send + inspect"明确列为职责边界）。
- `session_create` 不等待模型生成结束：返回 `status: "queued"` 后调用方需要自己用 `session_status` 轮询或 `session_read` 拉窗口（`README §5` / `SPEC §2 session_create`）；想在同一次工具调用里拿到完整回复做不到。
- `session_send` 不会创建新会话：传入一个不存在的 `session_id` 会抛 `missing`，不会自动建（`SPEC §2 session_send`）；冷会话会被本插件恢复（`src/send-service.ts:30-42`），但不会补建不存在的会话。
- `session_read` 窗口上限 200：`limit` 超过 `READ_SESSION_LIMIT = 200` 直接抛 `limit must be less than or equal to 200`（`src/service.ts:144`），要分段读只能多次调用。
- `session_open_sidechat` 必须有 calling agent：在没有当前 agent 的工具调用上下文里会抛 `session_open_sidechat requires a calling agent`（`src/sidechat/sidechat-service.ts:31`）；这是因为它要复用 subagent 框架的 parent 关系。
- 侧边聊天仅 Web 端可见：底层 `session_open_sidechat` 工具在命令端也能跑，但没有 panel，只返回 JSON；UI 端的标签化和状态/常驻分离体验是 DSH Web 专属（`src/ui/SideChatPanel.tsx`、`package.json:27 dsh.client.platform: "web"`）。
- 命令行/桌面端是否可用未声明：`package.json:27` 只声明 `dsh.client.platform: "web"`，CLI / TUI / Desktop bundle 是否会自动挂载这个插件源码中未明确（同样适用于 `dsh-loop`，见 `_wiki/..._loop.md` 的同条限制）。
- 不修改 DeepSeek Harness 宿主代码，全部走 Cordis effect 和 DSH 公共 API（`README §5` / `SPEC §1`）；这意味着宿主一旦变更 `dsh-session` / `dsh-subagent` / `dsh-llm` 等公开 API 形状，插件需要同步升级 peerDependencies（当前要求 `dsh-agent-presets` / `dsh-agent-default-model` / `dsh-workspace` / `dsh-subagent` 都 `>=0.1.0-rc.7`）。
- 侧边聊天 `subagent_id` 与 `session_id` 是同一类 ID：它是真实 child Session ID，跟 `list_agents({ scope: "children" })` 列出来的一致（`SIDE_CHAT_SPEC §2`），但也意味着 `session_send({ session_id: <subagent_id>, message })` 走的是普通会话投递而不是 subagent 的 `send_message`，如果你想用 send_message 的"按 subagent ID 投递"语义需要直接调宿主工具，不要被本插件的 `session_send` 替代（`README §6` 明确警告）。

---

This document is auto-generated by [deepseek-plugin.org](https://deepseek-plugin.org). HTML page: [dsh-plugins](https://deepseek-plugin.org/plugins/Ephemeral-AI-Lab/dsh-plugins/sessions)
Wiki generated by AI (model: `MiniMax-M3`)
