[提案] cindy 槽新增 deposit_media 寄存通道:让 Mivo Canvas 插件里「用户自己的图」也能被 AI 改
- Dominant language
- TypeScript
- Stars
- 2.7k
- Forks
- 395
- Avg merge
- 21h 48m
- Merged PRs (30d)
- 776
Description
> **这是 Mivo Canvas 插件的宿主侧支持请求**(插件仓:[xindong/mivo-canvas](https://github.com/xindong/mivo-canvas),插件侧规划:[#297](https://github.com/xindong/mivo-canvas/issues/297) P1-#1)。
> 插件侧自己解不了——四条现有入仓通道逐条为什么不行见 §3。
**一句话诉求**:给 cindy 槽新增一个 `deposit_media` 寄存通道,让插件把**面板/沙箱内已持有的媒体字节**(用户粘贴、拖入、历史存量)存入媒体总仓、记到自己名下,换回指纹——从而这些媒体能作为 `edit_image` / `edit_video` 的源图使用。
---
## 1. 这个插件是什么,用户在里面干什么
Mivo Canvas = **Cindy 里的 Agent 原生无限画布**。用户和 Agent 共享同一块白板:对话里说的事,结果长在画布上;画布上的东西,Agent 看得见、改得动。
最核心的一条用法是 **AI 图像工作流**:用户先往板上攒参考图(网页复制、本地拖入),然后对 Agent 说「参考这张、生成 XX」「把这张的背景换成 XX」,结果直接落板并保留衍生关系。
## 2. 产品问题:同一块画布上,有的图能改、有的不能,而用户看不出区别
图进画布有几条路:**AI 生成**、**聊天附件过户**、**粘贴**、**拖入**、**早先就躺在板上的存量图**。目前**只有前两条路的图能被 AI 改**。
用户的期待极其朴素:
> 「画布上任何一张图,我说『把它改成 XX』,AI 就该能改。」
用户不知道、也不该知道「这张图是 AI 生的还是我拖进来的」有什么区别——两种图在画布上长得一模一样,都是一张图。但现状是:
```
用户:(从网页复制粘贴一张参考图)把这张图的背景换成雪山
Agent:这张图我改不了,麻烦你把图重新发到聊天里
用户:???图就在画布上,你不是看得见吗
```
更难向用户解释的是**同一张图会时好时坏**:插件侧账本是 LRU 200 条上限(`cindyplugin/src/agent/mediaLedger.ts`),AI 自己生成的图用久了也会被挤出账本 → 昨天还能改的图今天改不了。这在用户眼里不是"能力边界",是"产品坏了"。
**这个提案要买到的就一件事:抹掉「图的出身」这个用户看不见的概念,让「画布上的图 = AI 能加工的图」成立。**
### 等待期降级方案,以及它的代价
我们已经定了等待期方案(mivo-canvas#297 决策 2):改不了的图返回明确原因 + 引导用户把图重新发进聊天走 `args.attachments` 过户。
**这不是解决,是把问题甩给用户**——本来"选中图、说一句话"的动作,变成"找到原图文件、拖进聊天框、再把要求说一遍"。而且我们不会为此在插件侧养影子账本(把宿主的账重记一遍,只会越养越错),所以这条降级路径会一直是用户可见的粗糙感,直到宿主侧通道到位。
### 被一起卡住的能力:一个已建成、一个未开工
deposit_media 是下面两项的**前置**,但不是充分条件——这里写清楚,避免宿主侧误以为"合了这个就点亮了":
| 能力 | 现状 | 除 deposit_media 外还缺 |
|---|---|---|
| **局部重绘**(框选一块说改哪里) | **插件独立形态已实现**(mask 叠层 + 服务端合成);Cindy 形态下直接判 `unsupported`(`cindyplugin/src/agent/cindyGenerationPort.ts:202`) | 宿主 `edit_image` 需支持 mask / 框选参数——**另开一议**,不在本提案范围 |
| **图生视频** | 未开工 | cindy 详单加 video kind |
也就是说:**局部重绘这个功能我们已经做出来了,但在 Cindy 里点不亮**——源图挂不上号,mask 参数也递不过去。两道门缺哪道都亮不了。
## 3. 为什么插件侧解不了(四条现有通道逐条对照)
当前改图链路 `cindy-request kind:'edit_image'` 的 `hashes` 逐张查账验归属(`cindySlot.ts` 的 `resolveOwnedMedia`),**只认总仓里本意识名下的媒体**。而用户粘贴/拖入的图字节只存在于面板侧(IndexedDB / fs data 档案),不在总仓——于是"改图只认 AI 自己生成的图"。
| 通道 | 为什么不行 |
|---|---|
| `args.attachments`(聊天附件过户) | 要求用户把图**随消息重新发一遍**,画布上已有的图走不到这里 |
| network 槽 `as:'media'` | 需要公网 https URL;面板本地字节没有 URL,且 network 仅 https+默认端口,本地 loopback 不通 |
| fs 槽 `root:'data'` | 落的是私有数据目录文件,不产生总仓指纹,`edit_image` 不认 |
| pick 槽 / dir_deposit | 面向"用户亲选目录上传",不是程序化的单图入仓 |
## 4. 提案 API(与现有语义对齐)
```js
const r = await cindy.send({
type: 'cindy-request',
kind: 'deposit_media',
data: '', // 单张 ≤16MB,对齐 fs 槽单次写上限
label: '用户拖入的参考图', // 可选,入账备注/画廊 caption,对齐 as:'media' 的 label
callId: msg.callId, // 署名归因,同现有 kind
});
// 成功:{ ok:true, url:'cindy-media://blobs/<指纹>.<后缀>', hash, ext, bytes }
// 失败:{ ok:false, message }(超限/非受支持媒体/配额满)
```
- **魔数校验**:复用 `as:'media'` 已有的字节级类型识别,只收受支持媒体(图片/视频/音频/glb),识别失败拒收;
- **记账**:走现有 `storeBlob` + gallery ref 链路,出生=该意识,建议 provenance 标 `origin:'ghost-deposit'` 与生成产物区分;
- **配额**:建议每意识计入现有媒体配额口径,另加频控(如同一意识 ≥1s/张)防刷。
## 5. 为什么成本不高(对着源码盘过)
主机侧全部构件已存在,新 kind 基本是接线:
- `cindySlot.ts` 的 kind 分发表(`gen_image`/`edit_image`/…)加一项,无源图校验(`usesSources:false`);
- 落仓+记账:注入的 deps 已有"落 blob(SHA-256 主机算)+ 账本记账(出生=该意识)"能力(`cindySlot.ts:85`),`blobStore.ts` 现成;
- 字节类型校验:network `as:'media'` 的魔数识别逻辑现成;
- base64 处理先例:fs 槽已支持 `encoding:'base64'` ≤16MB 单次写;
- 面板→电子脑的字节递送:BroadcastChannel 现有机制,无需新通道。
预估改动集中在 cindySlot 新 kind + 政策参数(限额/频控)+ 装入确认框披露文案 + 测试。
## 6. 安全边界(需要设计拍板的点)
与现状对比,增量风险有限但需明确:
1. 声明了 network 槽的意识,本就能从自己白名单域拉任意字节入仓(`as:'media'`);deposit_media 把同样的能力给了**没有 network 槽**的意识——建议 deposit_media 单独作为 cindy 详单里的能力项(如 `"cindy": { "media": ["deposit"] }`),装入确认框如实披露"可将其持有的媒体存入你的媒体库";
2. 寄存媒体可进聊天卡片(card 槽只认本意识名下 `cindy-media://`)——与画廊/生成产物同权,建议与现有媒体审核口径拉平即可,不必单独加审;
3. 配额与回收:寄存物纳入现有 recycler 生命周期,卸载随意识回收。
## 7. 受益面:不止 Mivo Canvas
- **Mivo Canvas(直接诉求方)**:粘贴图、拖入图、存量图全部可 AI 改图;同时是局部重绘(#297 P2-#8)、图生视频(P3-#10)的共同前置(各自还有另一道门,见 §2 表);
- **任何"先在面板里攒素材、再让 Agent 加工"的插件形态**:只要面板持有字节而字节不在总仓,就撞同一堵墙。这是插件形态的结构性缺口,不是单个插件的功能请求。
Contributor guide
Research direction
Start in cindySlot.ts at the kind dispatch and the dependency setup around line 85; compare the existing network as:'media' validation and fs base64 handling. Trace blobStore.ts through storeBlob and gallery accounting, then inspect related tests and policy configuration. Done means a deposit_media request validates supported media, stores and attributes it, enforces size and quota limits, returns its fingerprint, and is covered by tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend-api-design
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100