docs/plan/2026-08-17-side-task-promote-to-main-session-design.md
Rama main · réplica de lectura del repositorio original.
# 边路任务升级为主会话技术方案
> 版本:v1.0
> 状态:需求方案草案
> 适用范围:桌面端(Electron)+ 后端会话体系
## 1. 需求背景
当前系统中,边路任务(`SIDE_TASK`)是挂在主会话下的子会话:
- 后端通过 `session_type = 'SIDE_TASK'` 与 `parent_session_id` 标识边路任务
- 主会话列表会主动排除 `SUBAGENT` 和 `SIDE_TASK`
- 前端将边路任务作为中心区域 Tab 展示,并按父会话缓存与恢复
这套结构适合“临时并行任务”,但不适合“把边路任务升级成一个新的主会话”这个需求。
本需求的核心不是原地修改边路任务记录,而是:
1. 以某个边路任务为源
2. 新建一个独立的主会话(`session_type = 'NORMAL'`)
3. 将边路任务的已有内容复制到新主会话
4. 让用户后续只在新主会话继续工作
## 2. 需求描述
### 2.1 目标
用户在边路任务中完成一段临时工作后,可以把它“升级”为新的主会话,作为后续正式工作的起点。
### 2.2 必须实现的内容
- 从边路任务创建一个新的主会话
- 新主会话继承边路任务的运行配置
- 新主会话保留边路任务已产生的历史内容
- 新主会话进入正常的主会话列表和主会话工作流
- 升级动作在边路任务面板中可触发
### 2.3 明确不做的内容
- 不做原地转换:不把 `SIDE_TASK` 记录直接改成 `NORMAL`
- 不做强关联跳转:不要求原边路任务和新主会话之间保存可跳转关系
- 不做“升级后双向同步”:原边路任务和新主会话不会继续共享后续消息
- 升级成功后按现有逻辑删除语义移除原边路任务入口
- 不做后台自动升级:必须由用户主动触发
- 不做管理员后台改造:仅桌面端相关
## 3. 现状分析
### 3.1 当前边路任务的实现事实
从代码现状看,边路任务已经是独立 session,但它并不是主会话:
- `backend-ts/src/session/ws/streaming-ws-handler.ts:526-600`
- `create_side_session` 会创建 `sessionType = 'SIDE_TASK'` 的子会话
- 它会继承父会话的 `agentId / executionMode / workspace / permissionLevel / modelId` 等配置
- `backend-ts/src/session/session.service.ts:289-316`
- 边路任务查询按 `parent_session_id` 过滤
- `backend-ts/src/session/session.service.ts:272-275`
- 主会话列表明确排除 `SUBAGENT` 和 `SIDE_TASK`
- `desktop/src/composables/useCenterTabs.ts:18-37`
- 边路任务以 Tab 形式存在,并依赖父会话的上下文
- `desktop/src/views/task/TaskView.vue:260-430`
- 右侧状态、todo、上下文、标题都按子会话缓存和展示
### 3.2 当前体系与“升级为主会话”的冲突点
存在以下逻辑冲突:
1. **会话类型冲突**
- 边路任务当前是 `SIDE_TASK`
- 主会话必须是 `NORMAL`
- 这不是一个简单的字段切换,因为现有 UI 和查询条件都基于类型分流
2. **父子关系冲突**
- 边路任务必须挂在父会话下
- 主会话按定义不应该再依赖原父会话的边路任务树
- 如果还保留原父关系,就会继续落入边路任务体系
3. **列表归属冲突**
- `SIDE_TASK` 不进入主会话列表
- 新主会话必须进入主会话列表
- 因此必须新建记录,而不是改原记录
4. **消息归属冲突**
- 边路任务的消息当前都写在自己的 session 下
- 升级后如果还想保持完整历史,必须把这些消息复制到新 session
- 不能只改标题或状态
5. **运行状态冲突**
- 运行中的边路任务可能还在流式输出、审批、工具执行
- 这种状态下复制出来的新主会话没有明确边界,容易产生“双会话同时继续”的歧义
- 所以本方案不允许运行中升级
结论:**存在逻辑冲突点,而且冲突点不止一个。** 这个需求必须按“复制创建新主会话”的方式实现,不能做原地升级。
## 4. 技术选型
### 4.1 方案选择
采用“**新建 NORMAL 主会话 + 复制边路任务内容**”方案。
### 4.2 选择原因
- 与现有 `sessionType` 设计一致
- 不破坏主会话列表、边路任务列表、Tab 恢复逻辑
- 不需要修改现有 `SIDE_TASK` 查询语义
- 能复用现有会话创建、消息保存、WS 流式执行能力
### 4.3 不采用的方案
- **原地升级 `SIDE_TASK -> NORMAL`**
- 会破坏父子会话语义
- 会让现有边路任务列表、Tab 状态和会话树出现歧义
- **只复制摘要**
- 无法满足“保留边路任务历史内容”的要求
- **保留强关联并双向同步**
- 会让后续消息分叉,维护成本高,且不符合当前需求
## 5. 实现方案
### 5.1 升级流程
1. 用户在边路任务面板点击“升级为主会话”
2. 前端先校验当前边路任务状态是否允许升级
3. 调用后端升级接口,传入边路任务 sessionId
4. 后端读取源边路任务及其消息
5. 后端创建一个新的 `NORMAL` 主会话
6. 将源边路任务的历史消息复制到新主会话
7. 返回新主会话 id
8. 前端跳转到新主会话
### 5.2 后端实现要点
#### 5.2.1 新增升级接口
在 session 领域新增一个专用接口,用于从边路任务创建主会话。
```http
POST /api/v1/sessions/{sideSessionId}/promote-side-task
```
接口职责:
- 校验源 session 存在且属于当前用户
- 校验源 session 类型为 `SIDE_TASK`
- 校验源 session 不处于 `RUNNING` / `WAITING_APPROVAL`
- 校验源边路任务不存在子会话
- 在事务中锁定源边路任务、复查类型/状态/子会话、创建新的主会话、复制历史、删除源边路任务
- 返回新会话 VO
#### 5.2.2 创建新会话
新主会话应复用源边路任务的配置:
- `agentId`
- `executionMode`
- `workspace`
- `permissionLevel`
- `modelId`
- `isGit`
- `platform`
- `shellPath`
- `osVersion`
- `projectKey`
#### 5.2.3 消息复制
复制范围:
- 源边路任务中的用户消息
- 助手消息
- 工具调用消息
- 消息关联的文件变更记录
- 任务 todo 列表
- 其他可见对话内容
不复制内容:
- 不复制 session id
- 不复制 parent session 关系
- 不复制只属于旧会话的运行态字段
- 不复制 `contextTokens`、`lastPromptTokens`、`contextAnchorMsgId` 和压缩记录;新主会话后续基于完整复制的消息历史重新计算上下文
#### 5.2.4 运行态与子会话限制
不允许以下状态升级:
- `RUNNING`
- `RESUMING`
- `WAITING_APPROVAL`
- `CANCELLING`
原因:这些状态下消息流和工具执行未收敛,复制会产生不一致。
存在子会话的边路任务不允许升级。子会话包括由边路任务触发的子代理或其他挂载在该边路任务下的会话;本期不做子会话迁移或重挂,避免升级后产生孤儿节点或不完整会话树。
### 5.3 前端实现要点
#### 5.3.1 入口
入口放在边路任务面板中,和当前边路任务条目绑定。
#### 5.3.2 交互
- 点击升级按钮后弹出确认
- 成功后自动打开新主会话
- 原边路任务不做跳转绑定
#### 5.3.3 状态展示
边路任务条目不需要新增复杂状态,仅在升级完成前后做按钮可用性控制。
## 6. 数据影响面
### 6.1 现有表复用
继续复用现有 `session`、`message` 表,不新增独立“升级任务”表。
### 6.2 新记录含义
升级后会产生一条新的 `NORMAL` 会话记录。
源边路任务记录在升级成功后走现有逻辑删除语义,界面和列表不再显示该边路任务。
### 6.3 不新增的内容
- 不新增专用升级表
- 不新增会话树联动关系
- 不新增原任务与新主会话的强关联字段
## 7. 落地步骤
### 7.1 后端
1. 新增“边路任务升级为主会话”服务方法
2. 新增对应路由接口
3. 补充会话类型、状态、消息复制相关校验
4. 复用现有会话创建与消息写入逻辑
5. 补充单测覆盖:
- 非边路任务拒绝升级
- 运行中拒绝升级
- 升级后新主会话消息完整
### 7.2 前端
1. 在边路任务面板加入升级操作入口
2. 发起升级请求并处理成功跳转
3. 在升级中禁用按钮,避免重复提交
4. 升级失败时提示错误原因
### 7.3 验证
1. 边路任务升级后是否进入主会话列表
2. 新主会话是否可继续正常对话
3. 历史消息是否完整保留
4. 原边路任务是否保持原行为不受影响
## 8. 落地清单
### 8.1 必做
- [ ] 新增升级接口
- [ ] 新增升级服务逻辑
- [ ] 复制边路任务消息到新主会话
- [ ] 校验边路任务类型与状态
- [ ] 前端增加升级入口
- [ ] 前端升级后跳转到新主会话
- [ ] 后端单测覆盖升级逻辑
- [ ] 若行为对用户可见,补充 `CHANGELOG.md`
### 8.2 不做
- [ ] 不做原地会话类型转换
- [ ] 不做双向同步
- [ ] 不做运行中升级
- [ ] 不做自动升级
- [ ] 不做强关联跳转链路
- [ ] 不做管理后台改造
## 9. 风险与约束
### 9.1 主要风险
- 复制消息时如果遗漏工具调用或多模态内容,会导致新主会话历史不完整
- 如果允许运行中升级,会出现消息截断和状态不一致
- 如果尝试沿用原边路任务的 parent 关系,会让新主会话和边路任务体系互相污染
### 9.2 约束
- 升级动作必须在会话收敛后执行
- 新主会话必须独立存在
- 历史复制必须完整且一次性完成
## 10. 结论
这个需求**不能直接复用现有边路任务原地升级逻辑**,因为当前系统里边路任务和主会话在类型、归属、查询、展示和执行上都已分层。
正确做法是:
> **从边路任务复制出一个新的 NORMAL 主会话,保留完整历史,之后只在新主会话继续。**
这条路线与现有架构兼容,冲突最少,落地最稳。