Skip to content

对话文件授权与桌面保存交互:RC 实施任务书 ​

更新日期:2026-09-17(修订版 2:参考 Cherry Studio 的草稿附件与发送时入库流程)

适用项目:Infinia / FengYu 4.x,Vue 前端 + Electron 桌面 + Spring Boot 后端

编写时源码版本:4.0.0-rc.2(实施与发布时必须重新读取版本源)

状态:增量实施任务书;工作区已有部分相关实现,必须先审计差距。本文不证明现有实现正确、完整或获准发布。

1. 给实施 AI 的执行指令 ​

请依据本文完成“RC 必做范围”,先检查真实源码,再按依赖顺序实施、补充行为测试、验证并更新用户文档。不要只输出建议,也不要只合并附件标签后宣布完成。

本文是一份待执行任务书。只有用户明确要求执行本文时,才开始修改应用代码。当前生成本文的请求仅授权创建方案文档。

执行时遵守以下规则:

  1. 阅读根目录及改动目录适用的 AGENTS.md,检查 git status,保留用户已有修改。
  2. 本文中的文件路径用于定位,类名、接口名和数据结构建议不高于实际源码;先核实调用链和测试。
  3. 可以自行决定局部实现命名和拆分方式,但不得静默删减必做验收项。
  4. 不主动升级依赖、不改变插件公开协议、不顺带重写聊天或插件框架。
  5. 不自动提交、推送、打标签、发布或修改应用版本;这些操作需要用户另行提出。
  6. 修改插件契约或官方插件前读取 fengyu-plugin-dev;同步产品文档时读取 docs-updater;只有实际执行发布时使用 app-release。
  7. 若发现必做目标依赖大规模运行时重构,完成可独立验证的部分,报告具体阻塞、证据和剩余工作。不得把不完整实现描述为可发布。
  8. 本文不要求多代理执行。遵守当前会话对代理委派的限制。

最终交付至少包含:代码改动、必要测试、文档更新、验收结果、已知限制、RC 发布判断;未执行的检查必须明确列出。

2. 目标与 RC 决策 ​

2.1 必须达到的用户结果 ​

  • 一个本地文件或文件夹只出现一次,不暴露内部插件 ID。
  • 用户通过系统选择器添加草稿附件,选择阶段只记录描述,不生成插件授权;发送时创建只读副本,不再追加“授权给所有兼容插件”的确认卡片。
  • 文件资源、输出位置和待处理操作属于具体对话;新增对话不继承它们。
  • 旧对话后台执行不受新增、切换对话影响;回包不会落入另一个对话。
  • 桌面生成结果直接保存到本地,提供打开和定位;不以“下载”作为桌面主流程。
  • 后端拒绝跨作用域或已撤销资源引用;不依赖前端隐藏标签实现授权。
  • 保存失败不能报告成功,也不能立即删除唯一的生成结果。

2.2 是否可以在 RC 修改 ​

可以。授权跨对话残留应在正式版前修复;资源聚合、只读默认和本地保存闭环可以随修复进入下一 RC。

范围RC 决策
对话归属、异步竞争、统一回收、服务端校验必做,正式版阻断项
单资源展示、删除冗余确认、资料与输出目录区分必做
桌面保存、保存失败重试、打开与定位必做;若无法验证,明确该交付仍未完成
任意文件原地编辑、目录实时同步、跨重启自动授权不在本次范围
每对话独立 Worker、并发执行架构重构、逐工具动态沙箱重建后续独立设计;不得混入本次

发布使用下一个未占用的 RC 编号。不得假定远端尚无 rc.3,不覆盖已分发 RC。新 RC 通过验收后才能评估正式版;不按等待天数替代测试证据。

3. 已核实的现状与代码入口 ​

以下表格主要记录初始问题及定位入口,不代表当前文件仍保持原状。2026-09-17 工作区已存在相关未提交改动,务必以当前源码重新核实;具体增量入口见第 13 节。

入口现状 / 检查重点
frontend/src/views/AiChat.vue先选择再确认;直接逐条展示 activeFiles 和 pluginId;异步选择与确认返回的归属
frontend/src/stores/aiSession.ts共享 activeFiles;busy 时跳过清理;空对话复用;聊天回包追加授权
frontend/src/api/client.ts、types.ts原生路径、上传、撤销和聊天 DTO;目录默认可写
frontend/src/mf/desktop.ts桌面检测、文件/目录选择器封装
frontend/src/i18n/{en,zh}.json添加资源、资源权限、保存状态、错误文案
FengYu/.../web/controller/AiFileController.java文件授权入口,对话作用域绑定
FengYu/.../web/controller/AiController.java客户端引用、文本路径引用、暂存输出及终止回收
FengYu/.../web/controller/ConversationController.java持久化、删除对话与资源清理关联
FengYu/.../ai/ChatFileGrantService.java每个兼容插件生成独立引用;文本路径授权;暂存输出导出
FengYu/.../ai/ChatFileContext.java、AiToolFileInjector.java当前轮资源注入与解析
FengYu/.../ai/config/AiToolRegistry.java插件调用入口、默认输出位置、Flow 上下文
FengYu/.../plugin/runtime/PluginFileGrantService.java不透明引用、快照、读写目录集合、撤销、授权版本
FengYu/.../plugin/runtime/PluginProcessManager.java按插件复用进程,权限版本变化触发重启,沙箱根目录聚合
desktop/electron/src/ipc/dialog.ts、window/preload.ts系统对话框、窄范围 IPC 桥接
frontend/src/components/agent/FlowChatPanel.vue、FlowRunDialog.vue共享文件 API 的其他调用方,必须兼容

上表中的 FengYu/.../ 表示 FengYu/src/main/java/fan/summer/fengyu/。

重要事实:

  1. 只读原生资源当前使用副本快照;不能承诺外部修改自动同步。
  2. 可写原生目录当前可以直接指向真实目录;本次聊天新交互优先使用宿主保存输出,避免直接扩大 Worker 的本地写权限。
  3. FileRef 目前主要验证插件、引用和权限属性;需要添加聊天作用域与本轮成员校验。
  4. 底层可访问根目录按插件聚合;添加 conversationId 不自动构成操作系统级跨对话隔离。
  5. 权限版本变化可能重启 Worker;Excel/Python 等多步操作状态必须保护。
  6. 部分暂存导出失败路径只记录日志,之后仍清理暂存;保存闭环必须改变这个行为。

4. 产品行为契约 ​

4.1 资源入口 ​

“+”菜单:

  • 添加文件:系统文件选择器,默认只读。
  • 添加资料文件夹:系统目录选择器,默认只读,明确包括子目录。
  • 设置输出文件夹:系统目录选择器,授权宿主在该目录保存生成结果。

入口文案承担权限说明。输入文件/资料目录选择完成后只加入所属草稿的附件描述,不调用创建快照或插件授权的接口;发送时才创建资源副本。输出目录选择是独立的宿主保存授权,可当场登记,但不能创建 Worker 授权。取消选择不产生资源或授权,不再出现一次笼统的“批准发送”。不要求用户选择插件。

文件夹受现有数量、大小和路径安全限制;错误明确说明,禁止无提示截断。空输出目录必须支持,不能用“读取目录快照”的限制阻止选择空输出位置。

4.2 展示与交互 ​

text
发送前:
  [文件 contacts-template.csv · 只读 ×]
  [文件夹 参考资料 · 只读 ×]
  [保存到:报表输出 ▾]

发送后:
  用户消息保留附件记录
  输入区附近:[本对话资源 2 项 ▾] [保存到:报表输出 ▾]

输出结果:
  报表.xlsx · 已保存
  /用户选择的位置/报表.xlsx
  [打开] [在 Finder 中显示]
  • 非 macOS 使用对应平台文案“在文件夹中显示”。
  • 内部插件 ID 不出现于主界面;详情可显示实际使用过资源的工具名称。
  • 详情展示来源、只读状态、快照说明、刷新、移除。
  • 不同目录的同名文件保持独立,必要时显示父目录名消歧。
  • 草稿移除表示取消添加;已发送后移除表示禁止后续访问,历史消息保留“已移除”记录。
  • 不承诺移除能清除模型上下文或工具已经读取的数据。
  • “请求批准”仍管理工具操作审批;切换批准模式不扩展资源范围。

4.3 新建与切换 ​

  • 新增对话显示空草稿、空资源、空输出位置和空待批准列表。
  • 只有已加载、无消息、无草稿、无资源、无输出目标、无选择/上传操作、无执行任务的对话才可作为纯空白对话复用。
  • 未加载历史消息的对话不能因为 turns.length === 0 被判定为空白。
  • 有附件的空草稿在新建时保留在原对话,不作为空白复用;明确删除原对话才清理。
  • 普通切换保留原对话资源和草稿。所有弹窗、回包、批准动作绑定发起时对话。
  • 沿用现有生成并发限制,不顺带支持多对话并行生成。

5. 数据模型与授权契约 ​

5.1 推荐逻辑模型 ​

名称可以调整,但各层职责不可合并为一个全局数组。

ts
type ResourceRecord = {
  resourceId: string
  scopeId: string
  name: string
  kind: 'file' | 'directory'
  purpose: 'input' | 'output'
  access: 'read' | 'save-output'
  revision: number
  status: 'preparing' | 'ready' | 'revoked' | 'expired' | 'failed'
  source: 'desktop-native' | 'browser-upload'
  // 展示信息不是授权凭证;真实路径与引用由服务端管理。
}

type PendingOperation = {
  operationId: string
  scopeId: string
  draftRevision: number
}

type TurnResourceLease = {
  executionId: string
  scopeId: string
  resourceVersions: Array<{ resourceId: string; revision: number }>
}
  • scopeId 在选择资源前建立,推荐由后端生成并登记为当前应用会话拥有的作用域。
  • 数据库 conversationId 可以后续关联,不使用全局空值作为所有未发送草稿的共同作用域。
  • 本地 UI 数字 ID、数据库 ID、后端作用域 ID 分清用途;前端随意拼出的 ID 不自动代表所有权。
  • RC 可保留“一个资源对应多个插件 FileRef”的内部结构,但这些引用由资源记录统一拥有和回收。
  • 不再用 pluginId + 文件名 作为资源替换依据。
  • 原生资源身份由服务端规范化路径与来源判定;不要简单小写所有路径。浏览器资源使用上传资源 ID,不仅按文件名或大小去重。

5.2 API 设计要求 ​

实施者根据当前路由风格设计确切路径,建议覆盖以下操作:

操作契约
创建作用域返回服务端登记的作用域标识;可关联尚未持久化的草稿
准备发送资源选择阶段不调用;发送时接收作用域、发送事务 ID、附件描述,创建一组可回收的副本记录;提交后成为正式消息资源
列出资源仅返回该作用域的资源与保存目标
刷新只读资源成功后原子替换版本;失败保留可用旧版本
移除资源幂等;先禁用后续使用,再按执行引用计数回收
发起聊天携带作用域及资源 ID/版本;服务端解析 FileRef,拒绝其他作用域资源
保存结果通过已登记产物及输出目标操作,不接受任意源路径作为可信产物
关闭作用域禁止新操作,终止或安全收束执行,回收所有资源

客户端不得靠更换 scopeId 重绑一个已有资源。校验作用域属于当前认证上下文、资源归属、状态、版本和用途。若现有产品是单用户本地认证,沿用其身份模型,不凭空引入账户系统。

旧文件接口被 Flow 等模块共享:可以新增聊天专用聚合接口,或显式增加所有者类型。不能把所有旧请求默认归入“当前聊天”。Flow 仍按运行归属管理。旧聊天兼容入口不得绕过新校验;旧页面需要刷新时明确报错,不静默退回无作用域授权。

5.3 不变量 ​

  1. 所有资源有明确逻辑所有者;消息/对话引用拥有副本,执行任务持有独立访问租约。副本所有权不能等同于 FileRef 生命周期。
  2. 页面切换不改变所有权。
  3. 一个执行固定使用发起时资源版本,刷新不改写正在执行的快照。
  4. 移除后不接受新调用;已开始的调用可安全结束,释放后物理清理。
  5. 撤销网络失败不等于后端已撤销:前端保留待清理状态/重试,后端提供过期回收。
  6. 授权不可由模型输出、工具响应、附件正文或引用文档中的指令创建。
  7. 用户明确提供的路径也必须进入同一资源登记流程。仅引用或讨论路径不得被解释为写入授权。
  8. “保存输出”不授予 Worker 对整个本地目录的删除、重命名或任意修改权限。

6. 异步与执行状态机 ​

6.1 资源操作 ​

text
发起选择(conversationKey, operationId, draftRevision)
  → 系统选择器返回文件描述
  → 检查原草稿与操作是否仍有效
  → 只向原草稿追加 DraftAttachment,不创建插件授权
  → 用户发送:固定草稿快照并创建服务端发送事务
  → 副本准备完成后提交原对话消息
  → 清理事务已消费的原草稿附件;失败保留草稿

不要在回调中读取 activeId 决定归属。取消一个组件中的 pending 状态不足以取消网络请求,必须处理延迟成功回包。

新增、切换后仍允许有效选择完成到原草稿;删除或取消后的选择结果丢弃。发送准备产生的迟到资源由原事务接管或回收。网络重试用发送事务 ID 去重,避免重复复制文件或提交消息。

6.2 执行租约 ​

text
准备并提交本轮消息资源 → 校验作用域与资源 → 获取执行租约 → 固定本轮引用 → 执行
  → 成功 / 失败 / 取消 / 超时 / 断连
  → 幂等释放租约
  → 回收无所有者、无执行持有的引用

POST 成功但 SSE 从未连接、重复终止回调、取消与完成竞争都需要覆盖。不能依赖前端一定发送清理请求。

新对话应立刻呈现为空;旧任务仍写回原对话。全局 busy 若保留,应只表示现有执行限制,不用于决定文件属于谁。

6.3 Worker 与沙箱边界 ​

本阶段先实现宿主级作用域校验,但必须同时审计运行时实际可访问目录。

  • 不允许把所有闲置对话的可写本地目录长期加入同一插件 Worker。
  • 输出目录由宿主写入,Worker 只访问受控暂存;这是本阶段避免扩大写权限的主要路径。
  • 活动资源的授权创建、切换、撤销在安全执行边界处理;不要在多步任务中逐次修改授权集合触发进程重启。
  • 若共享 Worker 会保留旧作用域的文件内容或根目录权限,必须在作用域切换的安全边界清理/重建,或明确当前隔离不能满足目标并阻止发布。
  • 审计 Flow、插件 UI 与聊天共用同一 Worker 的情况。不能仅凭聊天全局 busy 推断系统不存在其他执行。
  • 不在 RC 新增并发 Worker 池;复用已有调度/锁,必要时串行化冲突执行,并给出明确等待状态。
  • 不宣称新增一个 scopeId 字段就提供 OS 级或恶意插件隔离。报告实际实现的边界和测试证据。

7. 桌面输出保存闭环 ​

7.1 输出目标 ​

每个对话 RC 最多一个输出目标,初始为空。用户主动设置后复用;切换目标只影响后续执行,当前执行固定持有原目标。记住系统选择器上次所在目录只是一项便利设置,不能自动授权新对话。

未设置目标时:用户手势触发系统目录选择器,避免重复或后台突然弹窗。若生成已经开始,先保留暂存产物,在原对话展示“选择保存位置”。取消选择不销毁产物。

7.2 产物状态 ​

text
generating → ready-to-save → saving → saved
                              ↘ save-failed → retry → saving
  • 每个产物有服务端登记 ID、执行归属、逻辑名称、暂存位置、保存状态与最终位置。
  • 插件不支持完整产物事件时,可在任务结束后从已知暂存根安全收集普通输出文件;拒绝越界与链接,不从任意模型文本路径推断可信产物。
  • 先落盘并确认结果,再报告 saved。文件生成成功和目标保存成功必须分开。
  • 只允许从本执行登记的暂存产物复制到已授权目标;防止 ..、符号链接、目录替换和路径逃逸。
  • 写入使用目标文件系统内的临时文件后替换等可靠方式;跨文件系统不能假定 rename 原子可用。失败不得破坏已有文件。
  • 默认同名保留两份,原子地避免竞争覆盖;显式请求覆盖按批准模式处理,不能静默 REPLACE_EXISTING。
  • 多文件保存按文件记录成功/失败,重试不重复覆盖已成功文件;不假装整个批次具备事务原子性。
  • 取消不强制导出未完成结果;已完成产物可保留为未保存状态。删除对话时清理这些暂存产物。

7.3 失败保留与清理 ​

失败/取消选址的结果应从普通“终止即删除”的 staging 生命周期中分离。选用应用管理的待保存产物目录及清单,避免启动清扫误删。

建议默认保留 7 天,并设置总容量限制和用户可见的清理入口;具体限额根据现有存储设置选取并文档化。容量不足时明确失败,不静默删除尚在重试的唯一结果。保存成功后清理暂存副本。

跨重启恢复待保存产物只恢复内容与状态,不恢复原目录写入授权。用户重新选择保存位置后才能重试。

7.4 Electron 桥接 ​

增加或复用窄范围的本地产物操作:打开、在文件管理器中显示。同步 preload、前端声明、封装、IPC 和测试。

  • 校验调用来源为可信应用渲染器;不能让任意插件 iframe 获取通用文件系统桥接。
  • 使用可验证的产物登记信息解析路径;不要把 renderer 传入的任意字符串直接交给 shell。
  • 本地打开只处理存在的受支持产物,拒绝 URL/自定义协议;脚本、可执行文件默认仅定位,不提供直接执行入口。
  • 处理 openPath 等返回的错误,定位/打开失败不能显示成功。
  • 老 preload 缺少新方法时显示不可用或升级提示,不抛未处理异常,也不退回不受限 URL 打开。
  • Web 端继续使用浏览器上传和保存能力,隐藏桌面专用入口;不得把本机路径当作浏览器可读取文件。

8. 按依赖顺序实施 ​

阶段 A:复现与契约 ​

  1. 阅读第 3 节入口、现有测试和实际 API DTO。
  2. 复现:添加未发送后新增、生成中新增、延迟授权后新增、同名文件替换。
  3. 梳理资源、执行、Worker、Flow 的当前所有权;确定作用域与产物 DTO。
  4. 先补会失败的行为测试,锁定跨对话与延迟回包问题。

完成条件:有可重复失败用例与明确状态转换,未修改公开插件协议。

阶段 B:后端归属与回收 ​

  1. 引入作用域和聚合资源登记,支持首次发送前创建;本地选择可以先使用稳定的本地草稿标识,开始准备发送或设置输出目标时再取得服务端 scope。
  2. 输入选择只保留草稿描述;发送时对原生路径、浏览器上传及明确用户路径创建宿主资源副本,登记明确所有者。
  3. 分离副本所有权和插件引用;聊天入口、工具引用注入与实际解析校验作用域、本轮成员和资源版本。
  4. 统一幂等撤销、执行租约、异常与超时回收。
  5. 保持 Flow 运行授权语义,覆盖旧 API 调用方。
  6. 在安全边界处理 Worker 权限变化,验证多步工具状态。

完成条件:跨作用域引用与撤销后调用被服务端拒绝;资源不依赖前端才能回收。

阶段 C:前端归属与资源展示 ​

  1. 把资源、草稿、输出位置、pending 操作移动到对话状态;去除共享数组的所有者角色。
  2. 当前资源列表改为活动对话的派生视图。
  3. 修复纯空白对话判定与异步回调归属;所有回包更新捕获的原对话。
  4. 一个资源一张标签,按资源整体移除;去掉插件 ID 与冗余确认卡片。
  5. 添加详情、快照说明、刷新、权限与错误状态,补中英文文案。

完成条件:前端验收矩阵通过,旧任务与新对话展示互不污染。

阶段 D:桌面保存与产物 ​

  1. 输出目标与只读资料分开建模,接入系统目录选择器。
  2. 复用受控暂存,增加产物状态与宿主保存结果。
  3. 修复失败即清理问题,完成重试、重名和容量处理。
  4. 完成打开/定位的受限 IPC、能力检测和错误反馈。
  5. Web 保持兼容,桌面不出现主流程下载按钮。

完成条件:真实本地文件保存与失败恢复验证通过,权限不因设置输出目录扩大到 Worker。

阶段 E:回归、文档与交付 ​

  1. 执行第 9、10 节检查;修复失败后只重跑受影响检查。
  2. 使用 docs-updater 更新相关中英文用户文档及 CHANGELOG;生成镜像,不手改 changelog 镜像。
  3. 汇总实现与范围差异、测试结果、剩余风险、RC 判定。
  4. 保持未发布、未打标签;实际发布等待用户指令。

9. 必须覆盖的验收矩阵 ​

ID场景预期
A01CSV 兼容多个插件只展示一个资源,整体移除回收全部引用
A02不同目录同名文件资源独立,内容不互相替换
A03同一路径重复添加/重试去重或刷新版本,不泄漏授权
A04已有附件但未发送时新增新对话为空,原草稿资源保留在原对话
A05未加载历史消息时新增不复用该历史对话为空白
A06生成中新增/切换原执行和结果归属不变,新对话无旧权限
A07系统选择器/上传/授权延迟返回回到原对话;已废弃操作的资源被回收
A08聊天接口延迟返回文本路径授权不添加到当前新对话
A09选择期间删除原对话回包不能复活对话或资源
A10刷新资源期间原任务执行原任务使用旧版本,后续轮次使用新版本
B01跨作用域伪造/重用引用服务端拒绝,不能仅前端过滤
B02移除后新工具调用被拒绝;既有调用安全释放
B03POST 后未连接 SSE超时回收,没有孤立执行租约
B04完成/取消/断连重复回调幂等清理,不重复导出或提前删结果
B05撤销请求网络失败显示状态、重试;服务端过期回收
B06附件正文/模型输出包含授权指令不建立文件权限
B07只读资源被请求写入被拒绝,不能靠工具参数提升权限
B08聊天与 Flow/插件 UI 共用 Worker没有跨作用域权限累积、错误重启或死锁
B09多步 Excel/Python 操作不因中途权限变化丢失进程内状态
C01已有输出位置生成文件直接保存本地,实际内容正确
C02无输出位置/取消选择不循环弹窗,保留结果等待选择
C03空输出目录可选择并成功保存
C04同名文件及并发冲突不静默覆盖;保留两份或明确批准
C05无权限/磁盘满/目标被删除标记失败,保留唯一结果,能重试
C06多文件部分保存失败成功失败分开显示,重试不重复破坏成功项
C07路径穿越/符号链接/替换目标目录拒绝越界写入
C08打开/定位及旧 preload平台行为正确,无未处理异常或任意路径入口
C09重启恢复历史与未保存产物元数据/产物恢复;本地写权限不自动恢复
D01浏览器与 Flow 文件流程不因聊天改造退化或被强制绑定当前对话
D02macOS/Linux 桌面启动现有稳定 E2E 通过
D03中英文、长文件名、窄窗口、键盘操作可操作、可读、无明显布局溢出

单测应使用可控制的延迟 Promise、假时钟和显式状态,不依赖随机 sleep。文件系统测试使用临时目录,不能读取或写入用户真实资料。

10. 验证命令与证据 ​

先阅读当前 package scripts、测试配置及 Maven 模块依赖,调整新增测试类名。以下是起点,不是假定已经执行的结果。

前端(工作目录 frontend/):

bash
corepack yarn run test:unit src/stores/aiSession.test.ts
corepack yarn run typecheck
corepack yarn run build

运行新增资源组件/状态测试;合入 RC 前执行完整 test:unit,以及被此次修改影响的其他测试脚本。

后端(仓库根目录):

bash
./mvnw -f FengYu/pom.xml -Dtest=ChatFileGrantServiceTest,PluginFileGrantServiceTest,AiControllerChatGrantLeakTest,AiControllerFileContextTest test

追加新增作用域、租约、保存失败恢复测试。若依赖需本地安装,按 README 使用 wrapper 补齐必要模块,不盲目运行全 reactor,也不以 -DskipTests 替代行为测试。

桌面(工作目录 desktop/electron/):

bash
corepack yarn test
corepack yarn run build:ts
corepack yarn run test:e2e

先核实 E2E 所需后端/前端构建。新增系统选择器测试采用受控替身;不要让真实系统弹窗或外部网址造成 CI 不稳定。不得启用 FENGYU_E2E_BROWSER_BRIDGE=1 作为普通发布测试的默认条件。

文档(工作目录 docs/,更新 CHANGELOG 后):

bash
corepack yarn run sync:changelog
corepack yarn run build

仓库根目录:

bash
git diff --check

实际发布另按 app-release 执行构建、审计、release contract、打包和 scripts/e2e-smoke.sh。裸 JAR smoke 不覆盖 Electron 链路;CI macOS/Linux 的稳定 launch E2E 必须通过。改了 electron-builder.yml 才需要同步相关包装契约断言,不能留下失配。

手动桌面验证至少记录:运行平台、应用构建、测试临时目录、选择/保存/失败重试结果,以及打开和定位的实际效果。没有测试的平台不得宣称已验证。

11. 历史数据、兼容与回滚 ​

  • 尽量使用向后兼容的可选资源元数据,不把进程内引用写进历史当作永久权限。
  • 旧对话没有资源字段时正常加载为空;无稳定资源信息时显示旧附件不可用,不猜测路径恢复。
  • 新字段若需要数据库迁移,使用仓库现有机制,提供旧数据测试;禁止删除或重写现有聊天记录。
  • 应用热更新时考虑 SPA 与旧 preload 暂时不同步,用能力检测明确降级。
  • RC 回滚保留原文件和聊天历史;资源缓存可以过期,但不能删除已保存用户产物。
  • 不以回滚理由重启旧的跨对话共享授权路径。若新交互必须回退,授权归属修复应保持有效。

12. 完成标准与实施报告模板 ​

以下条件全部满足才可标记本任务完成:

  • [ ] RC 必做范围已实现,未把延后范围混入主交付。
  • [ ] 新建/切换/删除/异步回包归属符合契约。
  • [ ] 单资源展示、只读默认、输出目标分离生效。
  • [ ] 后端跨作用域及撤销后调用测试通过。
  • [ ] Worker、Flow 和多步工具状态经过实际回归。
  • [ ] 本地保存、冲突、失败保留、重试、打开和定位闭环通过。
  • [ ] 历史兼容、重启失效与未保存产物恢复符合设计。
  • [ ] 中英文文档与 CHANGELOG 同步。
  • [ ] 必要构建和检查有结果,未执行项如实披露。
  • [ ] 未擅自提交、推送、改版本或发布。

实施 AI 最终报告格式:

markdown
已实现:说明用户可见结果及关键后端行为。

验证:列出实际执行的命令、结果和桌面场景。

授权边界:说明宿主校验、Worker 实际权限范围及残余限制。

范围差异:列出与任务书不同的实现和原因,不能省略未完成项。

RC 判断:可进入下一 RC / 不可进入,并给出具体证据或阻断项。

发布状态:未发布;若用户另行授权发布,引用相应发布结果。

只有用户另行要求发布时,才读取最新版本和远端标签、按发布技能执行下一 RC。应用发布不修改独立插件工具链版本。

13. 修订版实施基线:必须增量改造现有代码 ​

本节及后续章节明确修订版的技术契约。与此前方案的核心差别是:选择阶段只保存草稿描述;发送时创建宿主副本;每轮执行的 FileRef 单独建立和释放。 不能仍在选择时调用原来的 grant 接口,仅把界面文案改成附件。

2026-09-17 只读检查发现工作区已有下列改动,尚未在本次文档任务中验证:

  • ChatResourceScopeService:已有 scope、聚合资源、输出目标和 lease;addNative 等目前会立即调用授权服务。
  • ChatResourceController:已有聊天资源路由,需复用并调整语义,不另建第二套 scope 控制器。
  • ChatArtifactStore、Electron ipc/artifact.ts:已有产物管理与桥接,需逐条核对保存、归属、重启与清理契约。
  • aiSession.ts:已有按对话的 draft/resources/scope 和附件操作计数;应扩展为独立待发送附件,不退回旧的全局数组。
  • 后端、前端、Electron 已有相关测试和中英文文档改动;保留并修正测试,不删除失败断言以迎合实现。

先输出简短差距表:目标 / 已有实现 / 缺口 / 修改入口 / 验证方式,然后实施。不能因为已有同名类就认定功能完成,也不能重置未提交修改来获得“干净基线”。当前代码状态只是审计入口,不是后续永久有效的事实。

13.1 Cherry 参考范围 ​

参考提交:b112a46b0e2d93105540ddbc9234ce468e9e4b46,属于当时 main 源码,不代表其发布版。

只借鉴职责拆分和交互,不复制其整套存储实现。其发送附件批处理源码也注明非原子失败可能残留,FengYu 必须按下一节补齐幂等与回收,不能照搬该限制。

14. 四类对象及明确存储责任 ​

ts
type DraftAttachment = {
  attachmentId: string
  conversationKey: string
  kind: 'file' | 'directory'
  name: string
  source: 'desktop-native' | 'browser-file'
  selectionHandle?: string  // 推荐主进程登记的选择凭据,不是插件权限
  displayPath?: string      // 只用于展示,不构成服务端授权
  selectedSize?: number
  status: 'selected' | 'preparing' | 'failed' | 'unavailable'
}

type ResourceVersion = {
  resourceId: string
  scopeId: string
  revision: number
  snapshotId: string
  status: 'prepared' | 'committed' | 'revoked' | 'expired'
}

type PreparedSend = {
  sendId: string
  scopeId: string
  state: 'preparing' | 'prepared' | 'committed' | 'aborted' | 'expired'
  resourceVersions: Array<{ resourceId: string; revision: number }>
}

上面是逻辑类型,不要求逐字复制。浏览器 File 对象单独保存在会话内存映射中,不能 JSON 序列化后假装恢复了文件内容。

对象存储位置清理责任
草稿附件描述对话草稿;浏览器二进制对象在内存移除、发送成功后消费、删除草稿
宿主副本独立资源存储;和 Worker runtime-files 清扫边界分开无消息/对话引用且无执行持有后清理
执行 FileRef后端活动租约执行终止幂等释放;不直接删除仍被消息引用的副本
生成产物独立产物存储与状态清单保存成功或按明确保留策略清理

实现要求:

  1. 使用仓库已有持久化机制保存附件元数据及消息关联,不能仅在会话内存显示已发送附件。
  2. 明确 releaseGrant 与 deleteSnapshot 的不同职责。底层现有 owned 标志不足时补充资源所有者,不把仍引用中的副本交给通用 revoke 删除。
  3. 同一版本只创建一个逻辑副本;若既有沙箱要求插件专属物理副本,可以内部派生,但必须统一配额、归属和清理。禁止通过硬链接让可写 Worker 修改共享只读快照。
  4. 本轮结束释放权限后,对话资源仍可作为下一轮“继续”的输入重新派生权限。
  5. 跨重启保留副本与消息记录,但旧 FileRef 一律失效。恢复对话时资源显示待恢复访问;明确用户操作后重新建立引用,不自动恢复原目录写权限。
  6. 默认只读快照以发送准备期间读到的内容为准。发送前变化提示和发送失败时的原草稿必须保留;不承诺原文件实时同步。
  7. 对复制期间仍被外部程序修改的文件,检测可识别的大小/时间变化并拒绝或重试;不声称普通文件复制提供完整文件系统快照一致性。

15. 发送协议:避免重复消息、重复副本与草稿丢失 ​

15.1 推荐操作 ​

沿用现有 /api/ai/chat-resources 等命名空间,增加必要操作或在原接口中等价实现。不要把下列示意路径当成必须照抄的公开协议。

操作输入结果
prepareSendscope、sendId、原生选择凭据/上传内容、草稿附件 ID、已提交资源 ID/版本PreparedSend;新副本只归本事务所有,未向 Worker 授权
commitSend / chatscope、sendId、文本、固定资源集合、输出目标版本唯一 message/stream/execution 标识;提交所有权并启动原对话执行
getSendStatusscope、sendId当前状态及已经生成的标识,供不确定结果恢复
abortSendscope、sendId未提交则回收;已提交不作为“取消任务”的替代接口
cancelExecutionexecutionId沿用执行取消语义并释放 lease

可以合并 prepare 与 chat 为服务端单一事务入口,但必须提供等价的幂等状态查询与不确定结果恢复能力。

15.2 完整顺序 ​

  1. 捕获原对话、草稿版本、附件集合、文本和输出目标。生成一次性的 sendId。
  2. 防止重复点击产生第二次发送。准备期间可编辑新草稿,但不得修改已经捕获的发送载荷。
  3. 后端验证作用域和选择来源,对整个批次做路径、大小、数量、类型及配额校验。
  4. 创建宿主副本,按事务记录已完成条目;失败则回收本事务独占副本,不碰其他消息资源。
  5. 提交唯一用户消息及其资源关联;创建执行前确定本轮资源版本。
  6. 在安全调度边界获取执行租约、派生兼容插件的 FileRef、启动执行。
  7. 前端得到明确已接受结果后,只消费捕获的附件 ID 和对应草稿版本。不得把用户刚输入的新内容清空。
  8. 后续流失败也不撤销已经成功提交的消息;把失败展示在原消息上,允许显式重试执行。
  9. 执行结束释放 FileRef 与租约;副本依据消息引用保留。

15.3 幂等与异常规则 ​

  • 同一 sendId 同一载荷重复请求返回同一结果,不再次复制或发送模型请求。
  • 同一 sendId 不同载荷拒绝,并返回可识别冲突错误。
  • 连接超时不能被当作“未提交”。先查询状态,再决定重试或回收。
  • 服务端已提交但前端未收到响应时,通过 sendId 找回消息和流,禁止新建重复消息。
  • prepare 阶段失败保留草稿,标记具体失败附件;用户可移除失败附件后用新事务重试。
  • prepare 已成功、前端退出或从未 commit:服务端 TTL 回收孤儿事务;活跃事务不能被清扫。
  • 删除对话与 commit 竞争由服务端线性化:关闭后拒绝新提交,迟到副本安全回收。
  • 提交后按 sendId 去重的记录至少覆盖客户端合法重试周期;不可在执行刚完成就删除。
  • 不为了幂等把完整文件内容或敏感路径写进普通日志。

16. 原生选择与 Web 兼容的实现细节 ​

16.1 桌面输入 ​

  • 扩展原生选择器支持文件多选;为旧调用方保留兼容封装或同步所有调用点。
  • 推荐 Electron 主进程登记选择结果并返回不透明 selectionHandle 和展示元数据。其作用是证明选择来源,不开放 Worker 文件访问。
  • 后端兑现选择凭据时通过可信桌面桥接校验,不信任 renderer 自称“用户选了此路径”。若复用当前可信路径机制,先确认其边界并记录理由,不能用可伪造字段代替凭据。
  • 选择后立即进入草稿,禁止创建 FileRef、启动 Worker 或递归复制大型目录。
  • 发送前主进程/后端重新校验存在性、真实路径与文件类型;失效凭据提示重新选择。
  • 选择操作在对话切换后写回原草稿,在原对话删除后丢弃并释放凭据。
  • 本地草稿持久化路径只能用于展示;重启后的旧 selectionHandle 不自动有效。

16.2 浏览器输入 ​

  • 文件选择保留 File 对象,发送准备时才上传;不要求浏览器提供本地绝对路径。
  • 大文件进度属于原发送事务。刷新页面后失去未发送 File 对象时显示“重新选择”,不能拿文件名自动匹配。
  • 目录上传沿用相对路径验证;浏览器不能替代桌面输出目录授权,保留适合浏览器的保存行为。
  • 不给 Web 输入创建永久可写目录权限,不把桌面 IPC 能力暴露给浏览器降级路径。

16.3 文本路径 ​

明确用户要求处理的本地路径可在发送准备时进入同一管线,不另外调用旧的“即时全插件授权”逻辑。仅出现在引用段落、示例、附件正文、模型回复或工具结果里的路径不构成授权。

不要依靠更宽泛的正则表达式推断所有权限。若现有解析无法可靠区分用户意图,应生成待确认资源项或要求系统选择器,而不是静默授予写权限。整段用户请求“把结果保存到 X”也必须登记到当前作用域输出目标并走同一保存策略。

17. 增量实施拆分与退出条件 ​

顺序增量工作本步退出条件
1审计现有 scope/artifact/store 改动;写差距表每个必做目标有真实代码入口,不重复造服务
2增加 DraftAttachment,选择只写草稿;保留现有对话归属修复选择/取消零插件 grant;新增对话为空
3将资源存储从 grant 生命周期分离release lease 后副本仍可用;删除引用后回收正确
4完成 prepare/commit/status/abort 等价协议重复提交、超时和部分复制失败不泄漏、不重复发送
5执行边界派生/回收 FileRef,接回现有 scope 校验原任务状态正常;跨 scope 和撤销后调用被拒绝
6消息持久化附件元数据与版本;清理仅消费已发送草稿重启可见历史;新增编辑不被延迟回包清空
7补齐输出目标、产物重试、打开/定位已有实现成功真实落盘;失败保留;越界路径被拒绝
8执行全矩阵、同步文档、给 RC 结论所有阻断项有证据,未执行项明确披露

这些是逻辑实施批次,不授权创建 Git 提交。可以在兼容层中逐步迁移,但最终用户可达的聊天入口不得继续绕过新协议。Flow 旧授权保持其 run-scoped 行为;不得为统一代码而强行改成聊天 scope。

18. 修订版新增强制测试 ​

第 9 节全部保留,另增加以下用例:

ID场景预期
E01选择文件但不发送grant 数量不增加,没有快照复制,没有 Worker 启动
E02选择后取消/移除无后端资源残留;选择凭据释放
E03选择后原文件变化/被删除按发送时版本或明确失败处理,草稿保留
E04第 N 个附件准备失败本事务独占副本回收;前 N-1 个不会永久泄漏
E05同 sendId 重复 prepare/commit一个事务、一条消息、一次执行
E06commit 已成功但响应丢失状态查询恢复原结果,不重复发送
E07同 sendId 不同载荷冲突拒绝,不覆盖已准备资源
E08用户发送后继续输入/添加附件延迟成功回包只消费旧载荷,新草稿保留
E09本轮完成后说“继续”副本仍存在,重新建立本轮访问权限
E10执行完成但对话仍在活动执行引用释放;消息拥有的副本不被删除
E11删除资源与执行释放竞争逻辑撤销及时,物理回收只发生一次
E12重启时有未提交发送事务按清单回收;不删除已提交消息副本
E13历史附件加载展示名称/版本/状态;不复用旧 grant 或自动恢复本地写权限
E14再次执行已有保存产物只有显式加入对话资源后可作为输入,不自动开放任意输出路径
E15在宿主重启/权限变化后复用 Worker不残留其他作用域本地访问;有状态任务不在执行中被重启

至少在测试中观测资源存储、grant 数量、实际模型/执行调用次数,而不只是断言 UI 文本。现有 scope 和 artifact 测试应扩展;新增发送事务、消息关联与副本所有者测试。所有测试使用临时目录与受控执行替身。

19. 修订版 RC 决策与最终交付要求 ​

本方案可以作为下一 RC 的完整修复目标,但它包含状态与存储生命周期变更,不能当作纯 UI 调整发布。作用域校验、副本回收、发送幂等、Worker 状态和保存失败恢复属于正式版阻断项。

允许后移:实时目录同步、任意原地编辑、每对话独立 Worker、跨重启静默恢复授权。不能后移:选择不提前授权、发送失败保留草稿、已提交附件持久化、执行引用与副本解耦、跨对话校验。

如果时间不足,应延后正式版或明确缩减产品承诺并由用户决定范围,不能继续保留已知串权限问题。发布时读取当前 app 版本和远端标签,按发布技能选择未占用 RC;不要改动独立工具链版本。

实施报告还必须回答四个问题:

  1. 选择后、发送前,是否创建了任何插件访问权限?用测试说明。
  2. 一轮结束后,哪些对象被释放、哪些对象保留、谁负责最终清理?
  3. 发送响应丢失时,如何防止重复消息、重复副本和重复模型调用?
  4. 当前实现保证的是宿主级校验还是更强的 Worker/OS 隔离?有哪些实际验证?

给实施 AI 的启动请求:

请执行本任务书修订版 2 的完整 RC 必做范围。先审计并保留工作区现有改动,输出简短差距表,然后增量实施、测试并同步中英文文档。以“选择仅入草稿、发送时创建宿主副本、执行权限独立回收”为准。不要提交、推送、打标签、改版本或发布;最终报告验收结果与 RC 阻断项。

Last updated:

Released under the GPL-3.0 License.