deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
PROJECT TOPICS
PROJECT README
为 DeepSeek Harness 开发的「建议提示词」插件:每个 agent 回合完成后,通过一次有界的辅助 LLM 调用,在会话日志中写入一条建议的下一条提示词;Web 端把它渲染成输入框内部的浅色幽灵占位文字,按 Tab(默认)即可采纳进草稿(与 Claude Code 一致)。
想快速上手?直接看 使用说明(面向终端用户的操作指南)。
Read this in English · 中文文档
对于开发者 / 维护者:本仓库是单个 bundle 包(@studyzy/dsh-suggest-prompt)的权威源码,把宿主生成与浏览器渲染合并为一个可一键安装的 bundle:
| 包 | 作用 |
|---|---|
@studyzy/dsh-suggest-prompt |
宿主插件(., ./invariant, ./types):在 turn/end(reason=completed)时生成建议,发布 suggestPrompt 会话投影。浏览器插件(./client):读取投影,渲染为输入框内部的浅色幽灵占位文字(inputActions.setDraft),按配置的快捷键填入草稿。 |
一个自动「接话」助手:AI 答完后,它替你预测下一句该说什么——既省去反复输入,又不会打断你的思路。
provider / model 时继承主请求最近一次记录的路由,无需为建议单独选模型;需要时也可显式指定任意路由(例如本地 OpenAI 兼容网关)。reasoningEffort: off(DeepSeek 序列化为 thinking: disabled),不消耗推理预算;模型不支持该参数时自动去掉并重试一次。maxRecentTurns 默认为 1),中间的工具调用 / 推理过程一律不发送。Tab,可在「建议提示词」设置卡片里按实际按键录制(如 Alt+Slash、Ctrl+Enter)。每个 agent 回合完成后,建议模型会在输入框里以浅色幽灵占位文字的形式显示一条预测,按 Tab 即可采纳进草稿:

^22.19 或 >=24、pnpm。conversation.input.overlay 槽位与 inputActions.setDraft(deepseek-harness 的标准 web 输入机均已提供)。本插件是一个单包 bundle:仓库根 @studyzy/dsh-suggest-prompt 声明了 dsh.bundle(自带 cordis.patch.yml),因此用 dsh plugin add 指向 GitHub 仓库即可安装,装完自动成为 profile 的一个 bundle 层,无需手动改配置文件。
# 从 GitHub 安装(推荐)
dsh plugin --profile web add git@github.com:studyzy/dsh-suggest-prompt.git
# 或 HTTPS
dsh plugin --profile web add https://github.com/studyzy/dsh-suggest-prompt.git
装完后重启正在运行的 dsh web 服务即可。安装后 profile 层叠顺序变为 dsh-base → dsh-web-app → @studyzy/dsh-suggest-prompt。
卸载:
dsh plugin --profile web remove @studyzy/dsh-suggest-prompt
git 安装的 pnpm ≥10 提示:git 托管的插件在安装时通过
prepare脚本构建,pnpm ≥10 会拦截该脚本直到放行。若add报错,把 pnpm 打印的包键加进~/.dsh/profiles/web/pnpm-workspace.yaml的allowBuilds,再重跑add。
dsh plugin --profile web add /path/to/dsh-suggest-prompt
dsh plugin --profile web add @studyzy/dsh-suggest-prompt
说明:无论哪种来源,装完都是同一个 bundle 层。日常建议模型的 provider / model 通过 WebUI 设置卡片配置(见下「配置」),不需要在安装时手动指定。
配置分两层:日常的路由配置走界面,一次性的资源上限在 bundle 自带的补丁层提供(可在 profile 补丁层覆盖)。
「设置 → 内置插件」的「建议提示词」标签页。这是日常配置建议模型的主入口,无需手动改配置文件:
DeepSeek 与 pi-ai 各 provider)中选择建议生成使用的路由;选择「跟随会话路由」则不覆盖,继承主请求路由。Alt 再按 Slash → Alt+Slash,Ctrl+Alt+X 显示为三个键),无需手动打字。suggest-prompt entry;保存后下一个完成回合生效,无需重启。provider / model / acceptKey。只有当宿主能描述该 entry 时标签页才会出现,这要求设置字段在插件 Config schema 中标记 volatile()(见 AGENTS.md 的「Version-critical contracts」)。同一标记也让这些值以活引用而非普通值传入插件,因此 resolveSuggestPromptConfig 会在配置边界统一解包。

以下字段由 bundle 自带的 cordis.patch.yml 提供默认值,通常无需改动;需要自定义时,在 profile 补丁层(~/.dsh/profiles/web/cordis.patch.yml)用 - insert: 覆盖同名 entry 的 config。provider / model / acceptKey 可在 WebUI 设置卡片中配置;其余字段不在 WebUI 设置卡片中:
| 字段 | 含义 | 默认值 |
|---|---|---|
maxInputBytes |
最终框架化用户提示的最大 UTF-8 字节数 | 4096 |
maxOutputTokens |
建议生成输出令牌上限 | 512 |
timeoutMs |
辅助请求端到端截止时间(毫秒) | 60000 |
maxRecentTurns |
转录尾部保留的最近完成回合数 | 1(只取最后一轮的用户输入 + AI 最终回答) |
maxTranscriptChars |
转录字符预算 | 12000 |
maxSuggestionChars |
建议的可见字符上限 | 240 |
provider / model |
各自独立覆盖主请求路由的对应字段;省略的字段自动继承主请求路由 | 继承(也可经界面配置) |
acceptKey |
采纳建议的输入框快捷键 | Tab(界面可录制为 Alt+Slash、Ctrl+Enter 等) |
maxOutputTokens提示:建议生成默认关闭思考(reasoningEffort: off),推理不消耗输出预算;但对无法关闭思考的模型(如部分 pi-ai 路由)会降级重试,此时思考仍会消耗预算——maxOutputTokens偏小时,流会在输出建议文本之前就以max-tokens结束。这类模型请留足预算(例如512)。
turn/end(reason=completed)时触发生成;按会话 + 回合去重,下一个完成回合会中止上一个在途生成。suggest-prompt/suggested 事件,suggestPrompt 投影把它暴露给 Web 端。suggestPromptTranscript 投影从已提交事件增量折叠而来,而不再扫描会话历史:dsh >= 0.2.0 禁止新生产代码同步读取历史事件。该折叠同时约束内存,只保留最近约 4 个回合。dsh >= 0.2.0 移除了客户端的 turnEnds 映射,因此新鲜度改由空闲标志判断,而非本地比对完成回合。visibility,不引起重排);否则两者会绘制在同一文字起点上,谁都看不清。acceptKey(默认 Tab)把建议填入草稿(可编辑后再发送);焦点不在输入框或处于 IME 组合输入时不触发,快捷键也只在显示幽灵文字时才被拦截(否则 Tab 保持默认焦点行为)。dsh >= 0.2.0 的输入框是 Lexical 的 contenteditable 宿主而非 <textarea>;采纳路径同时兼容两种形态,因此快捷键在任一形态下都可用。简体中文,否则 English)。[User Message] / [Assistant Response] 带标签块(已脱敏、受 maxTranscriptChars 约束)。suggest-prompt/request 事件,满足「模型可见 ⟺ 日志可重建」。reasoningEffort: off(DeepSeek 序列化为 thinking: disabled),追求快速与低成本;模型不支持时自动去掉该字段重试一次(拒绝发生在任何网络 I/O 之前,几乎无额外开销)。maxInputBytes / maxOutputTokens 约束;主 agent 请求不增加任何 token。AKIA…、OpenAI sk-…、GitHub ghp_/gho_/ghu_、Slack xox-…、JWT、Stripe rk_… 等密钥形状在发送前被掩蔽为占位标签。maxSuggestionChars。suggest-prompt/suggested 事件,投影保持 null,也不记录警告。pnpm install
pnpm build # host tsc + client tsdown bundle
pnpm test # vitest
pnpm typecheck
pnpm test:e2e # browser e2e against an isolated dsh web (needs DEEPSEEK_API_KEY)
pnpm test:e2e:local # local e2e against your real ~/.dsh (macOS: visible browser)
E2E(CI):
pnpm test:e2e会起一个隔离$DSH_HOME,用dsh plugin add安装本插件、dsh web起服务,再用 Playwright 走 WebUI(配置 DeepSeek Key、把建议模型设为 DeepSeek Flash),输入一道数学题后断言输入框出现下一条建议的幽灵文字。需要环境变量DEEPSEEK_API_KEY(无则跳过)与全局dsh;CI 里由DEEPSEEK_API_KEYsecret 注入。默认pnpm test不含 e2e。
E2E(本地):
pnpm test:e2e:local复用你的真实~/.dsh(不装 dsh、不跑 onboarding、不连工作区——本机已就绪),把当前源码 link 进本地 web profile(dsh plugin add),起dsh web后用 Playwright 把「建议提示词」模型设为 DeepSeek Flash(ccr /hai/DeepSeek-V4-Flash),输入「出一道小学数学题给我」并断言幽灵建议出现。macOS 下弹出可见浏览器,Linux 下 headless。会写真实~/.dsh(suggest-prompt 模型与 profile 依赖来源)——仅限本地开发验证,不入 CI。
安装说明:本仓库依赖已发布的
@deepseek-ai/*包(deepseek-harness 工作区)。上游少数内部包(@deepseek-ai/dsh-compact、@deepseek-ai/dsh-type-meta、@deepseek-ai/dsh-environment)尚未出现在 npm registry,本仓库通过根package.json的pnpm.overrides把它们映射到本地stubs/空包;同时用一条@deepseek-ai/dsh-*: 0.2.0-rc.2override 把整个 dsh 依赖集统一到当前插件所适配的 0.2.0-rc.2(与本仓库针对 0.2.0 契约的适配保持一致),因此pnpm install可直接成功;等 registry 补齐、上游稳定后,这两处 overrides 与stubs/均可清理。完整测试矩阵在 harness monorepo 内运行;本仓库是单 bundle 包的权威源码副本。pnpm build产出宿主 ESM(lib/{index,invariant}.js)、浏览器 bundle(lib/client.js)与lib/types/声明。
prepare脚本:package.json的prepare脚本会在pnpm install(含dsh plugin add <git-url>的安装流程)时自动运行pnpm build现场构建lib/,产物不入库。因此源码改动后无需手动构建即可被本地 dsh 加载;从 Git 安装也总能拿到完整产物(含类型声明)。
MIT
阅读中文版 · English
Suggested-next-prompt plugin for the DeepSeek Harness. After every completed agent turn, a bounded auxiliary LLM call writes one suggested next prompt into the session log; the web side renders it as ghost placeholder text inside the composer — press Tab (default) to adopt it into the draft (the Claude Code behavior).
For developers / maintainers: this repository is the authoritative source of record for a single bundle package (@studyzy/dsh-suggest-prompt) that merges the host generation and the browser rendering into one one-command-installable bundle:
| Package | Role |
|---|---|
@studyzy/dsh-suggest-prompt |
Host plugin (., ./invariant, ./types): generates the suggestion on turn/end (reason completed) and publishes the suggestPrompt session projection. Browser plugin (./client): reads the projection, renders the suggestion as ghost placeholder text inside the composer (inputActions.setDraft), and fills the draft on the configured shortcut. |
An automatic "next line" companion: after the AI answers, it predicts what you'd say next — saving repeated typing without interrupting your flow.
provider / model the suggestion inherits the route of the most recently logged main request — no model to pick just for suggestions; set them explicitly to route anywhere (for example a local OpenAI-compatible gateway).reasoningEffort: off by default (DeepSeek serializes it as thinking: disabled) so no budget is spent on a chain of thought; models that reject off retry once without the field.maxRecentTurns defaults to 1); intermediate tool calls / reasoning are never included.acceptKey (default Tab) and can be recorded from the "建议提示词" settings card (e.g. Alt+Slash, Ctrl+Enter).After every completed agent turn, the suggestion model renders the predicted next prompt as light ghost placeholder text inside the composer. Press Tab to adopt it into the draft:

^22.19 or >=24, pnpm.conversation.input.overlay slot and inputActions.setDraft — both standard in the deepseek-harness web input machine.This is a single-package bundle: the repo root @studyzy/dsh-suggest-prompt declares dsh.bundle (it ships its own cordis.patch.yml), so dsh plugin add pointing at the GitHub repository installs it as one bundle layer of the profile — no manual config-file edits.
# From GitHub (recommended)
dsh plugin --profile web add git@github.com:studyzy/dsh-suggest-prompt.git
# Or HTTPS
dsh plugin --profile web add https://github.com/studyzy/dsh-suggest-prompt.git
Then restart the running dsh web service. After install the profile layering becomes dsh-base → dsh-web-app → @studyzy/dsh-suggest-prompt.
Uninstall:
dsh plugin --profile web remove @studyzy/dsh-suggest-prompt
pnpm ≥10 git note: git-hosted plugins build on install via their
preparescript, which pnpm blocks until allowed. Ifaddfails, add the exact key pnpm printed toallowBuildsin~/.dsh/profiles/web/pnpm-workspace.yaml, then re-runadd.
dsh plugin --profile web add /path/to/dsh-suggest-prompt
dsh plugin --profile web add @studyzy/dsh-suggest-prompt
Note: every source ends up as the same bundle layer. The day-to-day suggestion provider/model is configured from the WebUI settings card (see Configuration below) — nothing to set at install time.
Configuration is split in two: the day-to-day route is set in the UI, and the one-time resource caps ship with sensible defaults in the bundle's patch layer (overridable in your profile patch layer).
A "建议提示词" card appears under Settings → Built-in plugins (内置插件). This is the primary entry point for choosing the suggestion route — no manual config-file edits:
Alt then Slash → Alt+Slash; a three-key combo like Ctrl+Alt+X displays as three keys), no typing needed.suggest-prompt entry of the active profile, and takes effect on the next completed turn — no restart needed.provider / model / acceptKey in the patch layer.The card is only discoverable when the host can describe the entry, which requires the settings-facing fields to be marked volatile() in the plugin's Config schema (see AGENTS.md, "Version-critical contracts"). That marking is also what makes those values arrive as live references rather than plain values, so resolveSuggestPromptConfig unwraps them at the config boundary.

The following are provided with defaults by the bundle's own cordis.patch.yml and normally need no changes; to customize, override the same entry's config via - insert: in your profile patch layer (~/.dsh/profiles/web/cordis.patch.yml). provider / model / acceptKey are editable from the WebUI card; the rest are not in the WebUI settings card:
| Field | Meaning | Default |
|---|---|---|
maxInputBytes |
Maximum UTF-8 bytes in the final framed user prompt | 4096 |
maxOutputTokens |
Suggestion output-token cap | 512 |
timeoutMs |
End-to-end auxiliary request deadline (ms) | 60000 |
maxRecentTurns |
Transcript tail keeps at most this many recent completed turns | 1 (only the last turn's user input + assistant final answer) |
maxTranscriptChars |
Transcript character budget | 12000 |
maxSuggestionChars |
Visible-character cap for the suggestion | 240 |
provider / model |
Each independently overrides the matching member of the main request route; omitted members inherit the main route | inherited (also editable from the WebUI) |
acceptKey |
Composer shortcut that adopts a displayed suggestion | Tab (recordable in the UI as Alt+Slash, Ctrl+Enter, ...) |
On
maxOutputTokens: suggestion generation disables thinking by default (reasoningEffort: off), so reasoning does not consume the output budget; but a model that cannot turn thinking off (some pi-ai routes) falls back to a retry where thinking still spends budget — a smallmaxOutputTokensthen ends the stream withmax-tokensbefore any suggestion text is produced. Leave a generous budget (e.g.512) for such models.
turn/end (reason completed), deduplicated per session and turn; the next completed turn aborts the in-flight generation.suggest-prompt/suggested event, and the suggestPrompt projection exposes it to the web side.suggestPromptTranscript projection folded from committed events, rather than by scanning session history: dsh >= 0.2.0 deprecates synchronous history reads for new production callers. The fold also bounds memory, keeping roughly the last four turns.dsh >= 0.2.0 removed the client's turnEnds map, so freshness comes from the idle flag rather than a local completed-turn comparison.visibility, so nothing reflows) — otherwise the two would paint on the same text origin and neither would be readable.acceptKey (default Tab) fills the draft (editable, not sent). It is ignored while focus is outside the composer or during IME composition; the shortcut is intercepted only while ghost text is displayed (otherwise Tab keeps its default focus behavior).contenteditable host in dsh >= 0.2.0, not a <textarea>; the accept path detects focus across both shapes so the shortcut works on either.简体中文 when the last user message contains CJK, otherwise English).[User Message] / [Assistant Response] blocks (redacted, bounded by maxTranscriptChars).suggest-prompt/request event before dispatch, satisfying the model-visible ⟺ logged invariant.reasoningEffort: off by default (DeepSeek serializes it as thinking: disabled) for speed and low cost; a model that rejects off retries once without the field (the rejection happens before any network I/O, so the retry is nearly free).maxInputBytes / maxOutputTokens; the main agent request gains zero tokens.AKIA…, OpenAI sk-…, GitHub ghp_/gho_/ghu_, Slack xox-…, JWTs, and Stripe rk_… secret shapes are masked before the transcript reaches the model.maxSuggestionChars.suggest-prompt/suggested event is written, the projection stays null, and no warning is logged.off (such as the built-in DeepSeek).See CONTRIBUTING.md for setup, the CI gates, and the rules around version-coupled code. Changes are recorded in CHANGELOG.md; security reports go through SECURITY.md.
pnpm install
pnpm build # host tsc + client tsdown bundle
pnpm test # vitest
pnpm typecheck
pnpm test:e2e # browser e2e against an isolated dsh web (needs DEEPSEEK_API_KEY)
pnpm test:e2e:local # local e2e against your real ~/.dsh (macOS: visible browser)
E2E (CI):
pnpm test:e2eboots an isolated$DSH_HOME, installs this bundle viadsh plugin add, startsdsh web, and drives the WebUI with Playwright (stores the DeepSeek key, sets the suggestion model to DeepSeek Flash, sends a math question, then asserts a ghost next-prompt suggestion appears, that the native placeholder is hidden underneath it — the double-ghost regression — and that Tab adopts the suggestion). It requiresDEEPSEEK_API_KEY(skipped otherwise) and a globally installeddsh; CI injects the key as a secret. The defaultpnpm testdoes not include e2e. In CI this job iscontinue-on-error, because it depends on a live model round-trip; thebuildjob is the gate.
E2E (local):
pnpm test:e2e:localreuses your real~/.dsh(no dsh install, no onboarding, no workspace pick — your machine is already set up). It links the current source into the local web profile viadsh plugin add, startsdsh web, then drives Playwright to set the "建议提示词" suggestion model to DeepSeek Flash (ccr /hai/DeepSeek-V4-Flash), sends "出一道小学数学题给我", and asserts a ghost suggestion appears. On macOS the browser runs headful (watch it drive the UI); headless elsewhere. It writes to your real~/.dsh(the suggest-prompt model and the profile's dependency source) — local development only, not part of CI.
Install caveat: this repo depends on the published
@deepseek-ai/*packages (the DeepSeek Harness workspace). A small number of internal packages referenced by the publisheddsh-*releases are not yet on the npm registry (@deepseek-ai/dsh-compact,@deepseek-ai/dsh-type-meta,@deepseek-ai/dsh-environment); the rootpackage.jsonpnpm.overridesmap them to the local emptystubs/packages. A second override (@deepseek-ai/dsh-*: 0.2.0-rc.2) pins the whole dsh dependency set to the 0.2.0-rc.2 release this plugin targets (aligned with its 0.2.0 contract adaptation), sopnpm installsucceeds out of the box — remove both overrides andstubs/once the registry is complete and the upstream stabilizes. The full test matrix runs inside the harness monorepo; this repo is the source-of-record copy for the single bundle package.pnpm buildemits the host ESM (lib/{index,invariant}.js), the browser bundle (lib/client.js), and thelib/types/declarations.
The
preparescript:package.json'spreparerunspnpm buildonpnpm install(includingdsh plugin add <git-url>), buildinglib/on the spot. The build output is never committed, so source edits take effect for a local dsh load without a manual build, and a Git install always receives a complete artifact set (types included).
MIT
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。