# RFC 0010：Desktop 后台对话完成系统通知

> 状态：草稿
> 日期：2026-04-30
> 关联：`src-tauri`、`frontend/src/store/useStore.ts`、`frontend/src/pages/MainApp.tsx`

---

## 1. 背景

Desktop 模式已经支持多会话并发执行。当前前端在检测到非当前会话任务完成时，使用浏览器 `Notification` API 尝试提示用户，但该实现存在几个问题：

- 通知能力依赖 WebView 运行时，不是 Tauri 原生系统通知链路。
- 权限请求与发送逻辑散落在前端，缺少统一的 Desktop 能力边界。
- 用户切到其他应用、窗口被遮挡或应用在后台运行时，浏览器通知行为不够稳定。
- 通知内容无法复用后端运行时对 session、状态和错误的判断。

因此需要为 Desktop 模式补齐系统级通知能力：当后台会话完成或失败时，通过操作系统通知提醒用户，并保持前端状态更新与现有会话列表刷新逻辑不回退。

---

## 2. 目标

- Desktop 模式下，当非当前查看会话从运行中进入完成状态时发送系统通知。
- 通知必须使用 Tauri 原生通知能力，不再依赖浏览器 `Notification` API 作为主路径。
- 通知标题应包含产品名或明确的任务完成语义。
- 通知正文应包含会话标题；失败时应包含失败语义或错误摘要。
- 只通知后台会话完成，当前正在查看的会话不重复弹出系统通知。
- 通知权限应在应用启动或首次需要时以可控方式请求，权限拒绝时不影响对话执行。
- 通知发送失败必须降级为日志或前端静默失败，不得影响运行时状态同步。
- 保持现有前端会话列表刷新、消息快照同步和后台会话状态标记行为。

---

## 3. 非目标

- 首期不保证通知点击后跳转到对应会话；macOS 下应尽量支持点击或“显示”动作聚焦桌面端主窗口。
- 不实现通知设置页、免打扰时间段或通知偏好管理；通知显示时长由操作系统通知样式控制，应用不强行覆盖。
- 不实现 Server / Connector 远程通知。
- 不实现移动端推送通知。
- 不要求通知包含完整回复内容，避免泄露敏感对话。

---

## 4. 行为定义

### 4.1 后台会话完成

当 Desktop 前端发现某个 `session_id` 从运行中状态移出，并且该会话不是当前活动会话时：

- 发送系统通知。
- 标题建议：`天工 - 任务完成`。
- 正文建议：`「{session_title}」执行完成`。

### 4.2 后台会话失败

如果快照或运行时状态能够区分失败，应发送失败通知：

- 标题建议：`天工 - 任务失败`。
- 正文建议：`「{session_title}」执行失败`，可附加短错误摘要。

首期如果现有前端快照无法稳定区分后台会话失败与完成，可以先统一使用完成通知，但实现应预留失败分支。

### 4.3 当前会话

当前活动会话完成时，不发送系统通知。用户已经在当前页面内看到状态变化，重复系统通知会造成干扰。

### 4.4 权限

Desktop 启动后应检查通知权限：

- 未授权时可以请求权限。
- 用户拒绝授权时，不再阻塞对话执行。
- 权限状态不应影响 `run_snapshot` 消费和前端状态管理。

### 4.5 后台审批

当 Desktop 后台会话进入 `waiting_approval` 状态时：

- 应发送系统通知提醒用户有工具调用等待审批。
- macOS 首期应提供“同意 / 拒绝”快捷动作，并将选择回传到对应会话的审批通道。
- 当前正在查看的会话仍优先使用页面内审批按钮，避免重复干扰。
- 非 macOS 平台首期允许降级为普通通知，提示用户回到应用处理。

---

## 5. 架构方案

### 5.1 首期方案：前端检测 + Tauri 通知插件

首期复用现有前端 `sessionRunStatuses` 差异检测逻辑：

1. `useStore.updateFromSnapshot()` 维护后台会话运行状态。
2. 当旧状态中存在某个后台会话，而新快照中不再存在该会话时，判定该后台会话完成。
3. 前端调用 Tauri notification 插件发送系统通知。
4. 若插件不可用或权限不足，记录 `console.warn`，不影响状态更新。

该方案改动小，符合当前 Desktop 架构：前端已经掌握“当前查看会话”和“后台会话刚完成”的差异。

### 5.2 Rust 侧统一通知门面

通知主链路收敛到 `src-tauri` command。前端负责识别后台完成状态，Rust 侧负责权限请求、系统通知发送和平台差异处理：

- `request_notification_permission()`
- `send_desktop_notification(title, body, session_id)`
- macOS 后台审批通知由 Rust 侧监听 `ApprovalNeeded` 后发送，并在用户选择“同意 / 拒绝”后调用对应 session 的审批响应。

Rust 侧封装 Tauri notification 插件；macOS 交互通知使用平台专用路径补齐点击聚焦和审批动作。

---

## 6. 权限与平台

- Tauri 配置必须启用 notification 插件所需权限。
- macOS / Windows / Linux 行为以 Tauri notification 插件能力为准；macOS 交互通知允许使用平台专用能力补齐点击聚焦与审批动作。
- 通知正文默认只包含会话标题和完成状态，不包含回复正文、工具输出或文件路径。
- 权限请求失败、用户拒绝或系统通知不可用时，应降级为无通知。
- 通知显示时长由操作系统通知样式控制；macOS 用户若需要更长展示，应在系统设置中将通知样式调整为“提醒”。

---

## 7. 验收标准

- Desktop 启动后可请求系统通知权限。
- 非当前会话在后台完成时，系统通知弹出。
- 当前会话完成时不弹系统通知。
- macOS 下点击完成通知或“显示”动作时，桌面端主窗口应被唤起并聚焦。
- macOS 下后台审批通知支持同意 / 拒绝快捷动作，并能把结果回传给对应会话。
- 通知权限被拒绝时，对话执行、会话列表刷新和快照更新不受影响。
- 前端构建通过。
- 若引入 Tauri 插件配置变更，Rust 检查通过。

---

## 8. 实施步骤

1. 在前端封装 Desktop 通知工具，优先使用 Tauri notification 插件。
2. 应用启动时检查或请求通知权限。
3. 替换 `useStore.updateFromSnapshot()` 中的浏览器 `Notification` 调用。
4. 补齐 Tauri 插件依赖、初始化和权限配置。
5. 运行 `yarn build` 和 `cargo check --workspace` 验证。
