新增: 无人值守P0任务边界全链
- 任务级授权边界判定与审批旁路单点插桩 - boundary_json 随会话落库(V45 可逆迁移) - 六路非正常终局统一安全停靠与托盘通知 - 每五轮进度事件与会话头部进度条 - 前端边界配置卡侧栏徽标及 UX 校验补齐
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
//! 生命周期事件域 — handleLifecycleEvent + cleanupTerminatedConversation
|
||||
//!
|
||||
//! 处理:AiCompleted / AiError / AiHelpRequired + 上下文生命周期事件(AiCompressing/
|
||||
//! 处理:AiCompleted / AiError / AiHelpRequired / AiTaskParked + 上下文生命周期事件(AiCompressing/
|
||||
//! AiManualCompressed/AiAutoCompressed/AiContextCleared)。
|
||||
//! 依赖方向:本模块 → aiShared / useAiPendingState(叶子)/ useAiStream / streamingGuard /
|
||||
//! useAiConversations,不导入任何同级子 composable。
|
||||
@@ -10,7 +10,7 @@ import { useAppSettingsStore } from '@/stores/appSettings'
|
||||
import { state } from '@/stores/ai'
|
||||
import { t } from '@/i18n/i18n-helpers'
|
||||
import { nextMsgId, convStates, clearApprovalTimer, flushCurrentText, clearAllToolSlowTimers, friendlyError, notifyConversationChanged, clearLastDelta, discardConvPendingApprovals, notifyQueueCleared } from './aiShared'
|
||||
import { clearTextIdleTimer, pendingMaxRounds, pendingHelp, pendingDirAuths } from './useAiPendingState'
|
||||
import { clearTextIdleTimer, pendingMaxRounds, pendingHelp, pendingDirAuths, pendingTaskParked, pendingTaskProgress } from './useAiPendingState'
|
||||
import { clearStreamWatchdog, clearResumeAttempts } from './useAiStream'
|
||||
import { setStreaming } from './streamingGuard'
|
||||
import type { AiChatEvent, AiMessage, MessageId } from '@/api/types'
|
||||
@@ -43,14 +43,16 @@ function isShowTokenUsage(): boolean {
|
||||
}
|
||||
|
||||
/**
|
||||
* 公共会话终止收尾(AiCompleted/AiError/AiHelpRequired 共用):清看门狗/计时器/流式态/挂起。
|
||||
* 公共会话终止收尾(AiCompleted/AiError/AiHelpRequired/AiTaskParked 共用):清看门狗/计时器/流式态/挂起。
|
||||
*
|
||||
* 语义差异(调用方自行处理):
|
||||
* - AiCompleted: 调用后追加 incomplete 气泡 / token 用量 / 队列 drain
|
||||
* - AiError: 调用后追加错误气泡 + 清 queue
|
||||
* - AiHelpRequired: 调用后翻 pendingHelp 驱动求助卡
|
||||
* - AiTaskParked: 调用后登记 pendingTaskParked 驱动停靠卡(摘旧卡;不清 pendingHelp,
|
||||
* 熔断并存场景见下方 pendingHelp 分支注释)
|
||||
*/
|
||||
function cleanupTerminatedConversation(convId: string, reason: 'AiCompleted' | 'AiError' | 'AiHelpRequired') {
|
||||
function cleanupTerminatedConversation(convId: string, reason: 'AiCompleted' | 'AiError' | 'AiHelpRequired' | 'AiTaskParked') {
|
||||
clearStreamWatchdog(convId || undefined)
|
||||
// 正常收尾路径清该会话的 delta 去重记录:flushCurrentText 只清活跃会话的,这里按 convId
|
||||
// 显式清一遍兜住 convId 与 active 不一致或事件缺 convId 的时序,防残留记录误丢下轮同内容 delta
|
||||
@@ -83,8 +85,16 @@ function cleanupTerminatedConversation(convId: string, reason: 'AiCompleted' | '
|
||||
pendingMaxRounds.value = null
|
||||
}
|
||||
if (pendingHelp.value && pendingHelp.value.conversationId === convId) {
|
||||
pendingHelp.value = null
|
||||
// 熔断场景后端并存发求助卡+停靠卡(agentic/mod.rs 熔断分支先 emit 求助卡再「补发」
|
||||
// AiTaskParked,注释自称并存意图):停靠收尾不清求助卡,help options 三选项不丢,
|
||||
// 两卡并存供用户任选入口。其余终态(AiCompleted/AiError/AiHelpRequired)照旧清。
|
||||
if (reason !== 'AiTaskParked') pendingHelp.value = null
|
||||
}
|
||||
// 无人值守停靠卡:会话有任何终态(含停靠本身)即摘旧卡;停靠 case 在收尾后重新登记,
|
||||
// 其余终态(AiCompleted/AiError,点继续续跑后的正常收敛/出错)摘卡即消失,语义对齐 pendingHelp
|
||||
pendingTaskParked.delete(convId)
|
||||
// 无人值守进度快照:终态即清(事件即逝态,终止后不留陈旧「进行中」;下次 loop 每 5 轮重建)
|
||||
pendingTaskProgress.delete(convId)
|
||||
// 路径授权挂起仅清本 conv(终止只清本会话弹窗,不连累并发会话;无归属旧项保守保留)
|
||||
pendingDirAuths.value = pendingDirAuths.value.filter(p => !p.conversationId || p.conversationId !== convId)
|
||||
// 审批收尾仅清目标 conv 的待审批项 + 对称清其超时计时器(其他会话的审批卡/计时器不受影响;
|
||||
@@ -104,6 +114,21 @@ function cleanupTerminatedConversation(convId: string, reason: 'AiCompleted' | '
|
||||
localStorage.removeItem('df-ai-text')
|
||||
}
|
||||
|
||||
/**
|
||||
* 登记无人值守停靠卡(幂等,convId 键控覆盖)。lifecycle 主路径与 useAiEvents 后台会话
|
||||
* 分支共用唯一写入口,防止两处字段口径漂移。convId 缺失兜底当前活跃会话(优于丢卡)。
|
||||
*/
|
||||
export function recordTaskParked(convId: string | null | undefined, reason: string, stopKind?: string): void {
|
||||
const id = convId || state.activeConversationId
|
||||
if (!id) return
|
||||
pendingTaskParked.set(id, {
|
||||
reason,
|
||||
stopKind: stopKind || undefined,
|
||||
conversationId: id,
|
||||
parkedAt: Date.now(),
|
||||
})
|
||||
}
|
||||
|
||||
/** lifecycle 域:整轮收尾(AiCompleted)与异常中断(AiError)。返回 true=已处理。 */
|
||||
export function handleLifecycleEvent(event: AiChatEvent): boolean {
|
||||
switch (event.type) {
|
||||
@@ -215,6 +240,18 @@ export function handleLifecycleEvent(event: AiChatEvent): boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
case 'AiTaskParked': {
|
||||
// 无人值守安全停靠(设计 §四):五路非正常终局统一出口,后端已保存现场并复位
|
||||
// generating(guard 复位非死等)。对齐 AiHelpRequired 家族:终止态收尾不建错误气泡,
|
||||
// 改登记 pendingTaskParked 驱动停靠卡(reason + stop_kind 标签 + 继续/停止归档)。
|
||||
// 队列不动:停靠等用户拍板,排队消息不自动续发不静默清(继续/停止由用户决定)。
|
||||
cleanupTerminatedConversation(event.conversation_id || '', 'AiTaskParked')
|
||||
recordTaskParked(event.conversation_id, event.reason, event.stop_kind ?? undefined)
|
||||
// FE1:停靠刷新侧栏(更新活跃时间排序),由 listener 防抖调度统一收口。
|
||||
notifyConversationChanged()
|
||||
return true
|
||||
}
|
||||
|
||||
case 'AiCompressing':
|
||||
_contextCallbacks.onCompressing?.()
|
||||
return true
|
||||
|
||||
Reference in New Issue
Block a user