Skip to content

UX:简化活跃 Agent 的消息投递,并补齐会话与失败尝试的生命周期 #1306

Description

@mindfn

UX:简化活跃 Agent 的消息投递,并补齐会话与失败尝试的生命周期

目标

F264 已经提供了重要的底层能力:在满足精确运行轮次条件时,新的用户/A2A 消息可以追加到某个正在回复的 Agent,而不必取消该轮并重新启动。现在的问题不在于这项能力本身无效,而在于它的交互入口、显示方式和相邻生命周期状态不够清楚。

本 issue 要把用户心智模型收敛为一句话:消息要么排队,要么追加到正在回复的 Agent;两者都不应意外中断当前工作。

拆分与交付顺序

本 issue 作为跨边界的总控与共享验收标准;实现拆成三个可独立测试、可独立合入的子 issue:

三个子 issue 共享本 issue 的“不得新建第二套队列/回执/session store、不得篡改 CLI stdout”约束;除这些约束外不互相阻塞。

六项问题的性质与范围

# 用户看到的问题 性质 这次要解决什么
1 发送时出现“仅这一次 / 本 Thread / 全局默认”、carrier/provider 等多层概念,难以判断该选什么 功能调整 将日常发送收敛为全局默认的“排队 / 追加到当前回复”,把高级设置移出发送主路径
2 对话页的“停止”“强制重置”“Steer”含义混杂 功能调整 + 实现一致性 用户只看到一个会停止整场对话运行的“停止”;Steer 明确为“强制停止并发送当前消息”
3 追加消息和 CLI 输出之间没有可理解的视觉关联 能力增强 为追加建立结构化的“源消息 → 目标回复”关系,并以 inline comment 形式投影
4 @ 某成员后若拉起失败或被取消,看不到该成员头像、失败气泡或原因 实现缺口 兑现已有 per-target receipt 的可见投影:每个已接受目标都必须有可追踪的 attempt
5 修改 MCP/成员配置后,用户无法主动封存当前 session,迫使下一次使用新会话 能力增强(复用已有状态机) 在 Session Chain 暴露安全的手动封存入口,不再新增第二套 session 存储
6 失败目标没有紧邻的重试入口,用户只能重新输入内容 能力增强 对可重试失败提供保留审计轨迹、可防重的 重试 动作

第 4 项不是“再造回执系统”:F264 已有 durable receipt/custody;缺的是让用户能看见每个目标的实际结果。第 3、5、6 项是以现有可靠状态为真相源的产品能力补齐,而不是用 UI 临时推断状态。

必须保留的现有边界

  • POST /api/threads/:threadId/cancel/:catId 是单个活跃 Agent 的底层取消能力;它不是对话页主“停止”按钮的产品语义。
  • POST /api/threads/:threadId/force-reset 取消全部活跃 controller、清理处理槽位、把持久化的 running 记录标为 cancelled。对话页的“停止”必须执行这一整场停止语义;若仍有运行没有被停止,就是实现 bug。用户不应同时面对两个名称不同但实际做同一件事的“停止/强制重置”动作。
  • F264 的 continue_current 只允许精确命中 exact_active_turn 的 carrier。无法精确投递时必须 fail closed:同一条消息落回 next_work 队列,并持久化原因;绝不能取消活跃轮次来“凑出”追加效果。
  • F264 receipt/custody 是投递、唤醒、读取、失败、处理状态的唯一真相源。不得从消息气泡文本、时间戳或 CLI stdout 反推状态。
  • Session 已有 active → sealing → sealed 生命周期,使用 SessionSealer.requestSeal()finalize();本项只补安全入口和 UI 状态,不再引入 session store。

预期交互

1. 把日常发送变成两种易懂的策略

在全局设置中提供一个明确、长期生效的“消息投递策略”:

  • 排队(默认):消息成为该 Agent 的下一件工作,不打断正在进行的回复。
  • 追加到当前回复:若目标 Agent 正处于可精确追加的活跃轮次,则在安全边界把消息交给该轮;否则保留为排队消息,并说明原因。

发送框不再展示“仅这一次 / 本 Thread / 全局默认”、carrier/provider 能力或“接着当前工作 / 下一件工作”等内部概念。用户只选择意图,系统负责显示最终结果。

当全局默认是“排队”且目标 Agent 正在可追加地回复时,发送框或该排队项旁提供一个紧凑的一次性动作 “追加到当前回复”:只改变这条消息,不改变全局默认。多目标投递时,每个目标独立记录结果;可追加的目标追加,不能精确追加的目标继续排队并显示各自原因。

不要把“追加”统称为 Steer。现有即时 Steer 的语义是取消目标后重新运行,属于另一种破坏性操作。

2. 对话页只有“停止”,Steer 是强制停止后发送

用户不需要理解底层的单 Agent cancel 或“强制重置”术语。对话页使用以下两种清晰的产品动作:

  • 对话页主操作:“停止”——停止当前对话内全部运行中的 Agent 与运行态,保留聊天记录/历史消息。它就是现有 force-reset 的用户可理解入口;不再额外展示一个同义的“强制重置对话”。
  • 当前输入消息的强制发送操作:“Steer(强制停止并发送此消息)”——停止该消息所指向目标的当前回复,再立即发送当前消息。确认文案必须明确“会停止当前回复后发送此消息”。

“停止”一旦执行仍有本应停止的运行继续存在,属于 bug,而不是一个需要用户另找“强制重置”的备选路径。“追加到当前回复”绝不能隐式走 Steer 或“停止”。

3. 追加消息以 inline comment 显示,而不是伪装成 CLI 输出

每次追加须持久化一条结构化关联:源消息 ID、发送者、目标 cat、目标 invocation/assistant bubble、接受时间、carrier 结果与 attempt 结果。

渲染分为两层:

  1. 在该 Agent 正在显示的输出位置,以简短引用投影,例如 ↳ lang:请优先检查重试路径…,让人一眼看出这是中途加入的指令。
  2. 在同一个 Agent 消息气泡底部提供可展开的 “已追加到本次回复” 区域,显示完整引用、发送者头像、时间、目标状态,并链接回原始消息/receipt。

这类似 GitHub 的 inline comment,但有两条硬约束:原始消息仍是正常、可持久化的时间线记录;引用层只是结构化投影。不得把用户/A2A 内容拼接进原始 CLI stdout,也不得通过解析 stdout 恢复关系。刷新、重连、历史 hydration 后必须稳定地恢复一次,不重复、不篡改 Agent 原话。

4. 每个目标都可见,失败也不消失

消息被接受或 @ 解析成功时,立即为每个目标创建持久化 target attempt,并显示其头像和状态:startingqueuedappendedfailedcancelledhandled

若成员拉起失败,或在产生正文前被取消:

  • 原始消息下必须保留该目标的 receipt;
  • 时间线/目标位置显示系统拥有的失败气泡或卡片,带头像、明确失败原因与 source-message/attempt 关联;
  • 不伪造一条 Agent 回复,也不让该目标从界面消失。

对可重试的终态失败,在该失败 attempt 的气泡标题/现有 # 动作区域提供 “重试”。重试复用不可变的原消息和目标选择,创建新的 attempt ID,保留旧失败记录;同一 source + target 的双击/并发请求必须幂等,不能生成重复的同时运行。

5. Session Chain 提供安全的手动封存

在右侧 Session Chain 的现有 bind 控件附近提供 “封存当前会话”

  • 确认文案说明:封存保留 transcript/history;下次激活该成员时会使用一个新的 session。
  • 仅当该成员没有活跃 invocation 时可执行。
  • 成员运行中时按钮禁用,并显示“请先停止该 Agent,再封存会话”;不得封存一个仍在运行的 provider turn,也不得产生并行 session。
  • 调用既有 manual reason 与 requestSeal → finalize 生命周期;成功后卡片立刻切到 sealed 状态,下一次 activation 分配/使用新的 active session。

实施边界

  1. 前端功能调整:收敛发送策略入口;把对话页“停止”映射为现有 force-reset 的整场停止语义;将 Steer 明示为“强制停止并发送此消息”。不改变 cancel、即时 Steerforce-reset 的既有后端作用范围。
  2. 投递/气泡实现补齐:以 F264 receipt/custody 建立 target attempt 的可见投影;追加关联存为结构化数据,并由 bubble pipeline 渲染引用层。
  3. 能力增强:补充 retry 以及 Session Chain 的安全 seal 入口,分别复用 attempt 真相源和现有 SessionSealer
  4. 禁止项:不新增第二条队列、第二个 receipt store、第二个 session store 或 UI-only 的状态真相源;不修改/解析 CLI 原始输出来承载追加文本。

验收标准

  1. 新用户在活跃 Agent 存在时,只能看到“排队”或“追加到当前回复”的业务语言,不会在发送主路径看到偏好作用域、provider/carrier 细节或 Steer
  2. 全局默认“排队”与一次性追加均可用;全局默认“追加”也可用。无精确 carrier 时,消息确定性地排队并显示原因,绝不取消活跃轮次。
  3. 多目标消息逐目标显示追加/排队/失败结果;用户与 A2A 来源都遵循同一模型。
  4. 对话页只展示一个“停止”,它能停止全部当前运行并保留消息/历史数据;“停止”与旧 force-reset 的实际影响范围一致。Steer 明示“强制停止并发送此消息”,并在确认后停止目标当前回复再发送当前消息。
  5. 追加消息以短引用 + 可展开完整详情出现,刷新、重连和历史 hydration 后关系不丢失、不重复,且 CLI stdout 完全未被改写。
  6. 每个已接受 @ 目标都立即可见;拉起失败和正文前取消均有头像、类型化原因、持久化 attempt/receipt 关联。
  7. 仅可重试失败显示“重试”;重试保留旧 attempt、创建一个新 attempt,并通过双击/并发测试证明不会重复执行。
  8. 空闲 active session 可手动封存;运行中不可封存;封存后历史保留且下次激活使用新的 session。
  9. 覆盖精确/不支持 carrier 回退、跨多目标部分失败、A2A 追加、对话页“停止”的全量停止语义、Steer 的停止后发送语义、刷新/重连、retry race、seal race 和 next-session 行为的自动化测试。

非目标

  • 不改变 F264 的精确 active-turn 安全边界。
  • 不把即时 Steer 删除或改造成“追加”;它仍是经确认的取消后立即执行路径。
  • 不清理消息、历史记录或已失败 attempt。

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions