{"_id":"@anionex/dsh-suggested-replies","name":"@anionex/dsh-suggested-replies","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@anionex/dsh-suggested-replies","version":"0.1.0","description":"DSH Web plugin that proposes concise next user messages above the composer and inserts a selected reply into the draft without sending it.","keywords":["deepseek","deepseek-harness","suggested-replies","web","plugin"],"repository":{"type":"git","url":"git+https://github.com/Anionex/dsh-suggested-replies.git"},"bugs":{"url":"https://github.com/Anionex/dsh-suggested-replies/issues"},"type":"module","license":"MIT","packageManager":"pnpm@11.20.0","main":"./lib/index.js","types":"./lib/types/index.d.ts","exports":{".":{"types":"./lib/types/index.d.ts","import":"./lib/index.js"},"./invariant":{"types":"./lib/types/invariant.d.ts","import":"./lib/invariant.js"},"./client":{"types":"./lib/types/client/index.d.ts","default":"./lib/client.js"},"./package.json":"./package.json"},"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["@deepseek-ai/dsh-client-runtime","@deepseek-ai/dsh-client-locale","@deepseek-ai/dsh-client-connection","@deepseek-ai/dsh-client-ui-slots","@deepseek-ai/dsh-client-ui-conversation","@deepseek-ai/dsh-client-ui-settings"],"platform":"web"}},"scripts":{"links":"node scripts/link-dsh.mjs","clean":"node scripts/clean.mjs","typecheck":"tsc --noEmit -p tsconfig.json","test":"vitest run","build":"pnpm run clean && tsc -p tsconfig.json && tsdown -c tsdown.config.ts","check":"pnpm run links && pnpm run typecheck && pnpm run test && pnpm run build && pnpm pack --dry-run","prepack":"pnpm run build"},"peerDependencies":{"@deepseek-ai/dsh-agent":"^0.1.0-rc.6","@deepseek-ai/dsh-client-connection":"^0.1.0-rc.6","@deepseek-ai/dsh-client-locale":"^0.1.0-rc.6","@deepseek-ai/dsh-client-runtime":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-conversation":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-settings":"^0.1.0-rc.6","@deepseek-ai/dsh-client-ui-slots":"^0.1.0-rc.6","@deepseek-ai/dsh-host-apiproxy":"^0.1.0-rc.6","@deepseek-ai/dsh-invariants":"^0.1.0-rc.6","@deepseek-ai/dsh-llm":"^0.1.0-rc.6","@deepseek-ai/dsh-session":"^0.1.0-rc.6","@deepseek-ai/dsh-session-persistence":"^0.1.0-rc.6","@deepseek-ai/dsh-settings":"^0.1.0-rc.6","@deepseek-ai/dsh-storage-domain":"^0.1.0-rc.6","@deepseek-ai/dsh-system-prompt":"^0.1.0-rc.6","@deepseek-ai/dsh-tools":"^0.1.0-rc.6","@deepseek-ai/dsh-workspace":"^0.1.0-rc.6","@deepseek-ai/cordis":"^4.0.1","react":"^18.2.0","react-dom":"^18.2.0","@deepseek-ai/schemastery":"^3.18.1"},"peerDependenciesMeta":{"@deepseek-ai/dsh-agent":{"optional":true},"@deepseek-ai/dsh-client-connection":{"optional":true},"@deepseek-ai/dsh-client-locale":{"optional":true},"@deepseek-ai/dsh-client-runtime":{"optional":true},"@deepseek-ai/dsh-client-ui-conversation":{"optional":true},"@deepseek-ai/dsh-client-ui-settings":{"optional":true},"@deepseek-ai/dsh-client-ui-slots":{"optional":true},"@deepseek-ai/dsh-host-apiproxy":{"optional":true},"@deepseek-ai/dsh-invariants":{"optional":true},"@deepseek-ai/dsh-llm":{"optional":true},"@deepseek-ai/dsh-session":{"optional":true},"@deepseek-ai/dsh-session-persistence":{"optional":true},"@deepseek-ai/dsh-settings":{"optional":true},"@deepseek-ai/dsh-storage-domain":{"optional":true},"@deepseek-ai/dsh-system-prompt":{"optional":true},"@deepseek-ai/dsh-tools":{"optional":true},"@deepseek-ai/dsh-workspace":{"optional":true},"@deepseek-ai/cordis":{"optional":true},"react":{"optional":true},"react-dom":{"optional":true},"@deepseek-ai/schemastery":{"optional":true}},"dependencies":{"zod":"^4.4.3"},"devDependencies":{"@testing-library/dom":"^10.4.1","@testing-library/react":"^16.3.2","@types/node":"^22.20.0","@types/react":"~18.3.31","@types/react-dom":"~18.3.0","jsdom":"^29.1.1","react":"^18.3.1","react-dom":"^18.3.1","tsdown":"^0.22.2","typescript":"^6.0.3","vitest":"^4.1.8"},"engines":{"node":"^22.19.0 || >=24.0.0"},"_id":"@anionex/dsh-suggested-replies@0.1.0","homepage":"https://github.com/Anionex/dsh-suggested-replies#readme","_integrity":"sha512-FJYY35tRDz3UY9w4msE8OltKSGq9C2h7LR5LkKhjck1hMFIYbnscvQt74m8iuul1JVu8azm9O7ijq4tcJy6xRA==","_resolved":"/tmp/dsh-anionex-release.oFTNLY/packs/suggested-replies/anionex-dsh-suggested-replies-0.1.0.tgz","_from":"file:/tmp/dsh-anionex-release.oFTNLY/packs/suggested-replies/anionex-dsh-suggested-replies-0.1.0.tgz","_nodeVersion":"25.9.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-FJYY35tRDz3UY9w4msE8OltKSGq9C2h7LR5LkKhjck1hMFIYbnscvQt74m8iuul1JVu8azm9O7ijq4tcJy6xRA==","shasum":"39cc77863432c3b12b3178e34c9c1525f50c2d1d","tarball":"https://registry.npmjs.org/@anionex/dsh-suggested-replies/-/dsh-suggested-replies-0.1.0.tgz","fileCount":33,"unpackedSize":169596,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDJrvPaXjvUk8TJ6xCUVgMTr9nENNE9geLZRYiIaFvwfAIhAOxRWp5zqN4Y+jxRBiys7GSHaUnSkq+i3wR0u9qHxyzD"}]},"_npmUser":{"name":"anionex","email":"davidyang042@gmail.com"},"directories":{},"maintainers":[{"name":"anionex","email":"davidyang042@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dsh-suggested-replies_0.1.0_1786711961684_0.8214663332036722"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-14T12:52:41.481Z","0.1.0":"2026-08-14T12:52:41.842Z","modified":"2026-08-14T12:52:42.003Z"},"maintainers":[{"name":"anionex","email":"davidyang042@gmail.com"}],"description":"DSH Web plugin that proposes concise next user messages above the composer and inserts a selected reply into the draft without sending it.","homepage":"https://github.com/Anionex/dsh-suggested-replies#readme","keywords":["deepseek","deepseek-harness","suggested-replies","web","plugin"],"repository":{"type":"git","url":"git+https://github.com/Anionex/dsh-suggested-replies.git"},"bugs":{"url":"https://github.com/Anionex/dsh-suggested-replies/issues"},"license":"MIT","readme":"# dsh-suggested-replies\n\nDSH Web 的“预测回复”插件：AI 回复结束后，生成几条用户下一步可能会发送的消息候选，并将它们显示在**聊天输入框上方**。点击候选只会将文本填入输入框，**不会自动发送**。\n\n![候选气泡位于输入框上方](docs/suggested-replies-layout.svg)\n\n## 行为\n\n```text\nAI 完成本轮回复\n  -> 插件创建一个只使用官方事件的内部 Agent Session\n  -> 内部 Agent 以零工具、完整 persona 根据近期对话生成候选\n  -> flush 并归档内部 Session，把 UI 状态写入插件自有 storage-domain sidecar\n  -> Web 在 conversation.input.dock（输入框上方）显示气泡\n  -> 点击候选：inputActions.setDraft(text)\n  -> 用户自行编辑或点击发送\n```\n\n- **正确位置**：注册 `conversation.input.dock`，位于 DSH 的消息输入卡片上方；不会放到输入框下方的 `conversation.composer.dock`。\n- **单行展示**：候选始终保持在一条横线上；宽度不足时横向滚动查看，不换到第二行。\n- **只填入草稿**：点击候选会替换当前草稿为该候选，不会调用发送动作。\n- **候选内容**：优先覆盖合理的下一步执行、验证/追问、或决策/选择；候选跟随最近对话语言，彼此去重且可直接发送。\n- **失败兜底**：辅助模型没有返回规定 JSON 时，插件仍按最近对话语言生成配置数量的保守候选，不再把空数组当成成功结果隐藏整行。\n- **上下文范围**：辅助提示词只截取直接用户消息和 AI 回复；AGENTS、运行时快照和 Skill 目录等注入上下文不会挤占最近对话窗口。\n- **过期保护**：新用户输入、设置关闭、辅助调用超时或插件卸载都会取消当前生成，避免旧结果在下一轮对话中回流。\n- **模型调用与成本**：每个可生成候选的完成轮次额外运行一个短文本内部 Agent。默认复用 Session 最新 `request/header` 中实际使用的 provider/model，再回退到 Agent 默认路由；也可通过 `suggestionProvider` + `suggestionModel` 显式覆盖。关闭开关后不再运行该 Agent。\n- **Core 零修改**：父 Session 不追加插件自定义事件，也不修改 `KNOWN_SESSION_EVENT_TYPES`。生成过程完整记录在内部官方 Agent Session；候选的 loading/ready/cleared 状态保存在插件拥有的 `suggested_replies_state` domain，因此卸载插件后父 Session 仍能由原版 DSH reader 恢复。\n- **状态恢复**：sidecar 行按 Session id 存储，并校验 header 的 `{ createdAt, cwd }`；页面刷新或 Host 重启后仍可恢复当前候选，复用同一 id 的另一条 Session 不会读到旧状态。\n\n## 安装\n\n### 从 npm 一键安装\n\n```sh\ndsh plugin --profile web add @anionex/dsh-suggested-replies\n```\n\n### 本地开发目录安装\n\n```sh\ndsh plugin --profile web add /absolute/path/to/dsh-suggested-replies\n```\n\n安装或更新后，重启正在运行的 `dsh web` 服务，并在浏览器硬刷新页面。新建或重新打开一个会话后进行验证。\n\n## 设置与配置\n\nWeb 设置页中的“下一步建议”分区提供 `enabled` 总开关。它写入 `$DSH_HOME/settings.yaml` 的 `suggested-replies` 区域，下一轮立即生效。\n\n其余部署参数在 `cordis.patch.yml` 或 profile overlay 中配置：\n\n| 字段 | 默认值 | 说明 |\n| --- | ---: | --- |\n| `enabled` | `true` | 是否生成候选；关闭后无辅助模型调用。 |\n| `suggestionCount` | `3` | 每轮候选数量，范围 `2-4`。 |\n| `contextMessageCount` | `4` | 传给辅助模型的最近可见消息数，范围 `2-6`。 |\n| `maxSuggestionChars` | `160` | 单条候选保留的最大字符数，范围 `32-300`。 |\n| `maxTokens` | `384` | 辅助调用的最大输出 token，范围 `64-1024`。 |\n| `timeoutMs` | `15000` | 辅助调用最长时长（毫秒），范围 `1000-30000`。 |\n| `suggestionProvider` | 未设置 | 可选：显式指定辅助调用 provider；省略时跟随当前会话模型。 |\n| `suggestionModel` | 未设置 | 可选：显式指定辅助调用 model；必须与 `suggestionProvider` 同时提供。 |\n\n示例 overlay：\n\n```yaml\n- patch:\n    - id: suggested-replies\n      config:\n        suggestionCount: 4\n        maxSuggestionChars: 120\n        timeoutMs: 10000\n        suggestionProvider: deepseek-official\n        suggestionModel: deepseek-v4-flash\n```\n\n## 开发与验证\n\n```sh\nexport DSH_SOURCE=/path/to/deepseek-harness\npnpm install --no-frozen-lockfile\npnpm run links\npnpm run typecheck\npnpm run test\npnpm run build\npnpm pack --dry-run\n```\n\n提交前的独立 Profile 验证：\n\n```sh\nTEMP_DSH_HOME=\"$(mktemp -d)\"\nDSH_HOME=\"$TEMP_DSH_HOME\" dsh plugin --profile web add /absolute/path/to/dsh-suggested-replies\nDSH_HOME=\"$TEMP_DSH_HOME\" dsh --profile web --dump-config\n```\n\n实际页面验收要点：\n\n1. AI 回复结束后，候选行的几何位置在 `[data-composer-card]` 上方。\n2. 点击候选后，textarea 草稿变为候选文本。\n3. 点击候选后不会创建下一轮、不会自动发送消息。\n\n## 致谢\n\n本项目是独立实现，并明确致谢 [dsh-external/dsh-auto-blame](https://github.com/dsh-external/dsh-auto-blame) 的双端 Web 插件组合思路。\n\n本项目重新实现了产品语义、内部 Agent、sidecar 状态、RPC 长轮询、提示词、过期结果处理、输入框上方布局和“只填入不发送”的交互；未包含该项目的源代码、图像或品牌资产。\n\n## License\n\n[MIT](LICENSE)\n","readmeFilename":"README.md","_rev":"1-46842d4ff16fb6bd810310e39fade266"}