mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-16 04:18:17 +08:00
Keep rejected managed approval pending in UI
This commit is contained in:
parent
90c9256dc6
commit
3e9377458d
@ -824,13 +824,18 @@ export function useChat(options: UseChatOptions): UseChatReturn {
|
||||
currentAssistantId.value = null
|
||||
}
|
||||
|
||||
streamPhase.value = data.status === 'awaiting_approval' ? 'awaiting_approval'
|
||||
streamPhase.value = errorFired ? 'idle'
|
||||
: data.status === 'awaiting_approval' ? 'awaiting_approval'
|
||||
: data.status === 'stopped' ? 'stopped' : 'completed'
|
||||
if (data.status !== 'awaiting_approval') {
|
||||
phaseInfo.value = null
|
||||
compactStatus.value = null
|
||||
lifecycleStage.value = null
|
||||
expirePendingApprovals(data.status === 'stopped' ? 'stopped' : 'completed')
|
||||
// An error may be followed by a protocol-level done event. Its status
|
||||
// does not resolve an approval or turn the failed request into success.
|
||||
if (!errorFired) {
|
||||
expirePendingApprovals(data.status === 'stopped' ? 'stopped' : 'completed')
|
||||
}
|
||||
}
|
||||
|
||||
// Safety cleanup for queue state (no-op if queued_input_started already handled it)
|
||||
@ -846,13 +851,15 @@ export function useChat(options: UseChatOptions): UseChatReturn {
|
||||
: data.status === 'interrupted' ? 'interrupted'
|
||||
: data.status === 'awaiting_approval' ? 'awaiting_approval'
|
||||
: 'completed'
|
||||
onStreamEnd?.({
|
||||
conversationId: data.conversationId || streamConversationId,
|
||||
reason,
|
||||
assistantMessageId: data.assistantMessageId,
|
||||
persisted: data.persisted,
|
||||
messageCount: data.messageCount,
|
||||
})
|
||||
if (!errorFired) {
|
||||
onStreamEnd?.({
|
||||
conversationId: data.conversationId || streamConversationId,
|
||||
reason,
|
||||
assistantMessageId: data.assistantMessageId,
|
||||
persisted: data.persisted,
|
||||
messageCount: data.messageCount,
|
||||
})
|
||||
}
|
||||
|
||||
// Re-attach SSE if any generative task is still in flight, so the eventual
|
||||
// async_task_completed event reaches us live (otherwise the user has to
|
||||
@ -861,7 +868,7 @@ export function useChat(options: UseChatOptions): UseChatReturn {
|
||||
const reconnectableStatus = !data.status
|
||||
|| data.status === 'completed'
|
||||
|| data.status === 'idle'
|
||||
if (reconnectableStatus
|
||||
if (!errorFired && reconnectableStatus
|
||||
&& !reconnectingForAsyncTasks
|
||||
&& pendingAsyncTaskIds.size > 0
|
||||
&& streamConversationId) {
|
||||
@ -918,7 +925,8 @@ export function useChat(options: UseChatOptions): UseChatReturn {
|
||||
lifecycleStage.value = null
|
||||
// Clear queue on error to avoid stale state
|
||||
messageQueue.clear()
|
||||
expirePendingApprovals('failed')
|
||||
// The approval may still be pending after a rejected request. The view
|
||||
// reconciles it against the server's pending list in onStreamEnd.
|
||||
|
||||
if (errorFired) return
|
||||
errorFired = true
|
||||
|
||||
@ -808,6 +808,32 @@ const {
|
||||
// 流结束后刷新会话列表(更新 lastActiveTime / 标题等)
|
||||
await loadConversations()
|
||||
if (meta.conversationId && meta.conversationId === currentConversationId.value) {
|
||||
if (meta.reason === 'error') {
|
||||
// Keep the failed turn visible, but take approval state from the
|
||||
// server. A rejected approval SSE error leaves its pending row open.
|
||||
try {
|
||||
const approvalRes: any = await chatApi.getPendingApprovals(meta.conversationId)
|
||||
if (meta.conversationId !== currentConversationId.value) return
|
||||
const serverIds = new Set<string>((approvalRes.data || []).map((p: any) => p.pendingId))
|
||||
messages.value = messages.value.map((m) => {
|
||||
const pending = (m as any).metadata?.pendingApproval
|
||||
if (!pending?.pendingId || pending.status !== 'pending_approval') return m
|
||||
const active = serverIds.has(pending.pendingId)
|
||||
return {
|
||||
...m,
|
||||
status: active ? 'awaiting_approval'
|
||||
: m.status === 'awaiting_approval' ? 'failed' : m.status,
|
||||
metadata: {
|
||||
...(m as any).metadata,
|
||||
currentPhase: active ? 'awaiting_approval' : undefined,
|
||||
pendingApproval: { ...pending, status: active ? 'pending_approval' : 'expired' },
|
||||
},
|
||||
}
|
||||
})
|
||||
} catch {
|
||||
// Keep the local pending card if the authoritative read is unavailable.
|
||||
}
|
||||
}
|
||||
// Skip DB refresh for awaiting_approval / interrupted / error:
|
||||
// - awaiting_approval / interrupted: avoids overwriting local-only state
|
||||
// or breaking message ordering.
|
||||
@ -2065,10 +2091,6 @@ async function handleSendMessage(content: string, pendingApprovalId?: string) {
|
||||
return
|
||||
}
|
||||
|
||||
// 乐观更新审批状态
|
||||
const decision = trimmed === '/approve' ? 'approved' : 'denied'
|
||||
;(pendingMsg as any).metadata.pendingApproval.status = decision
|
||||
|
||||
inputText.value = ''
|
||||
chatInputRef.value?.clear?.()
|
||||
|
||||
@ -2082,8 +2104,6 @@ async function handleSendMessage(content: string, pendingApprovalId?: string) {
|
||||
})
|
||||
} catch (e: any) {
|
||||
console.error('Approval stream failed:', e)
|
||||
// 回滚乐观更新
|
||||
;(pendingMsg as any).metadata.pendingApproval.status = 'pending_approval'
|
||||
mcToast.error(e?.message || 'Approval failed')
|
||||
}
|
||||
return
|
||||
|
||||
Loading…
Reference in New Issue
Block a user