🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

鲸鱼娘桌宠 wangquanqing/dsh-coopanion-pet

DeepSeek Harness
wangquanqing15 天前有提交已提供中文文档扫描:含敏感能力

在界面下沿养一只会走动、说话、跟随状态的鲸鱼娘

30/100
综合分
2
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/11/3 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add wangquanqing/dsh-coopanion-pet
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:仅索引
是什么[目录]—
dsh 原生插件 · ui
暂无时间戳·判据 →
装得上吗[实装验证]—
本站尚未做安装检查
暂无时间戳·判据 →
安全吗[自动扫描]—
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
数据截至 2026/11/03·判据 →
还在维护吗[仓库]✓
活跃:最近一次提交在 15 天前
暂无时间戳·判据 →
图例:✓ 通过! 需注意— 未判定 / 无数据· 「未判定」= 本站尚无数据,不代表插件有问题
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →

可信度证据

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 44 处,展示前 34 条)
  • 读取本地文件
    index.js:123
    const stream = createReadStream(file);
  • 读取本地文件
    index.js:432
    if (file === null || rel === '' || !existsSync(file) || !statSync(file).isFile()) {
  • 读取本地文件
    index.js:432
    if (file === null || rel === '' || !existsSync(file) || !statSync(file).isFile()) {
  • 读取本地文件
    index.js:468
    if (!existsSync(file)) {
  • 读取本地文件
    index.js:491
    if (!existsSync(file)) { res.writeHead(404); res.end('client missing'); return; }
  • 写入本地文件
    index.js:500
    try { mkdirSync(PACK_DIR, { recursive: true }); } catch (error) { }
  • 读取本地文件
    index.js:555
    if (!existsSync(file)) { res.writeHead(404); res.end('entry missing'); return; }
  • 发起网络请求
    client.js:457
    fetch(REPORT, {
  • 发起网络请求
    client.js:594
    fetch(REPORT, {
  • 发起网络请求
    client.js:617
    res = await fetch(SPEECH, {
  • 发起网络请求
    client.js:803
    const res = await fetch(PACK_LIST);
  • 发起网络请求
    client.js:1286
    const probe = await fetch(SPEECH, { method: 'GET' });
  • 发起网络请求
    client.js:1305
    res = await fetch(SPEECH, {
  • 读取本地文件
    lib/packs.js:43
    if (!existsSync(file) || !statSync(file).isFile()) return null;
  • 读取本地文件
    lib/packs.js:43
    if (!existsSync(file) || !statSync(file).isFile()) return null;
  • 读取本地文件
    lib/packs.js:46
    raw = JSON.parse(readFileSync(file, 'utf8'));
  • 读取本地文件
    lib/packs.js:86
    if (!rootDir || !existsSync(rootDir) || !statSync(rootDir).isDirectory()) {
  • 读取本地文件
    lib/packs.js:86
    if (!rootDir || !existsSync(rootDir) || !statSync(rootDir).isDirectory()) {
  • 读取本地文件
    lib/packs.js:91
    names = readdirSync(rootDir, { withFileTypes: true });
  • 写入本地文件
    lib/pack-import.js:133
    await mkdir(path.dirname(target), { recursive: true });
  • 写入本地文件
    lib/pack-import.js:134
    await writeFile(target, f.data);
  • 删除本地文件递归
    lib/pack-import.js:139
    await rm(staging, { recursive: true, force: true });
  • 删除本地文件递归
    lib/pack-import.js:148
    try { await rm(staging, { recursive: true, force: true }); } catch (e) { }
  • 删除本地文件递归
    lib/pack-import.js:171
    if (existsSync(dst)) await rm(dst, { recursive: true, force: true });
  • 读取本地文件
    lib/pack-import.js:171
    if (existsSync(dst)) await rm(dst, { recursive: true, force: true });
  • 删除本地文件递归
    lib/pack-import.js:178
    await rm(pending.staging, { recursive: true, force: true });
  • 删除本地文件递归
    lib/pack-import.js:188
    try { await rm(s, { recursive: true, force: true }); } catch (error) { }
  • 执行外部命令来自配置/变量
    lib/desktop-bridge.js:191
    child = spawn(electronPath, args, {
  • 执行外部命令固定命令
    lib/desktop-bridge.js:236
    spawn('taskkill', ['/PID', String(pid), '/T', '/F'], { stdio: 'ignore', detached: true }).unref();
  • 写入本地文件
    desktop/main.cjs:63
    try { fs.appendFileSync(STATUS_FILE, new Date().toISOString() + ' ' + line + '\n'); } catch (error) { }
  • 发起网络请求
    desktop/main.cjs:103
    const up = http.request({
  • 读取本地文件
    desktop/main.cjs:138
    if (!fs.existsSync(target) || !fs.statSync(target).isFile()) return null;
  • 读取本地文件
    desktop/main.cjs:202
    fs.createReadStream(file)
  • 读取本地文件
    desktop/main.cjs:363
    const img = fs.existsSync(iconPath) ? nativeImage.createFromPath(iconPath) : nativeImage.createEmpty();
扫描于 2026/11/3(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

社区实装还没有人打卡 · 装过的第一个就是你
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

Coopanion 桌宠 for DeepSeek Harness:把 Coopanion 的桌面宠物(DeepSeek 鲸鱼娘)移植为 DSH Web UI 插件

README

由 DeepSeek 最新模型翻译生成
Coopanion 桌宠(DSH 插件) 把 Coopanion 的桌面宠物搬进 DeepSeek Harness 的 Web 界面,作为零侵入的 UI 插件运行。当前版本(v1.9.1)实现 P0 桌宠本体 + P1 表现层 + P2 接入 DSH + 独立窗口—— 桌宠本体 - 鲸鱼娘(DeepSeek Chan) 站在界面下沿,可自由走动 - 待机动画:呼吸、眨眼、视线跟随鼠标、头发与裙摆的弹簧摆动 - 鼠标拖拽:抓住她拖到任意位置,松手后落下,甩得快会晕 - 不挡界面:只有光标落在她本体或她自己的界面元素上时才接管指针,其余区域完全放行 说话与互动 - 气泡说话:文字逐字出现,标点处自然停顿,说完自动收起 - 表情与动作标记:【happy, jump】 先播再说话,<happy> 打到那个字时播 - 提问气泡:最多 3 个选项 + 自填框,按 1–3 可直接选,Esc 放弃 - 悬停按钮条:靠近她时出现,可让她说话、换表情、做动作、走两步、装扮、睡觉 - 右键菜单:装扮、状态气泡开关、音效、体型、漫游倾向、回到原位、隐藏 - 音效:全部由 Web Audio 合成,不带任何音频素材,不增加额外体积 状态气泡 - 她头顶会显示你正在做什么:正在看文件 / 正在写文件 / 正在查找 / 正在跑命令 / 等待回答 / 思考中的三个点
展开完整文档(README 剩余 1 节)
Coopanion 桌宠(DSH 插件)

把 Coopanion 的桌面宠物搬进 DeepSeek Harness 的 Web 界面,作为零侵入的 UI 插件运行。当前版本(v1.9.1)实现 P0 桌宠本体 + P1 表现层 + P2 接入 DSH + 独立窗口——

桌宠本体

- 鲸鱼娘(DeepSeek Chan) 站在界面下沿,可自由走动
- 待机动画:呼吸、眨眼、视线跟随鼠标、头发与裙摆的弹簧摆动
- 鼠标拖拽:抓住她拖到任意位置,松手后落下,甩得快会晕
- 不挡界面:只有光标落在她本体或她自己的界面元素上时才接管指针,其余区域完全放行

说话与互动

- 气泡说话:文字逐字出现,标点处自然停顿,说完自动收起
- 表情与动作标记:【happy, jump】 先播再说话,<happy> 打到那个字时播
- 提问气泡:最多 3 个选项 + 自填框,按 1–3 可直接选,Esc 放弃
- 悬停按钮条:靠近她时出现,可让她说话、换表情、做动作、走两步、装扮、睡觉
- 右键菜单:装扮、状态气泡开关、音效、体型、漫游倾向、回到原位、隐藏
- 音效:全部由 Web Audio 合成,不带任何音频素材,不增加额外体积

状态气泡

- 她头顶会显示你正在做什么:正在看文件 / 正在写文件 / 正在查找 / 正在跑命令 / 等待回答 / 思考中的三个点
- 细到具体对象:正在看文件 · config.ts、正在跑命令 · pnpm test——从工具参数里取文件名或命令,只留 basename 免得把完整路径摊在屏幕上
- 数据来自宿主侧的 agent/status 与 tools/execute,经 SSE 推给页面
- 对话永远优先:她说话或提问时,状态气泡自动让位

两个形象

- 鲸鱼娘(主形象,sprite rig:25 张纹理 + model.json)
- Coo(上游的第二个形象,纯代码绘制:SVG path,无纹理,三个文件共 49KB)
- 在设置面板「形象」一行切换,按需加载——默认用鲸鱼娘的人不为 Coo 付加载时间
- 两者共用同一套渲染内核(assets/kit/);Coo 的三个文件与上游逐字节相同

对话栈

- 她头顶旁边显示最近 3 条对话(人话与她的回话),最新的贴着她的头
- 数据来自 DSH 的 session/event(user/message 与 assistant/message 的文本)
- 她说话/提问时整栈淡出让位(透明度 1 → 0.04)
- 只显示 3 条、不做分页/附件/撤回——那些是 DSH 会话 UI 的活,重复造没意义
- 设置面板可关

第三方形象包(可选)

- 导入:设置面板「形象」一行有「导入形象包」,选一个含 figure.json 的文件夹
- 安全:包代码只在沙箱 iframe 里跑(sandbox="allow-scripts",不给 same-origin), CSP 再限一道(default-src 'none'、connect-src 'none');宿主只读文件、不执行包代码
- 包不能吞点击:她的包围盒与命中圆被收进舞台内;触摸事件只在页面刚给过指针输入后才算数
- 包放在 ~/.dsh/coopanion-pet-packs/<id>/,插件升级不会丢
- 只支持「选文件夹」:不引第三方 zip 库(省一个依赖),zip 请先解压再选

独立窗口(桌面上那个她) [已验证]

她可以不依赖 DSH 窗口待在桌面上——一个真透明、无边框、置顶、可穿透的窗口。

| 能力 | 说明 |
| 真透明 | transparent: true + backgroundColor: '#00000000',只有她和她的气泡可见 |
| 鼠标穿透 | 默认穿透到桌面;光标移到她身上才接点击(setIgnoreMouseEvents(true, {forward:true}) + 命中判定) |
| 置顶 | setAlwaysOnTop(true, 'floating'),浮在普通窗口之上 |
| 跨屏 | setVisibleOnAllWorkspaces,全屏时也在 |
| 全局快捷键 | 默认 Alt+Space 唤出/收起——DSH 没聚焦时也管用 |
| 右键菜单 | 打开 DSH 主窗口 / 打开对话面板 / 让她说话 / 隐藏 / 穿透模式 / 置顶 / 退出 |
| 托盘 | 点图标唤出/收起,右键同一套菜单 |
| 独立聊天面板 | 列会话、读历史、发消息——与 DSH 主面板操作的是同一批会话 |
| 13 个 pet_ 工具 | 含新增的 pet_desktop(读状态/启停独立窗口) |

架构(与上游 Coopanion、takboo/dsh-coopanion 同源):

DSH 宿主进程 └─ 插件 index.js ├─ 起一个本地 bridge 服务(刻意不在 /api 下 → 不走鉴权) └─ spawn Electron 子进程(desktop/main.cjs) ├─ 自己的静态服务(镜像 DSH 路由形状 → client.js 一个字不用改) ├─ 宠物窗口(透明/穿透/置顶) └─ 聊天面板窗口

关键:独立窗口连的是同一条 channel,因此 pet_ 工具、状态气泡、语音、审批 全部照旧可用——不是另一套。

DSH 没开时:她仍会出现(静态资源由 Electron 自己的服务提供),只是没有「Agent 指挥」的能力。

怎么用起来 / 怎么排查:见 docs/standalone-usage.md(操作指南 + 常见现象对照表)。

Electron 运行时:作为 optionalDependencies 声明,npm 安装时自动带(约 151 MB)。
也可以自己在设置里指定路径;找不到时独立窗口不可用,但本体照常——不假装成功。

独立预览页

访问 /api/dsh-coopanion-pet/preview 打开一个带地板与墙的干净页面,只为「单独看她」——调形象、录屏、给别人演示。它加载的是同一套客户端模块,因此能力与 DSH 页面里的她相同(说话、走位、收工具命令)。左上角有昼夜切换按钮。

装扮

- 8 套配色:DeepSeek / Harness / ChatGPT / Claude / Gemini / Qwen / Kimi / MiniMax
- 2 套服装:女仆装、冬装
- 面板可拖动,位置会记住

走路足迹与帧率

- 她走动时身后留一串渐隐的脚印(右键菜单或设置面板可关)
- 帧率三档:省电(静止 12fps / 活动 30fps)、平衡(静止 24fps)、流畅(跟随屏幕)
- 静止时自动降帧——呼吸、眨眼这类慢动画看不出来,但省下大半 CPU

全局说话热键

- 在任何页面里按 Ctrl+Shift+. 她就说一句(按平台自动显示 Ctrl 或 Cmd)
- 走 DSH 的 shortcuts 服务,因此输入框里打字时不会误触发,也不抢终端与弹窗的键位
- 这个组合键会登记进 DSH 的快捷键速查,可在设置里改
- 拿不到 shortcuts 服务时(某些组合里没有)静默跳过,桌宠本体不受影响

她自己能改什么(selfAdjust,与上游同义)

上游把「她能自己动哪些设置」分成两级——她自己的外观与习惯可以直接改, 碰到你的屏幕、耳朵与称呼的要先问。本插件照搬这套边界:

| 档位 | 行为 |
| off 什么都不许 | 一项都不改,连问都不问 |
| default 外观可以(默认) | figure / scheme / roam / snoreSeconds / 状态气泡 / 足迹 / 帧率 直接改;sound / scale / bubbleSize / fontSize / theme / hoverButtons / user / 六类音效 / 光晕 先问 |
| any 都可以 | 全部直接改 |
| custom 逐项定 | 设置面板里逐项打勾;没勾过的项按 default 那张表算(上游同义) |

她问的样子与上游一致——气泡上三个选项:

| 选项 | 结果 |
| 可以 | 这次改,偏好不变 |
| 以后都行 | 这次改,并把这几项记成 custom 下「可直接改」——以后不再问 |
| 不用了 | 一项都不改,工具回报 declined(模型据此不再重试) |

没答(超时 120 秒、按 Esc、纯净模式里气泡不弹)与「拒绝」是两回事,分别回报 no-answer 与 declined——前者可以再问一次,后者不该重试。

CUA 审批(可选)

装了 dsh-computer-use-win 时,它的工具(mcp__wincu__)要动用户的电脑之前,由桌宠当面问一句,而不是交给一个看不见的对话框。

| 档位 | 看屏幕(截图/查询/读窗口) | 操作(点、敲、拖) |
| 每轮都问 | 问 | 一轮只问一次,答完本轮不再问 |
| 动手前问(默认) | 不问 | 一轮只问一次 |
| 问一次 | 不问 | 问一次,答「可以」后 30 分钟内不再问 |
| 不问 | 不问 | 不问 |

- 她不在时不抢审批:放行给 DSH 自己的通道,免得把用户的 CUA 卡死
- 超时、拒绝、政策层出错一律按「不许」处理(fail closed),不重试
- 只拦 mcp__wincu__,其余工具一律原样放行——本插件不做通用审批
- 档位在设置面板「电脑操作审批」一行改,pet_cua 也能读改

多语言

- 界面文案有 11 门(与上游一致):中文、英文、繁中、日、韩、德、法、意、拉美西、巴西葡、俄
- 默认自动跟随浏览器语言(zh\* → 中文,其余先落英文),也能在设置面板里手动指定
- 改完语言立刻重挂桌宠,不必刷新页面——界面文案在模块加载时定下,因此语言(与「显示桌宠」开关)一变就重建整层
- 配色名(DeepSeek、Claude…)是品牌名,不翻译

智能体能指挥她(P2)

装了本插件后,智能体多了 6 个工具,可以在对话中直接让她做事:

| 工具 | 做什么 |
| pet_say | 让她在气泡里说话,支持 【表情, 动作】 与 <表情> 标记 |
| pet_ask | 让她问用户一个问题(最多 3 个选项 + 自填框),用户的回答会作为新消息回到会话 |
| pet_walk | 走到屏幕某个横向位置,可让她跑 |
| pet_act | 做一个表情或动作(19 种表情、14 个动作) |
| pet_dress | 换配色或服装 |
| pet_status | 查她现在在不在、在哪、在做什么 |
| pet_quiet | 让她安静 N 分钟:关音效、不走动、状态气泡让位,到时自动恢复。不动用户的偏好 |
| pet_set | 改她自己的体型、漫游倾向、音效、状态气泡、走路足迹、帧率。其中碰到用户屏幕/耳朵的项,她会先在气泡里问一句(可以 / 以后都行 / 不用了),工具等答案再回报 |
| pet_listen | 录一小段话,交给 DSH 的语音输入插件识别,返回文字(可选,见下) |
| pet_speak | 用真实语音朗读一小段话(可选,见下) |
| pet_voice | 列出可用的麦克风,或换一个(可选) |
| pet_cua | 读或改电脑操作的审批档位(可选,见下) |
| pet_desktop | 读独立窗口的状态,或打开 / 关掉它(可选:需要 Electron,见下) |

她不在(没有页面打开)时,工具会如实回报 offline 而不是假装成功。

语音(可选,不声明为必需依赖)

pet_listen 与 pet_speak 是条件可用的:宿主装了对应插件就能用,没装就如实回报、不假装成功,也不会因此让桌宠本体加载失败——这两个能力不写进插件的 inject(inject 是必需依赖,缺一个整插件就加载不了)。

| 能力 | 接的是什么 | 未装时的表现 |
| 听(识别) | DSH 自带的 @deepseek-ai/dsh-experimental-voice-input-bundle(SenseVoice 本地识别) | pet_listen 返回 no-speech-plugin,并说明装什么 |
| 说(合成) | dsh-xiaomi-tts 注册的同源路由 | pet_speak 返回 no-tts-plugin,并说明装什么 |

识别不自己搬 ASR:DSH 的 speechToText 是宿主侧服务,浏览器端插件运行时拿不到(客户端插件只有 slots/connection/loader/modules),因此由宿主半边调它,页面只负责录 WAV。
合成则绕宿主一圈:路由本来就能直接 POST,但走宿主能给出「没装那个插件」这句人话,而不是让页面拿到 404 去猜。

不需要读它们的配置、也不需要 API key:识别用宿主已选好的识别器,合成由 TTS 插件自己去调上游。

按住说话:Shift+Space(在 DSH 页面里按住就说,松开即识别)。
走页面内的 keydown/keyup——DSH 的 shortcuts 服务只有 keydown、没有 keyup,因此拿不到「松开」的时机,只能自己监听。代价:只在页面有焦点时生效。

录音时看得见:她头顶会显示「正在听 3.2s」+ 音量条,松开后显示「识别中…」。
这是替代原计划「假流式」的方案——实测分片识别比整段更慢更不准(见 docs/risk-verification.md),而「录的时候看得见」才是真正缺的那块体验。

识别结果的出口处理(照搬上游 src/asr/result.ts 的判据):
- 幻觉过滤:模型在没人说话的音频上会吐出训练语料里的字幕(「谢谢观看」「字幕由…」),靠内容分不出真假,只能按名单拦;Whisper 类模型还会把声音标在括号里([Musik]、(звук пива))。
- 繁简归一:逐字判断,只换表里有的字。不引 OpenCC(内联常用字表,覆盖范围小于上游,如实说明)。

识别结果去哪:两条路,运行时自动选。
- 填进输入框(理想):走 DSH 的 conversation.input.activity 槽拿 inputActions;
- 送进会话(降级):那个槽是 single 类型且被官方语音输入插件独占,装了它就填不进去,这时把识别文本作为一条用户消息发出(等价于「你说了这句话」,区别是会直接触发一轮对话)。

先探测、再申请麦克风:pet_listen 会先 GET 一次 /speech 问「你能不能听」,确认可用才去 getUserMedia。否则没装语音插件时用户会先看到一个麦克风权限框,白点一次拒绝——而他拒绝完拿到的还是「没装插件」。已验证:未装时调用前后 navigator.permissions 的 microphone 状态都停在 prompt,即从未申请过。

她记得自己站哪儿

刷新或重开页面后回到上次的位置(存的是占窗口宽度的比例,换窗口大小也不会跑出屏幕)。

设置面板与提示词(P2)

- 设置页多出一栏「桌宠」,25 行都能改(custom 档多一行逐项开关),改完立刻生效(不必重载):显示开关 / 状态气泡 / 走路足迹 / 双击说话 / 体型(滑块 0.5–10 倍)/ 气泡框大小 / 字号 / 轮廓光晕 / 主题(跟随·浅·深)/ 称呼 / 漫游 / 帧率 / 触摸唤醒 / 悬停按钮(勾选最多 6 个)/ 音效总开关 / 六类声音分开关 / 鼾声时长 / 她自己能改什么(含 custom 档的逐项开关)/ 电脑操作审批 / 语言(11 门)
- 系统提示词里多一段,告诉模型她存在、有哪些工具、什么时候不该用 (不要为了让她说话而每轮都调用一次工具,她是背景陪伴不是旁白)

形象、动画与渲染内核 原样取自 Coopanion,未作修改。

安装

需要一个带 Web UI 的配置档(dsh web、DSH 桌面端,或任何含 @deepseek-ai/dsh-web-app 的组合)。

从 npm 装(推荐):

dsh plugin --profile <你的profile> add dsh-coopanion-pet

指定版本:dsh plugin --profile <你的profile> add dsh-coopanion-pet@1.9.1 (npm 上没有 1.9.0——它只作为代码与 tag 存在,请用 1.9.1。)

从 GitHub 装(跟仓库 main 分支):

dsh plugin --profile <你的profile> add "github:wangquanqing/dsh-coopanion-pet"

从本地目录装:

dsh plugin --profile <你的profile> add "file:D:\path\to\dsh-coopanion-pet"

装完重启宿主(浏览器刷新不够,客户端模块需要重新装配)。桌面端由 Electron 独占管理其配置档,请在桌面端的插件管理界面安装,不要直接对 desktop 配置档执行上面的命令。

卸载:dsh plugin --profile <profile> remove dsh-coopanion-pet。

它做了什么

单包双面,两个半边各司其职,都不修改任何内置包文件:

| 半边 | 文件 | 职责 |
| 宿主 | index.js | 静态路由;采集运行状态经 SSE 下发;注册 pet_ 工具;收页面回传 |
| 宿主 | lib/channel.js | 双向通道:命令编号、下发、等回执、超时兜底、离线判定 |
| 宿主 | lib/tools.js | 六个 pet_ 工具的定义与执行 |
| 宿主 | lib/define-tool.js | 把属性级 required: true 转成标准 JSON Schema(见下) |
| 宿主 | lib/optional.js | 可选接缝:探测语音识别服务与 TTS 路由,不可用时返回 null |
| 宿主 | lib/cua-policy.js | CUA 审批政策层(四级档位 + 让位);判定抽成纯函数 cuaGate,方便单测 |
| 客户端 | client.js | shell.overlay 插槽里的桌宠,跑 WebGL 循环,收命令、发回执 |

路由用 prefix 而非 exact,因为形象包内部有相对路径导入(whale/figure.js → ../kit/rig.js、纹理 tex/.png),只有路径形式的 URL 才能让浏览器按相对关系解析成功。

五个 HTTP 端点

| 端点 | 方向 | 用途 |
| /api/dsh-coopanion-pet/assets/ | 宿主 → 浏览器 | 渲染内核、形象包、纹理 |
| /api/dsh-coopanion-pet/events | 宿主 → 浏览器(SSE) | 运行状态;智能体下发的命令 |
| /api/dsh-coopanion-pet/report | 浏览器 → 宿主(POST) | 命令回执、用户回答、桌面侧状态 |
| /api/dsh-coopanion-pet/speech | 浏览器 → 宿主(POST) | 录音(base64 WAV)→ 识别文字;没装语音插件时回 501 |
| /api/dsh-coopanion-pet/tts | 浏览器 → 宿主(POST) | 文字 → 合成音频;没装 TTS 插件时回 501 |

不用 DSH 的 RPC 做上下行:本插件只需要「推一件事、回一件事」,自己的路由更直接,也不依赖宿主内部接口的稳定性。

状态为什么要绕宿主一圈

探针实测(dsh 0.2.0-rc.2)显示,浏览器端的插件 apply 运行时只有 slots、connection、 loader、modules 四个服务可用,sessions、conversation、theme、locale 都取不到;而 shell.overlay 是 root 作用域插槽,框架注入的标准 props(GlobalStandardProps)是空接口,也没有会话信息。所以状态气泡不能由客户端自己取数。

宿主侧则拿得到:agent/status 事件(一轮对话的开始与结束)与 tools/execute 钩子(每次工具调用)。
因此状态由宿主采集、经 SSE 下发,客户端只负责显示。同一条通道也让 pet_ 工具能反向驱动页面。

工具定义为什么要自己转 schema

DSH 官方的工具都用 defineTool() 造,它把「属性上写 required: true」的写法提成对象级的
required: [...] 数组。直接调 tools.register() 绕过这一步,注册会报
required is not supported on type "boolean"。

lib/define-tool.js 做同一件事,但不依赖 @deepseek-ai/dsh-tools——那是宿主内部包,版本一变就可能对不上。转换只有两条规则:标了 required: true 的属性名进 required 数组,属性自己的那个标记去掉。

资源构成

assets/
├── kit/ Coopanion 的渲染内核(约 1.2k 行,自包含,原样复制) │ ├── rig.js Live2D 式 WebGL sprite rig:网格蒙皮 + 形变器树,零依赖 │ └── body.js 身体仿真:走路、跳跃、拖拽、19 种表情、18 个动作、粒子、物理 ├── whale/ DeepSeek 鲸鱼娘形象包(原样复制) │ ├── figure.js 把每帧转成 rig 参数,为头发/裙摆/尾巴跑弹簧,绘制表情 │ ├── model.json 骨架、部件、形变器、配色方案定义 │ ├── tex/ 25 张身体部件纹理(deepseek 配色) │ ├── feat/ 25 张表情贴图(眼、嘴、腮红、皮肤) │ ├── outfits/cape/ 20 张冬装纹理 │ └── schemes/ 其余 7 套配色,每套 50 张纹理,共约 9.6 MB ├── sfx.js 音效:Web Audio 合成,无音频素材(移植自 web/sound.js) ├── bubble.js 气泡:打字机、标记、提问与选项(移植自 pet-app.js 的 say/ask 分支) ├── bubble.css.js 气泡样式,配色对接 DSH 主题令牌 ├── status.js 状态气泡:接 SSE,让位于对话(移植自 pet-app.js 的 stepStatus) ├── dress.js 装扮面板:配色与服装切换(新写) ├── i18n.js 界面文案的中英词表(新写) ├── trail.js 走路足迹(移植自 pet-app.js 的 trail 段,去掉对话栈后重写) ├── voice.js 录音(WAV 封装)与合成音频播放(新写) ├── hotkey.js 全局说话热键(走 DSH 的 shortcuts 服务) ├── mic-worklet.js 麦克风采集 worklet(原样取自 Coopanion) ├── ui.js 悬停按钮条与右键菜单(新写,上游这些由 World 协议驱动) └── ../lib/ 宿主半边(通道、工具定义)

合计约 15 MB,其中 9.6 MB 是 7 套可选配色;只用默认配色的话实际加载约 2.5 MB (纹理按需拉取,切换配色时才加载那一套)。全部本地加载,无外部请求、无遥测。

渲染内核为什么能直接搬

kit/rig.js 零 import,kit/body.js 只依赖 rig.js,形象包通过 opts.kit 注入渲染器。
这套内核不依赖 Coopanion 的 World 协议、socket 或 Electron,是干净的可移植单元。

已做的适配

移植时改动集中在这几处,其余照搬:

1. 指针改为坐标命中判定。 Coopanion 里桌宠跑在 Electron 透明窗口里,靠窗口级 setInteractive 切换鼠标穿透。浏览器没有这个概念。本插件让覆盖层与其中所有元素始终 pointer-events:none,把指针处理挂在 window 捕获阶段,用渲染内核每帧回报的命中圆按坐标判定:命中她本体、或命中她自己弹出的按钮条与气泡时才拦事件,否则原样放行。

这样处理还避开了一个陷阱:pointer-events 从 none 切到 auto 时,浏览器不会把已在该位置的指针重定向过去,因此「悬停时才激活热区」的写法在真实点击下并不生效。

2. 不调用 set({ theme })。 渲染内核的该方法会写 document.documentElement.dataset.theme,与 DSH 自己的主题机制(body[data-ds-dark-theme])冲突。配色改由内核 CSS 的 prefers-color-scheme 与气泡 CSS 的 DSH 令牌各自判断。

3. 帧循环加了 dt 上限。 标签页切回或系统休眠后 dt 会非常大,不限制的话她会一帧冲过整个屏幕。

4. 音效去掉音频文件支路。 上游会加载形象包自带的音频,本插件只用合成音,因此 sfx.js 去掉了 usePack 与解码逻辑,play 直接进合成器。

5. 气泡按需创建、随桌宠卸载。 上游的气泡是页面固定元素,这里由插件创建并在卸载时移除,不留残余。

6. 状态来自宿主而非页面。 见上文「状态为什么要绕宿主一圈」。

7. 装扮面板重写为插件内面板。 上游是独立页面(dress.html + dress.js),本插件把它做成跟随桌宠的浮层,只保留配色与服装两条轴。

8. 状态上报不用 keepalive,且在途只留一条。 她走动时位置每帧都在变,状态上报是高频的;浏览器在同一个 keepalive 请求还没落地时再次发起会把前一个取消掉(表现为 ERR_ABORTED),于是每次上报都变成「一次成功 + 一次被取消」。因此改为普通 fetch,并把位置量化到 10px 粒度,只有跨过一格或模式/装扮变了才发。用户的回答与命令回执走另一条必达路径(不受在途限制)。

验证状态

在真实浏览器(Edge headless + SwiftShader)里对 DSH Web UI 做过端到端验证:

v1.9.1 pet_set 的同意流程(真浏览器 + 25 条单测):

| 项 | 结果 |
| 默认档只改 roam | 通过 —— 直接 {ok:true, applied:{roamIndex:2}},不弹提问(v1.9.0 这里会被整条拒绝) |
| 默认档改 sound | 通过 —— 弹三个选项 1可以 / 2以后都行 / 3不用了;点「可以」后 {ok:true, applied:{sound:false}} |
| 点「不用了」 | 通过 —— {ok:false, reason:'declined', refused:['sound']},值没变 |
| off 档 | 通过 —— 连问都不问,{ok:false, reason:'self-adjust-off'} |
| 点「以后都行」 | 通过 —— 偏好变 selfAdjust:'custom' 且 selfAdjustCustom.sound:true;再改同一项不再弹气泡 |
| 单测 | 通过 —— 25/25:四级分级、patch 键映射、只判本次改的键、readConsent 三种解读、pet_set 四条回报路径、设置面板 custom 档逐项开关 |
| 控制台错误 | 无 |

这两套验证都在仓库里,可以自己跑:

npm test # 25 条单测,无需浏览器,约 0.4 秒
npm run test:e2e # 真浏览器(Edge headless)跑上表 5 个用例,约 40 秒

test/e2e/ 里是一台最小验证台:它复刻宿主的四条路由(资源 / client.js / 预览页 / SSE)与 report 端点,于是 client.js 一个字都不用改就能在真浏览器里收命令、点气泡。浏览器路径可用 CPN_E2E_BROWSER 覆盖。

发布件验收(2026-10-20):npm 1.9.1 发布后,从 registry 拉包装进临时 profile 验过——dist-tags.latest = 1.9.1、gitHead = 1a3ad65(与 main 一致)、tgz shasum 与本地构建逐字节相同、docs/ 九个 md 齐全且 README 里那个链接点得开、apply 是函数、13 个工具造得出来。

| 项 | 结果 |
| 桌宠渲染 | 通过 —— 覆盖层与画布存在,WebGL 上下文存活,逐像素采样确认约 5.5k 个不透明像素 |
| 画面在动 | 通过 —— 相隔 900ms 的两次采样像素值不同 |
| 拖拽 | 通过 —— 抓取本体左拖 300px,实际位移 300+px |
| 界面穿透 | 通过 —— 点她本体:界面收到 0 次事件;点界面中央:收到 1 次 |
| 气泡打字机 | 通过 —— 连续采样文字长度递增(1,3,4,…,8,10),标点处有停顿,说完自动收起 |
| 表情动作标记 | 通过 —— 【happy, jump】 触发新模式序列 crouch,air,land |
| 提问气泡 | 通过 —— 3 个选项 + 自填框,点击后回调触发并收起 |
| 悬停按钮条 | 通过 —— 靠近她时出现 6 个按钮,点击生效 |
| 右键菜单 | 通过 —— 8 个菜单项,Esc 可关闭 |
| 装扮面板 | 通过 —— 8 套配色 + 2 套服装,切换后纹理真的换了且画面仍在动 |
| 状态气泡样式 | 通过 —— 状态类样式与思考动画规则已注入 |
| 状态链路 | 通过 —— 宿主 emit('agent/status','running') 后,SSE 依次推出 none → think → none |
| 音效模块 | 通过 —— 6 类声音,AudioContext 可用 |
| 主题未被污染 | 通过 —— html 上无 data-theme |
| 控制台错误 / 失败请求 | 均无 |

P2 工具闭环(用一个会回执的客户端扮演页面,让探针调真实工具):

| 项 | 结果 |
| 工具注册 | 通过 —— 6 个 pet_ 全部注册,disposer 都是函数 |
| 命令下发 | 通过 —— 通过 SSE 依次推出 5 条命令帧,编号 c1–c5、参数完整 |
| 回执闭环 | 通过 —— 扮演页面对 5 条命令全部回执 204,工具拿到 ok:true |
| 离线如实回报 | 通过 —— 没有页面连上时返回 offline 与原因,不假装成功 |
| 端点校验 | 通过 —— /report 只收 POST(GET 返回 405),坏 JSON 与未知 kind 返回 400 |

P2 设置面板与提示词:

| 项 | 结果 |
| 设置面板出现 | 通过 —— 设置页导航多了「桌宠」一栏,面板含 3 个开关 + 2 组选择 |
| 面板改设置生效 | 通过 —— 体型改到「大」后,她的画布从 107px 变成 135px |
| 提示词注入 | 通过 —— systemPrompt.assemble() 渲染出 7914 字符,里面含完整的桌宠段落 |

v1.0 收尾三项:

| 项 | 结果 |
| pet_quiet | 通过 —— minutes: 1 返回「安静约 1 分钟」,安静期间位置 0 → 0 不动,minutes: 0 立刻恢复 |
| pet_set | 通过 —— 依次改成 small / large / medium + roaming quiet,均返回 ok:true |
| 参数校验 | 通过 —— 空 pet_set 报 empty,size: 9 报 bad-size,roam: -1 报 bad-roam |
| 位置记忆 | 通过 —— 存下比例、重载后回到原处 |

v1.1 改进:

| 项 | 结果 |
| 状态气泡显示具体对象 | 通过 —— 正在看文件 · config.ts;a.ts → b.ts 切换会重绘 |
| 静止降帧省电 | 通过 —— 静止 48 次画布变化/2s(≈24fps),活动时 123 次/1.5s(≈82fps) |

v1.2 多语言(真实浏览器,Edge headless + SwiftShader):

| 项 | 结果 |
| 自动跟随浏览器语言 | 通过 —— 浏览器 zh-CN 时按钮条为「让她说句话/换个表情/…」 |
| 切到英文 | 通过 —— 偏好改 lang: en 后重挂,按钮条变 Say something / Change expression / …,装扮面板变 Dress up / Colours / Outfit / Maid / Winter cape |
| 状态气泡两语 | 通过 —— 中:正在看文件 · config.ts、正在跑命令 · pnpm test;英:Reading a file · config.ts、Running a command · pnpm test(六档全对) |
| 提问气泡 | 通过 —— 自填框 Say something else… / 按钮 Send |
| 设置面板语言行 | 通过 —— 「桌宠」栏有「语言」一行(自动/中文/English),点 English 后偏好落盘 zh → en 且整层重挂,面板标签同时变英文 |
| 显示开关即时生效 | 通过 —— 设置面板置 enabled: false 后层与画布归零;置回 true 后重新挂上并画出 5369 个不透明像素 |
| 「隐藏桌宠」仍说告别话 | 通过 —— 菜单点「隐藏桌宠」后气泡出现「那我先躲起来,」,约 1.6 秒后收起并落盘 enabled: false |
| 渲染回归 | 通过 —— 126×126 画布、约 5.2k 不透明像素,相隔 900ms 两次采样不同(画面在动) |
| 控制台错误 | 无 |

v1.3 足迹、帧率与可选语音(真实浏览器,Edge headless + SwiftShader):

| 项 | 结果 |
| 走路足迹 | 通过 —— 走之前 0 个点;走动中稳定 3 个点,位置与透明度随帧衰减(701px,0.57 → 659px,0.76 → 617px,0.94) |
| 关掉足迹 | 通过 —— trail: false 后连续采样可见点恒为 0 |
| 帧率:静止 | 通过 —— 省电 12fps、平衡 24fps、流畅 61fps(与档位表一致) |
| 帧率:走动 | 通过 —— 省电 30fps、平衡 61fps、流畅 61fps |
| 设置面板新行 | 通过 —— 新增「走路足迹」「帧率」两行,八行齐全 |
| 语音识别(装了语音插件) | 通过 —— 用 TTS 合成一句真语音喂给假麦克风,pet_listen 认出「你好,我是捉宠,请听我说一句话。」(原文「桌宠」被识别成「捉宠」,属识别器本身的误差) |
| 语音合成(装了 TTS 插件) | 通过 —— pet_speak 拿回 99884 字节 audio/wav,RIFF 头正确 |
| 未装语音插件 | 通过 —— pet_listen → no-speech-plugin、pet_speak → no-tts-plugin,都带一句「装什么」;桌宠本体照常渲染(5908 个不透明像素)且能说话 |
| 工具层 | 通过 —— 10 个工具注册;pet_listen/pet_speak 的 schema 转换、在线回执、离线、超时、空文本、越界参数逐条核对 |

v1.4 全局热键与 CUA 审批(真实浏览器 + 纯逻辑单测):

| 项 | 结果 |
| 全局说话热键 | 通过 —— 按 Ctrl+Shift+. 后气泡出现「这会儿挺安静的。」;默认关调试时零日志 |
| 热键登记进目录 | 通过 —— DSH 设置页「快捷键速查」里出现「让桌宠说句话 Ctrl+Shift+.」 |
| 热键不误触发 | 通过 —— 靠 shortcuts 服务的局部仲裁:输入框/终端/真弹窗区域不响应 |
| CUA 政策层单测 | 通过 —— 30/30:工具分类、四级档位 × 看/动、授权期、撤销、一轮一答、fail closed、参数校验 |
| CUA 闸门集成测试 | 通过 —— 15/15:只拦 mcp__wincu__(其余 5 类工具名放行)、离线放行、拒绝/超时拦下、政策层抛错也拦下、空调用放行 |
| 设置面板 | 通过 —— 九行齐全(新增「电脑操作审批」),四个档位按钮可点,点后偏好逐项落盘 |
| CUA 确认气泡 | 通过 —— 两个选项「1可以 / 2不要」,键盘 1、2、Esc 都能正确收掉 |
| 回归 | 通过 —— 渲染、双语、足迹、帧率、显示开关、语音安全跳过全部照旧 |

v1.8 对话栈与形象包系统(真实浏览器):

| 项 | 结果 |
| 40 对话栈渲染 | 通过 —— 推 3 条后正确显示,user/pet 分色、位置从下往上分层 |
| 40 让位对话 | 通过 —— 她说话时透明度 1 → 0.04,说完回到 0.91 |
| 41 包清单端点 | 通过 —— 报出内置 whale/coo 与已装的包 |
| 41 沙箱隔离 | 通过 —— sandbox="allow-scripts",主页面访问 frame 内文档被拒(跨源) |
| 41 CSP | 通过 —— default-src 'none'; script-src 'self'; connect-src 'none' |
| 41 沙箱真跑一个包 | 通过 —— 造了个「会动的小方块」包,切过去后沙箱回传布局(x=329)且画面在动(x 在 329–360 间摆动),控制台零错误 |
| 42 导入 | 通过 —— 选文件夹 → stage → install → 清单里出现新包 |
| 42 拒绝 | 通过 —— 空清单回 no-files;内置 id 不许覆盖 |
| 设置面板 | 通过 —— 「形象」行列出内置 + 第三方包,有「导入形象包」入口 |
| 控制台错误 | 无 |

v1.7 形象与预览页(真实浏览器):

| 项 | 结果 |
| 43 切到 Coo | 通过 —— canvas 消失、变成 8 个 SVG path(纯代码绘制)、画面在动(两次快照 882 → 958 字节) |
| 43 切回鲸鱼娘 | 通过 —— canvas 回来,偏好落盘为 whale |
| 43 设置面板 | 通过 —— 「形象」一行有两个按钮「鲸鱼娘 / Coo」 |
| 46 预览页 | 通过 —— /preview 打开后墙、地板、昼夜按钮都在,桌宠挂载成功(hasPet: true) |
| 46 昼夜切换 | 通过 —— 点按钮后 data-pv 由 light 变 dark |
| 44 语音模型状态 | 通过 —— GET /speech 报可用性与包名;设置面板有「语音识别」行与「检查」按钮 |
| 设置面板总行数 | 通过 —— 13 行(新增「形象」与「语音识别」) |
| 控制台错误 | 无 |

v1.6 语音增强(真实浏览器 + 纯函数单测):

| 项 | 结果 |
| 48 切句器 | 通过 —— 你好。我是桌宠!现在测试? → 切成三句 |
| 49 幻觉过滤 | 通过 —— 拦下「谢谢观看」「字幕由…」「[Musik]」「(звук пива)」「Bruit」「Thanks for watching」与纯重复「啊啊啊啊」;保留「你好」与「哈哈哈哈」 |
| 50 繁简归一 | 通过 —— 這個東西很好,我們說話的時候要聽清楚。 → 这个东西很好,我们说话的时候要听清楚。 |
| joinSpeech 分片拼接 | 通过 —— 英文片段补空格(hello world),中文不补(你好世界);与上游语义一致 |
| 52 按住说话 | 通过 —— 按住 Shift+Space 显示「正在听 1.4s」+ 音量条,松开显示「识别中…」 |
| 53 麦克风选择 | 通过 —— 扫描列出 3 个设备(含系统默认),设置面板有下拉框与扫描按钮 |
| 55 填输入框(理想路径) | 通过 —— 槽可用时拿到 insertText/setDraft/submit/captureInsertion 与 sessionId |
| 55 送进会话(降级路径) | 通过 —— 槽被官方语音插件占用时自动降级(mode: 'session'),insert() 返回 true |
| 录音反馈(替代 54) | 通过 —— 计时 + 音量条,状态机 idle → recording → transcribing |
| 控制台错误 | 无(降级路径的 503 是「测试页面没有活跃会话」,属预期) |

v1.5 十二项配置 + 三项小功能(每项都开独立页面、加载前写偏好,排除同步时序):

| 项 | 结果 |
| 59/60 气泡框与字号 | 通过 —— 字号 14.5px → 21.75px,内边距 14px → 21px,圆角 18px → 27px(框与字各自独立) |
| 61 主题三档 | 通过 —— --cpn-ink 浅 #1B1626 / 深 #FFFFFF,data-cpn-theme 同步;auto 跟随 DSH |
| 35 轮廓光晕 | 通过 —— glow=0 时 filter: none,glow=12 时 drop-shadow(… 18px) |
| 65 体型滑块 | 通过 —— 画布 133px(1 倍)→ 396px(3 倍),连续可调 |
| 57/58 声音分类与鼾声 | 通过 —— 六类开关与鼾声时长都落盘并生效 |
| 62 悬停按钮 | 通过 —— 勾选 [say,dress] 后按钮条只剩两个,改回六项即恢复 |
| 56 称呼 | 通过 —— 气泡变成「王哥,我在这儿呢。」 |
| 64 触摸唤醒 | 通过 —— poke 时戳她回话、none 时不回话 |
| 66 安全边界 | v1.9.1 重做 —— 四级 selfAdjust 落盘;ask 层的项真的用气泡问人(可以/以后都行/不用了),拒绝与超时分开回报。见上方「v1.9.1」表 |
| 67 十一门语言 | 通过 —— 每门 128 键完整无缺;日/韩/德/法/俄/繁中逐一实机核对 |
| 34 纯净模式 | 通过 —— #cpn-clean 下气泡与按钮条都不出现,本体照常渲染 |
| 63 双击说话 | 通过 —— 双击弹出输入框(带自填框) |
| 回归:拖拽 | 通过 —— 左拖 180px,实际位移 205px |
| 回归:界面穿透 | 通过 —— 点她本体界面收到 0 次;点界面中央收到 1 次 |
| 控制台错误 | 无 |

未验到的部分(如实说明):工具的端到端验证用的是「扮演页面的客户端」而非真实浏览器标签页——因为探针在服务启动后几秒就开始轮询,而浏览器要十几秒才把桌宠加载完并连上 SSE,两者的时序在这个验证环境里总是错开。因此「SSE 命令帧真的会让浏览器里的她说话/走路」这一段,是由以下两点合并支撑的:(1)命令帧格式与 runCommand 的处理分支已逐条核对;(2)页面自身的执行能力(api.say、装扮切换等)已在浏览器里单独验证通过。真机上两者是同一个页面,链路是通的,但没有一次「真实浏览器 + 真实工具调用」的合并验证,建议首次使用时留意。

配置

偏好写在 localStorage 的 dsh-coopanion-pet:prefs(JSON)。右键菜单与装扮面板改的设置即时生效并落盘:

{ "enabled": true, "sizeIndex": 1, "roamIndex": 0, "sound": true,
"startShare": 0.78, "scheme": "deepseek-maid", "statusBubble": true, "lang": "auto" }

| 键 | 含义 |
| enabled | 是否显示桌宠(右键「隐藏桌宠」会置为 false,改回需手动编辑) |
| sizeIndex | 体型档位:0 小 / 1 中 / 2 大 |
| roamIndex | 漫游倾向:0 安静 / 1 随意 / 2 活跃 |
| sound | 音效开关 |
| startShare | 初始横向位置,占窗口宽度的比例 |
| scheme | 装扮,配色-服装,如 claude-maid、deepseek-cape |
| statusBubble | 是否显示状态气泡 |
| lang | 界面语言:auto 跟随浏览器,或 zh / en 显式指定 |
| trail | 是否显示走路足迹 |
| fpsIndex | 帧率档位:0 省电 / 1 平衡 / 2 流畅 |
| cuaLevel | 电脑操作审批档位:ask-each-turn / ask-before-acting / ask-once / never-ask |
| selfAdjust | 她自己能改什么:off / default / any / custom(见上面那张表) |
| selfAdjustCustom | custom 档下逐项定:{ "sound": true, "scale": false, … },没写的项按 default 算 |
| scale | 连续体型倍率(0.5–10,1 为标准);sizeIndex 是三档快捷方式,两者同步 |
| theme | 气泡与本体主题:auto / light / dark |
| glow | 轮廓光晕强度(0–16) |
| dressPos | 装扮面板的位置(拖动后记下) |

从页面里驱动她

客户端暴露了 window.__dshCoopanionPet,供验证脚本与将来的 Agent 接线使用:

__dshCoopanionPet.say('【happy】你好呀。'); // 说话,可带标记
__dshCoopanionPet.ask('选一个?', ['甲', '乙', '丙']); // 提问
__dshCoopanionPet.do('jump'); // 表情或动作
__dshCoopanionPet.walk(400, true); // 走到横向 400px,true 为跑
__dshCoopanionPet.setScheme('claude-cape'); // 换装扮
__dshCoopanionPet.openDress(true); // 开关装扮面板
__dshCoopanionPet.layout; // 当前布局快照

后续

P2 及以上(注册 pet_ 工具让 Agent 直接指挥她、设置面板、语音、电脑操作等)见 ROADMAP.md,其中按移植难度与依赖做了分级。

许可

形象、动画与渲染内核来自 Coopanion,版权归 Pal AI Lab,AGPL-3.0-or-later。
本插件作为衍生作品同样以 AGPL-3.0-or-later 发布,详见 LICENSE。
预览分享卡片长什么样 ↓wangquanqing/dsh-coopanion-pet
分享 · DeepSeek Harness Hub
wangquanqing/dsh-coopanion-pet

在界面下沿养一只会走动、说话、跟随状态的鲸鱼娘

这个插件是你维护的吗?
作者本人可认领并核对本条目;认领通过后,详情页会显示「作者已认领」标记。
认领条目 →
回到顶部