优化: 子代理体验与纪律注入
- 工具纪律段抽共享同源注入 - 子代理轨迹浮层与完成卡身份 - 分离窗会话切换修复 - 960s 统一出口报告不再蒸发
This commit is contained in:
@@ -624,6 +624,14 @@ export interface AgentFinishedCard {
|
||||
status: AgentFinishedStatus
|
||||
rounds: number
|
||||
tokens: number
|
||||
/** 被拒工具清单("工具名×次数",如 "write_file×3",后端 format_rejected_tools 产出)。
|
||||
* 字段名保持事件 wire 格式 snake_case(summary 是后端拼好的全文,结构化清单只此一份,
|
||||
* 卡面 160 字截断裁掉 summary 尾部时渲染端仍可扫描被拒事实);缺省=零拒绝。 */
|
||||
rejected_tools?: string[]
|
||||
/** 终止原因结构化字段(后端 AiAgentFinished.stop_kind,cancelled/wall_clock/max_rounds/
|
||||
* token_budget/empty_convergence/error/converged)。缺省=旧后端未发,渲染端回退
|
||||
* summary 启发式(向后兼容);实时事件到达时以此覆盖启发式判断。 */
|
||||
stop_kind?: string
|
||||
finishedAt: number
|
||||
}
|
||||
|
||||
@@ -661,6 +669,52 @@ export function clearConvAgentFinished(convId: string | null | undefined): void
|
||||
convAgentFinished.delete(convId)
|
||||
}
|
||||
|
||||
/** ai_sub_agent_list 的单条轨迹元数据(wire shape 归口 api 层返回类型,此处只投影用) */
|
||||
type SubAgentRef = Awaited<ReturnType<typeof aiApi.listSubAgents>>[number]
|
||||
|
||||
/**
|
||||
* P2-2 完成卡回补:重启/分离窗回收后内存 Map 清零,子代理轨迹成无 UI 入口孤岛。
|
||||
* 按父 conv 经 ai_sub_agent_list 反查子代理轨迹,重建已完成卡(轨迹存在即视为 completed;
|
||||
* rejected_tools/rounds/tokens 后端该 IPC 不回,缺省=零拒绝/0,卡面隐藏 0 值 meta)。
|
||||
*
|
||||
* 幂等/去重/降级三保证:
|
||||
* - 幂等:挂载恢复与每次 switchConversation 都会触发,agentId 键控 insert-only,重复调用收敛;
|
||||
* - 去重:同 agentId 已有卡(实时事件先到/上轮回补过)跳过,不覆盖已有数据;
|
||||
* 实时事件走 addConvAgentFinished 的 splice 覆盖语义,晚到仍会覆盖回补卡(实时数据优先)。
|
||||
* - 降级:IPC 失败仅 console.warn 返回,调用方 fire-and-forget,不阻塞会话加载。
|
||||
*/
|
||||
export async function backfillConvAgentFinished(convId: string | null | undefined): Promise<void> {
|
||||
if (!convId) return
|
||||
let refs: SubAgentRef[]
|
||||
try {
|
||||
refs = await aiApi.listSubAgents(convId)
|
||||
} catch (e) {
|
||||
console.warn('[AI] 完成卡回补失败(静默降级):', convId, e)
|
||||
return
|
||||
}
|
||||
if (refs.length === 0) return
|
||||
let arr = convAgentFinished.get(convId)
|
||||
if (!arr) {
|
||||
arr = []
|
||||
convAgentFinished.set(convId, arr)
|
||||
}
|
||||
for (const ref of refs) {
|
||||
const agentId = ref.conversation_id
|
||||
// agentId 即子会话 conv id({parent}::agent-{n} 全形,与实时事件 agent_id 同源),
|
||||
// 与既有卡天然同键;无 id 的脏行跳过防空键卡
|
||||
if (!agentId || arr.some(c => c.agentId === agentId)) continue
|
||||
arr.push({
|
||||
agentId,
|
||||
// title 懒创建常为 None,回退显 agent-{n} 尾段(对齐轨迹浮层身份行切分逻辑)
|
||||
summary: (ref.title || '').trim() || agentId.split('::').pop() || agentId,
|
||||
status: 'completed',
|
||||
rounds: 0,
|
||||
tokens: 0,
|
||||
finishedAt: Number(ref.created_at) || Date.now(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ── UX-P2-A3 审批超时错误气泡 per-conv 守卫 ──
|
||||
//
|
||||
// 背景:startApprovalTimer 到点把「审批超时已自动拒绝」错误气泡直接推进 getMessages()
|
||||
|
||||
Reference in New Issue
Block a user