Skip to content

Commit 2f572a5

Browse files
andypalmiandypalmi
andauthored
fix(expert): dismiss pending tool-approval cards when a new message is sent (#7663)
## Description Fixes #7662. When the Expert deferred a tool call for approval, sending a new message instead of clicking Allow or Deny left the approval card active, with its buttons still live and the card looking pending. The backend already abandons the deferred tool calls and addresses the new message, but the frontend never dismissed the stale card. ## Changes - `handleQuery` now cancels the open approval batch when a new message is sent, so pending cards are dismissed instead of lingering. - `cancelPendingToolApprovals` takes an outcome status: a new message marks unanswered cards as `cancelled` (matching how the agent self-heals those calls), while chat stop and Start Over keep the existing `denied` outcome. - `ToolApprovalCard` renders a `Cancelled` outcome label. Co-authored-by: andypalmi <andrea@flowfuse.com>
1 parent 5abb3d8 commit 2f572a5

2 files changed

Lines changed: 12 additions & 8 deletions

File tree

frontend/src/components/expert/components/messages/components/resources/ToolApprovalCard.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export default {
6767
},
6868
status: {
6969
type: String,
70-
// 'pending' | 'approved' | 'always-allowed' | 'denied' | 'always-denied'
70+
// 'pending' | 'approved' | 'always-allowed' | 'denied' | 'always-denied' | 'cancelled'
7171
default: 'pending'
7272
},
7373
disabled: {
@@ -100,7 +100,8 @@ export default {
100100
approved: 'Allowed',
101101
'always-allowed': 'Allowed for this chat',
102102
denied: 'Denied',
103-
'always-denied': 'Denied for this chat'
103+
'always-denied': 'Denied for this chat',
104+
cancelled: 'Cancelled'
104105
}[this.effectiveStatus] || (this.effectiveStatus === 'pending' ? '' : 'Denied')
105106
},
106107
hasParams () {

frontend/src/stores/product-expert.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@ export const useProductExpertStore = defineStore('product-expert', {
212212
agentStore.sessionExpiredShown = false
213213
}
214214

215+
// A new message supersedes any pending approval cards; the agent cancels the
216+
// abandoned tool calls next turn, so dismiss the cards now.
217+
this.cancelPendingToolApprovals('cancelled')
218+
215219
// Add user message
216220
this.addUserMessage(query)
217221

@@ -564,16 +568,15 @@ export const useProductExpertStore = defineStore('product-expert', {
564568
agentStore.abortController = null
565569
}
566570
},
567-
// Abandon the open approval batch (chat stop / Start Over). The agent already left
568-
// memory when it deferred, so there is nothing to unblock — just mark any unanswered
569-
// card denied so it stops looking pending, and drop the batch (#421). If the user
570-
// later sends a new message the agent self-heals the abandoned tool calls.
571-
cancelPendingToolApprovals () {
571+
// Abandon the open approval batch (chat stop / Start Over / new message): mark any
572+
// unanswered card with the given outcome so it stops looking pending, and drop the
573+
// batch. A new message passes 'cancelled'; stop/Start Over keep 'denied'.
574+
cancelPendingToolApprovals (status = 'denied') {
572575
const batch = this._approvalBatch
573576
if (!batch) return
574577
const permStore = useProductAssistantStore()
575578
for (const id of Object.keys(batch.toolKeys)) {
576-
if (!(id in batch.decisions)) permStore.setToolApprovalStatus(id, 'denied')
579+
if (!(id in batch.decisions)) permStore.setToolApprovalStatus(id, status)
577580
}
578581
this._approvalBatch = null
579582
},

0 commit comments

Comments
 (0)