优化: workflow记录补project_id+前端审批/交互打磨

This commit is contained in:
lxy
2026-08-12 00:40:40 +08:00
parent d31a27512c
commit 7a1e80eac2
9 changed files with 83 additions and 32 deletions
+11 -1
View File
@@ -316,13 +316,23 @@ export function setConvStreaming(convId: string | null | undefined, value: boole
/**
* 写某 conv 的 currentText(空串时仅清字段,streaming 仍 true 则保留项)。
* FIX-17 P2-3:收敛对称——当 streaming=false 且 currentText 被清空时,删 Map 项。
*
* @param convId 目标会话 id
* @param value currentText 目标值(空串=清流式累积,但会话可能仍在 streaming)
*/
export function setConvCurrentText(convId: string | null | undefined, value: string): void {
if (!convId) return
ensureConvStreamState(convId).currentText = value
if (value) {
ensureConvStreamState(convId).currentText = value
} else {
// 空串收敛:若 streaming=false 则删项,否则仅清字段(保留项)
const cur = convStreamStates.get(convId)
if (cur) {
cur.currentText = ''
if (!cur.streaming) convStreamStates.delete(convId)
}
}
}
/** 清某 conv 的 stream state(会话删除/收尾彻底清,删整个 Map 项)。
+3 -1
View File
@@ -25,7 +25,9 @@ import { state } from '@/stores/ai'
import { resolveAiLang } from './aiShared'
import { setContextCallbacks } from './useAiEvents'
/// 压缩中 loading(防重复点击;AiCompressing→true / AiManualCompressed|AiAutoCompressed|AiError→false)
// FIX-17 P2-7:模块级单例 ref,多窗口共享(分离窗口与主窗口共用同一 isCompressing)。
// 已知限制:分离窗口压缩时,主窗口的压缩按钮也显示 loading(反之亦然);非功能 bug,
// 但若需 per-window 独立压缩态需改为 inject/provide 或独立 store 实例,本版暂不处理。
const isCompressing = ref(false)
/**
+4
View File
@@ -516,6 +516,10 @@ async function deleteConversation(id: string) {
loadMoreCursor.earliestSeq = null
loadMoreCursor.convId = null
loadMoreCursor.loading = false
// FIX-17 P1-11:回落相邻会话,避免空白无引导
if (neighborId) {
await switchConversation(neighborId)
}
}
scheduleConversationsRefresh()
// 删的是活跃会话:回落相邻会话作为新活跃视图
+2 -1
View File
@@ -326,7 +326,8 @@ async function syncToMain() {
if (screenWidth > 0) {
targetX = screenWidth - AI_DOCK_WIDTH - AI_DOCK_GAP
}
targetHeight = size.height > targetHeight ? targetHeight : size.height
// FIX-17 P2-5: 最大化时 targetHeight = size.height, height > height 恒 false,
// 原三元是无操作死代码,已移除。
} else if (screenWidth > 0 && targetX + AI_DOCK_WIDTH > screenWidth) {
// 非最大化但主+AI 超屏幕:AI 窗口本身让位(缩窄或往左偏),不动主窗口。
// 优先策略:贴屏幕右侧边,AI 窗口宽度可能被主窗口右边缘掩盖一部分,但不强迫主窗口缩小。
+1 -1
View File
@@ -21,7 +21,7 @@ import { useAppSettingsStore } from '../../stores/appSettings'
/// 流式渲染返回的单块渲染结果(html + 模板 v-for key)
export interface StreamBlock {
html: string
/** 已完成块=block文本hash(稳定), 末块='tail-'+递增序号(每次变) */
/** 已完成块=block文本hash(稳定), 末块='tail-'+递增序号(每帧内唯一) */
key: string
}
+46 -22
View File
@@ -55,7 +55,12 @@ export function dispatchApprovalIPC(
return aiApi.approve(tc.id, approved)
}
export function useToolApproval(tc: Ref<AiToolCallInfo>, emit: any) {
/** useToolApproval emit 签名 — FIX-17 P2-6:补全类型替代 any */
interface ToolApprovalEmit {
(event: 'approve', payload: { id: string; approved: boolean; decision?: 'once' | 'session' | 'always' | 'deny' }): void
}
export function useToolApproval(tc: Ref<AiToolCallInfo>, emit: ToolApprovalEmit) {
const { t } = useI18n()
const { confirmState, confirmDialog, answerConfirm } = useConfirm()
@@ -123,22 +128,32 @@ export function useToolApproval(tc: Ref<AiToolCallInfo>, emit: any) {
* 批准按钮处理(risk 类专用)。
* AE-2025-05:High 风险工具 → 先二次确认(useConfirm 弹层)。
* loading + 超时兜底复用 B-260616-08 机制。
* FIX-17 P1-10:approving 提前到 confirmDialog 前,防二次确认期间双击重入。
*/
async function onApprove() {
if (approving.value) return // 防重入
// AE-2025-05:High 风险 → 先二次确认
if (HIGH_RISK_TOOLS.has(tc.value.name)) {
const ok = await confirmDialog(highRiskConfirmMsg(tc.value.name))
if (!ok) return
}
// 先锁防重入(含二次确认 await 期间)
approving.value = true
approvingTimer = setTimeout(() => {
approving.value = false
approvingTimer = null
console.warn('[AI] 审批 loading 超时,后端可能未回执,已复位允许重试:', tc.value.id)
showApproveTimeoutToast()
}, APPROVE_LOADING_TIMEOUT_MS)
emit('approve', { id: tc.value.id, approved: true })
try {
// AE-2025-05:High 风险 → 先二次确认
if (HIGH_RISK_TOOLS.has(tc.value.name)) {
const ok = await confirmDialog(highRiskConfirmMsg(tc.value.name))
if (!ok) {
approving.value = false // 取消二次确认 → 解锁
return
}
}
approvingTimer = setTimeout(() => {
approving.value = false
approvingTimer = null
console.warn('[AI] 审批 loading 超时,后端可能未回执,已复位允许重试:', tc.value.id)
showApproveTimeoutToast()
}, APPROVE_LOADING_TIMEOUT_MS)
emit('approve', { id: tc.value.id, approved: true })
} catch (e) {
approving.value = false // 异常解锁
throw e
}
}
/**
@@ -213,19 +228,28 @@ export function useToolApproval(tc: Ref<AiToolCallInfo>, emit: any) {
* M5:拒绝前二次确认(useConfirm 弹层)。拒绝不可逆——工具返 Err、loop 续跑不再重试该工具,
* 与放行(once/session/always 均可后续再授权)不对称,误点代价高;补确认与 approve(高风险)对称。
* risk 类拒绝(onReject)不补确认:risk reject 是常规审批否决,语义明确且可重新触发;仅 path 类加。
* FIX-17 P1-10:approving 提前到 confirmDialog 前,防二次确认期间双击重入。
*/
async function onDeny() {
if (approving.value) return
const ok = await confirmDialog(t('aiChat.dirAuthDenyConfirm'))
if (!ok) return
approving.value = true
approvingTimer = setTimeout(() => {
approving.value = false
approvingTimer = null
console.warn('[AI] 路径授权 loading 超时,后端可能未回执,已复位允许重试:', tc.value.id)
showApproveTimeoutToast()
}, APPROVE_LOADING_TIMEOUT_MS)
emit('approve', { id: tc.value.id, approved: false, decision: 'deny' })
try {
const ok = await confirmDialog(t('aiChat.dirAuthDenyConfirm'))
if (!ok) {
approving.value = false // 取消二次确认 → 解锁
return
}
approvingTimer = setTimeout(() => {
approving.value = false
approvingTimer = null
console.warn('[AI] 路径授权 loading 超时,后端可能未回执,已复位允许重试:', tc.value.id)
showApproveTimeoutToast()
}, APPROVE_LOADING_TIMEOUT_MS)
emit('approve', { id: tc.value.id, approved: false, decision: 'deny' })
} catch (e) {
approving.value = false // 异常解锁
throw e
}
}
// status 离开 pending_approval → 复位 approving(后端回执/转态时)。