kovey/dsh-chat-interaction0

dsh-chat-interaction

Abstract interaction layer between DeepSeek Harness (DSH) and IM platforms (Feishu, WeCom, ...). Channel-agnostic hub with proven interaction patterns (dedupe, instant ack, wait-reply, followup wakeup, scoring-based model routing, approval gate) plus pluggable adapters for Feishu and WeCom.

包名
dsh-chat-interaction
版本
0.1.0
最近更新
2026年9月10日

安装

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:kovey/dsh-chat-interaction

使用

1. 安装(挂进 DSH)

前置:Node ≥ 18、pnpm、DSH 宿主 v0.1.5-rc.1;飞书/企业微信应用凭证(按需)。

依赖对齐:本包 peerDependencies@deepseek-ai/{dsh-agent,dsh-llm,dsh-tools}@^0.1.5-rc.1

  • cordis@^4.0.2 —— 与 dsh 0.1.5-rc.1 官方宿主提供的版本一致(dsh --version 可确认), pnpm 会直接复用、不会装第二份。

### 2. 配置

**a) 插件配置**(profile 覆盖,注意是 **id 覆盖**而非 insert —— bundle 层已经 insert 过,
重复 insert 同 id 会抛 `duplicate loader entry id`):

```yaml

### 3. 首次使用

1. 重启 TUI 会话(插件加载,但**默认不建立任何连接**)。
2. 在会话里对 agent 说 **「连接飞书」**(agent 调 `feishu_listener` 的 `action=start`);
   企业微信同理(启动回调服务)。也可让 agent 用 `feishu_auth_state` 查看当前状态。
3. 在飞书里给机器人发一条消息:
   - 会先收到**即时回执**("收到,正在处理~");
   - 然后 agent 用 `feishu_send_message` 回复结果;
   - 需要确认时 agent 会发 `feishu_send_card` 卡片,你点按钮后它会收到
     `[飞书卡片点击]` 继续处理。
4. 企业微信还需在**管理后台 → 应用 → 接收消息**里把回调 URL 填成
   `http://:8787/wecom`(URL 校验、签名、AES 解密都由适配器完成);
   也可以用你自己的中间件解密后调 `WeComChannel.feed(xml)`。

### 4. 日常使用

**工具**(每个渠道自动生成一套,前缀 = 渠道名):

| 工具 | 用途 |
|---|---|
| `feishu_send_message` / `wecom_send_message` | 回文本;带 `title` 时发富文本 |
| `feishu_send_card` / `wecom_send_card` | 交互卡片(按钮值 A/B/yes/…),点击以 `[渠道卡片点击]` 回来 |
| `feishu_wait_reply` / `wecom_wait_reply` | 阻塞等该 chat 的下一条消息(**被本次调用消费**,不产生新回合) |
| `feishu_listener` / `wecom_listener` | `start` / `stop` / `status`,连接控制 |
| `feishu_auth_state` / `wecom_auth_state` | 权限模式、allowlist、active chat、待确认问题 |

**你可以直接对它说的话**:「连接飞书」「断开飞书」「git status」(插件直接执行并回结果)、
「最近提交」「新增一个活动功能」(转交 agent 开发)、「今天天气不错」(闲聊直答)、
卡片上的 A/B/C/D 点击(确认类)。

**权限与审批**(`manual` 模式):

```sh
echo manual > /.dsh/feishu-permission-mode.txt   # 或让 agent 发权限模式卡切换

之后来自渠道的任务执行 bash 时,会先给飞书/企微推「同意 / 拒绝 / 始终同意」卡片; 超时按 fail-closed 拒绝;「始终同意」把该命令追加到 /.dsh/-permission-allowlist.txt

任务连续性 + 收尾卡:需求/修复类任务开始时,agent 会写 /.dsh/-task-active/.json(8h TTL)——这期间该 chat 的消息一律转交 agent(不被命令/闲聊路由截走);任务收尾时按提示词策略发「收尾提交方式」四按钮卡 (A 提交+推送+部署 / B 提交并推送 / C 仅本地提交 / D 暂不提交)。

打分与模型路由:每条转交 agent 的消息会先打分,回合里能看到 消息评分: 0.90 (high) → 执行模型: deepseek-reasoner;覆盖只作用于该回合, 回合结束自动恢复会话默认模型。关闭:scoring: { enabled: false }

6. 独立使用核心(不依赖 cordis / 不触 agent runtime)

import { InteractionHub, createFeishuChannel, createWeComChannel } from 'dsh-chat-interaction'

const hub = new InteractionHub({
  spoolFile: '~/.dsh/chat-spool.jsonl',
  onFollowup: (msg, ctx) => myAgent.followup(msg),   // 你自己接 agent
})

hub.addChannel(createFeishuChannel({ appId: 'cli_...', appSecret: '...' }))
hub.addChannel(createWeComChannel({ corpId: 'ww...', corpSecret: '...' }))

await hub.connect('feishu')                        // 开始收飞书消息
await hub.sendText('feishu', chatId, '你好')
const answer = await hub.waitReply('feishu', chatId, 120_000)

完整配置(apply / cordis.patch.yml 的 config)

{
  enabled: true,                       // 总开关
  logFile: '~/.dsh/chat-interaction.log',
  spoolFile: '~/.dsh/chat-interaction-spool.jsonl', // 每条入站消息 JSONL tee
  pendingDir: '~/.dsh/chat-pending',   // 插件待确认问题存储(按渠道分目录)
  retry: {                             // 模型失败重投守卫; false 关闭
    maxAttempts: 10, baseDelayMs: 30_000, capDelayMs: 600_000, pollMs: 5_000,
  },
  scoring: { /* 见「消息打分与模型路由」一节 */ },
  lease: {                             // 渠道租约(24×7 服务 ⇄ TUI 心跳接管)
    enabled: true,
    role: 'interactive',               // interactive | service (默认按 channels.*.role 推断)
    ttlMs: 90_000,                     // 心跳过期阈值
    heartbeatMs: 30_000,               // 续约间隔(检查间隔上限 2s)
    dir: '~/.dsh',                     // 租约文件目录
  },
  router: {                            // 插件自治(命令/确认/闲聊/任务连续性)
    enabled: true,
    evaluator: 'auto',                 // auto | rule | model
    autoPermissionCard: true,          // 任务类消息自动发权限模式卡
    autoDisambiguation: true,          // 无法分类且无模型时自动发消歧卡
    model: 'deepseek-v4-flash',        // 分类与闲聊用的模型
    autoReply: true,                   // 闲聊是否插件内直答
    chatHistoryTurns: 10,
    chatDir: '~/.dsh/chat-history',
    commandTimeoutMs: 30_000,
    commandMaxOutputChars: 2000,
    taskActiveTtlMs: 28_800_000,       // 任务标记 8h
  },
  permission: {
    mode: 'auto',                      // 权限模式文件缺省值 (auto/manual)
    activeWindowMs: 600_000,           // followup 后 bash 视为渠道来源的窗口
    answerTimeoutMs: 300_000,          // 审批卡超时 → fail closed
    bridgeHarnessApproval: false,      // L3: 把所有 harness 审批转渠道卡片 (headless)
  },
  channels: {
    feishu: {
      enabled: true,
      role: 'off',                     // 'listener' = 启动即连 (部署决策)
      appId: '', appSecret: '',        // 留空走 feishu-app.json 凭证链 / FEISHU_* 环境变量
      credsFile: '',                   // 可选: 显式凭证文件路径
      mediaRetentionDays: 7,
    },
    wecom: {
      enabled: true,
      role: 'off',
      corpId: '', corpSecret: '',       // 留空走 wecom-app.json 链 / WECOM_* 环境变量
      agentId: '',                      // 可选
      token: '', aesKey: '',            // 只用内置回调服务器时需要; 否则可留空 (feed() 模式)
      credsFile: '',                    // 可选: 显式凭证文件路径
      callback: { host: '0.0.0.0', port: 8787, path: '/wecom' },
      mediaRetentionDays: 7,
    },
    // 智能机器人(WS 长连接,与上面的自建应用二选一或并存)
    wecom_bot: {
      enabled: true,
      role: 'off',
      botId: '', secret: '',            // 留空走 wecom-bot.json 链 / WECOM_BOT_* 环境变量
      wsUrl: '',                        // 私有部署的长连接地址(默认官方 wss://openws.work.weixin.qq.com)
      credsFile: '',
      mediaRetentionDays: 7,
    },
  },
  channelFactories: { /* 自定义渠道: { ding: (cfg) => new DingChannel(cfg) } */ },
}