Skip to content
dsh.fish
Bundle

dsh-suggest

DeepSeek Harness dynamic plugin: renders three clickable, editable follow-up question suggestions under every agent answer

Source
shaoeric
stars
2 stars
License
MIT
Updated
Updated yesterday

Readme

# dsh-suggest

> DeepSeek Harness 插件(动态 + 正式双形态):Agent 每轮回答完成后,在答案下方生成 3 个**可点击、可编辑**的推荐后续问题。
>
> A DeepSeek Harness (DSH) plugin (dynamic and formal forms) that renders three clickable, editable follow-up question suggestions under every agent answer.

🏠 仓库:https://github.com/shaoeric/dsh-suggest

[English](#dsh-suggest-1) | [中文](#dsh-suggest)

---

## ✨ 功能特性 / Features

- **自动生成**:Agent 回答完成后,基于**最近 3 条用户消息**(含其间助手回复)自动生成 3 个推荐问题,以用户意图为锚点、紧扣最新进展;
- **一键直达**:点击推荐项 → 内容直接提交给 Agent,自动开启新一轮回答;
- **先编辑再发送**:悬停推荐项会浮现 ✏️ 编辑按钮,点击后内容填入输入框,编辑满意后自行发送;
- **flash 模型 + 关闭思考**:优先使用同 provider 的 flash 模型,请求携带 `reasoningEffort: 'off'`(DeepSeek 适配器映射为 `thinking: disabled`),生成又快又省;找不到 flash 或适配器不支持时自动回退;失败则回退会话原模型;
- **稳定兜底**:按会话+轮次缓存、生成中显示骨架占位、失败显示具体原因并可一键重试;
- **只显示最新一轮**:推荐项只在**最新一轮**回答下方显示,发起新一轮对话后自动隐藏,避免误触;同时补渲染 📎 产物行不丢失;
- **主题自适应**:全部配色使用 `--dsw-alias-*` 主题变量,自动适配明暗模式。

## 🧱 工作原理 / How It Works

| 半区 | 平台 | 职责 |
| --- | --- | --- |
| `host.js` | Host(Node) | 通过 `harness.handle('recommendations')` 暴露包内 RPC(正式版经 Typert Remote 暴露 `suggest/recommendations`);读取会话日志(≤ 收尾 seq),以最近 3 条用户消息为锚点截取上下文;优先使用同 provider 的 flash 模型(找不到则回退会话模型)调用 `llm.stream` 生成 3 个推荐(关闭思考模式),带缓存与并发去重 |
| `client.js` | Client(浏览器) | 注册 `conversation.chat.turnTail` 链式插槽(`priority: -1`);渲染垂直左对齐的推荐列表;点击 → `inputActions.setDraft` + `submit()` 直发;点 ✏️ → 仅 `setDraft` 填入输入框 |

```text
回答完成 → turn-tail 节点渲染
          → 链选举命中本插件 → 组件挂载
          → host.call('recommendations', {sessionId, turn, seq})
          → Host 截取最近 3 条用户消息上下文 → flash 模型(thinking off)生成 JSON 数组
          → 返回 3 条推荐 → 渲染为可点击列表
```

## 📁 文件结构 / Files

```text
dsh-suggest/
├── host.js          # 动态插件 Host 半:推荐生成(RPC、会话读取、LLM 调用、缓存)
├── client.js        # 动态插件 Client 半:turnTail UI、点击直发、hover 编辑、产物行
├── lib/
│   ├── index.js     # 正式插件 Host 半(与 host.js 同逻辑;Typert Remote + strict manifest)
│   └── client.js    # 正式插件 Client 半(与 client.js 同逻辑;ModuleLoader bundle)
├── package.json     # 正式插件包清单(dsh.bundle.patch + dsh.client)
├── cordis.patch.yml # 正式插件插入 host composition 的 patch 层
├── dsh.plugin.json  # 正式插件清单
├── LICENSE          # MIT License
└── README.md
```

## 🚀 安装与使用 / Install & Run

本插件提供两种安装方式:**正式插件(从仓库安装,持久化,推荐)**与**动态插件(临时,进程重启即失效)**。两套代码功能一致:`lib/` 是正式插件形态,`host.js` / `client.js` 是其动态源。

### 方式一:正式插件(从仓库安装,持久化,推荐)

仓库根目录本身就是一个标准 Harness web 插件包(声明了 `dsh.bundle.patch` 与 `dsh.client`)。把仓库克隆到本地后,用 `dsh plugin` 直接装进 web profile,跨重启常驻。

**从仓库安装:**

```sh
git clone https://github.com/shaoeric/dsh-suggest
cd dsh-suggest
dsh plugin --profile web add file:$(pwd)      # Linux/macOS
# Windows(PowerShell)换成仓库绝对路径,例如:
dsh plugin --profile web add file:C:/path/to/dsh-suggest
```

> `file:` 后面接的是**仓库根目录的绝对路径**——这个仓库根目录本身就是插件包(pnpm 只打包 `files` 字段列出的文件)。

`dsh plugin add` 会代理到 profile 目录里的 pnpm,并自动把包登记进 profile 的 `dependencies` 与 `dsh.profile.bundles`(因为包声明了 `dsh.bundle`)。装完重启生效:

1. 在运行 `dsh web` 的终端 Ctrl+C 停掉服务器;
2. 重新运行 `dsh web`;
3. 刷新浏览器页面。

重启后:Host 半随进程启动挂载(Typert Remote 端点 `suggest/recommendations`),浏览器半由 Web 服务器在 `/plugins/dsh-suggest/client.js` 提供给每个页面。

**更新(改了 `lib/` 或清单后):**

> ⚠️ `file:` 依赖缓存安装时的快照,直接重跑 `add` 不会刷新内容;先 remove 再 add。

```sh
dsh plugin --profile web remove dsh-suggest
dsh plugin --profile web add file:/绝对路径/dsh-suggest
# 然后重启 dsh web
```

**卸载:**

```sh
dsh plugin --profile web remove dsh-suggest
# 然后重启 dsh web
```

**正式版与动态版的实现差异:**

| | 动态版(`host.js` / `client.js`) | 正式版(`lib/`) |
| --- | --- | --- |
| 包内 RPC | `harness.handle` / `host.call`(动态 runner 注入) | Typert Remote:`suggest/recommendations` 端点 + strict manifest,经 api-gateway 走 `/api` |
| 服务访问 | `ctx.get('llm')` 等(动态 sandbox 门面) | 相同(正式 Cordis `ctx.get` 语义一致) |
| 客户端环境 | runner 闭包注入的 `React` / `host` / `styles` 全局 | `require('react')` + `ctx.remote.$mount` + `<style>` DOM 注入 |

### 方式二:动态插件(临时)

需要 DSH 的 Cordis 插件能力(「创造模式」agent preset)。

1. 在 DSH Web GUI 中用「创造模式」preset 打开一个会话;
2. 让 Agent 安装本插件(把仓库交给 Agent),或手动操作:
   - 用 `cordis_define` 创建插件:`code.host` 粘贴 `host.js` 内容,`code.client` 粘贴 `client.js` 内容;
   - 用 `cordis_run` 激活返回的 `pluginId` / `packageId`;
3. 首次运行需在 GUI 中**批准**插件(单勾授权当前版本,双勾授权后续更新);
4. 之后每一轮回答结束,答案下方即出现 3 个推荐问题。

> ⚠️ **生命周期说明**:动态插件是进程内临时定义,**DSH 进程重启后即丢失**,需重新 define + run。如需跨重启持久存在,用上面的方式一(正式插件)。

## 🧰 依赖的能力 / Dependencies

- Host 服务:`llm`、`sessions`(可选 `sessionQuery`、`agentDefaultModel` 作为回退);正式版另需 `typert`(注册 Remote manifest)
- Client 内置:动态版 `React`、`host`、`styles`;正式版 `remote`(api-gateway client)、`slots`(client-runtime)与 `react` 模块
- 客户端插槽:`conversation.chat.turnTail`(由 `dsh-client-ui-conversation` 声明)
- 标准 Props:`sessionId`、`inputActions`、`useSession`(客户端会话标准套件)

## 🛠️ 常见问题 / Troubleshooting

| 现象 | 处理 |
| --- | --- |
| 显示 `⚠️ 推荐生成失败 + 原因` | 按提示点击**重试**;错误文字含 `kind` 与错误码,可用于进一步定位 |
| 推荐项不出现 | 确认插件已批准并处于 running;刷新页面后查看历史轮次 |
| 希望恢复水平布局 | 把 `client.js` 中推荐区容器类名从 `dshrec-x42-list` 改回 `dshrec-x42-section`,并删除 list 相关 CSS |

## 🏷️ GitHub Topics / 仓库标签

建议在仓库 Settings → Topics 中设置:

```text
dsh  dsh-plugin  deepseek  deepseek-harness
```

同时推荐补上:`deepseek-api`、`cordis`(可选)。

## 🔍 技术参考 / Where the slot dispatch lives

`conversation.chat.turnTail` 是 DeepSeek Harness 内置的链式(chain)插槽,其声明与分发逻辑位于 DSH 源码([deepseek-ai/DeepSeek-Harness](https://github.com/deepseek-ai/DeepSeek-Harness))的三个包中:

| 逻辑 | 包 | 源文件 |
| --- | --- | --- |
| 插槽声明与渲染入口:`turn-tail` 节点注册(`key: 'turn-tail'`,children 声明 `conversation.chat.turnTail` chain)并调用 `renderSlotChain(..., { turn, seq, openFile })` | `@deepseek-ai/dsh-client-ui-conversation` | `src/client/chat/register-node-renderers.ts`、`src/client/chat/TurnTailNodeView.tsx` |
| 链式选举分发:按 `priority` 升序遍历条目,运行 `entry.select(ownerProps)`,**首个非 `null` 返回值胜出**并以 `matched` 属性挂载;全部返回 `null` 则走 owner fallback | `@deepseek-ai/dsh-client-web-react` | `src/scoped-slots.tsx`(`SlotOutlet` / `renderOutletContent` 的 `spec.kind === 'chain'` 分支) |
| 条目注册与优先级排序:`SlotCore.register` 对 `rec.entries` 按 `priority` 升序排序;chain 注册必须提供 `select` | `@deepseek-ai/dsh-client-ui-slots` | `src/index.ts`(`SlotCore.register`,约 121–123 行的 `next.sort`) |

本插件即依赖这套机制:`select` 无条件认领(`priority: -1`,先于内置的产物行条目 `x6`),组件内自行补渲染产物行,因此推荐项出现在每个完成的回答下方。

## 📄 License

[MIT](./LICENSE) © 2025 shaoeric

Install

dsh plugin --profile web add github:shaoeric/dsh-suggest

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source