@@ -66,12 +66,13 @@ type Adapter struct {
6666
6767 nowFn func () time.Time
6868
69- mu sync.RWMutex
70- activeRuns map [string ]sessionBinding
71- sessionChats map [string ]string
72- requestRuns map [string ]string
73- lastProgressAt map [string ]time.Time
74- permissionCards map [string ]string // requestID -> card message_id
69+ mu sync.RWMutex
70+ activeRuns map [string ]sessionBinding
71+ sessionChats map [string ]string
72+ requestRuns map [string ]string
73+ lastProgressAt map [string ]time.Time
74+ permissionCards map [string ]string // requestID -> card message_id
75+ runPermissionCards map [string ]string // runKey -> card message_id
7576 // resolvedPermissions 记录已完成审批的 request_id,避免重复事件将状态回滚为 pending。
7677 resolvedPermissions map [string ]string // requestID -> decision
7778 userQuestionCards map [string ]string // requestID -> card message_id
@@ -106,6 +107,7 @@ func New(cfg Config, gateway GatewayClient, messenger Messenger, logger *log.Log
106107 requestRuns : make (map [string ]string ),
107108 lastProgressAt : make (map [string ]time.Time ),
108109 permissionCards : make (map [string ]string ),
110+ runPermissionCards : make (map [string ]string ),
109111 resolvedPermissions : make (map [string ]string ),
110112 userQuestionCards : make (map [string ]string ),
111113 pendingQuestions : make (map [string ]userQuestionEntry ),
@@ -353,6 +355,7 @@ func (a *Adapter) untrackRun(sessionID string, runID string) {
353355 delete (a .pendingQuestions , requestID )
354356 }
355357 }
358+ delete (a .runPermissionCards , key )
356359 delete (a .lastProgressAt , key )
357360 _ = binding
358361 }
@@ -412,19 +415,19 @@ func (a *Adapter) handleGatewayEvent(ctx context.Context, raw json.RawMessage) {
412415 requestID , toolName , operation , target , reason := extractPermissionRequest (envelope )
413416 if requestID != "" {
414417 if a .markPermissionPending (sessionID , runID , requestID , toolName , reason ) {
415- cardID , err := a . messenger . SendPermissionCard ( ctx , chatID , PermissionCardPayload {
416- RequestID : requestID ,
417- ToolName : toolName ,
418- Operation : operation ,
419- Target : target ,
420- Message : reason ,
421- })
422- if err == nil && strings . TrimSpace ( cardID ) != "" {
423- a . mu . Lock ()
424- a . permissionCards [ requestID ] = cardID
425- a . mu . Unlock ()
426- a . safeLog ( "permission card created request_id=%s card_id=%s runtime_type=%s" , requestID , strings . TrimSpace ( cardID ), runtimeType )
427- }
418+ a . upsertPermissionCard (
419+ ctx ,
420+ sessionID ,
421+ runID ,
422+ chatID ,
423+ PermissionCardPayload {
424+ RequestID : requestID ,
425+ ToolName : toolName ,
426+ Operation : operation ,
427+ Target : target ,
428+ Message : reason ,
429+ },
430+ )
428431 }
429432 return
430433 }
@@ -675,7 +678,7 @@ func (a *Adapter) handleRunProgressCard(ctx context.Context, sessionID string, r
675678 }
676679}
677680
678- // markPermissionPending 将权限请求映射到 run 卡片,返回是否需要发送审批按钮卡片 。
681+ // markPermissionPending 将权限请求映射到 run 卡片,返回当前事件是否应继续刷新审批交互卡片 。
679682func (a * Adapter ) markPermissionPending (sessionID string , runID string , requestID string , toolName string , reason string ) bool {
680683 normalizedRequestID := strings .TrimSpace (requestID )
681684 if normalizedRequestID == "" {
@@ -693,14 +696,15 @@ func (a *Adapter) markPermissionPending(sessionID string, runID string, requestI
693696 return false
694697 }
695698
696- shouldSendCard := strings . TrimSpace ( a . permissionCards [ normalizedRequestID ]) == ""
699+ alreadyPending := false
697700 binding .ApprovalStatus = "pending"
698701 found := false
699702 for i := range binding .ApprovalRecords {
700703 if binding .ApprovalRecords [i ].RequestID != normalizedRequestID {
701704 continue
702705 }
703706 found = true
707+ alreadyPending = isApprovalPendingDecision (binding .ApprovalRecords [i ].Decision )
704708 if strings .TrimSpace (binding .ApprovalRecords [i ].ToolName ) == "" && strings .TrimSpace (toolName ) != "" {
705709 binding .ApprovalRecords [i ].ToolName = strings .TrimSpace (toolName )
706710 }
@@ -720,14 +724,6 @@ func (a *Adapter) markPermissionPending(sessionID string, runID string, requestI
720724 if strings .TrimSpace (reason ) != "" {
721725 binding .LastSummary = strings .TrimSpace (reason )
722726 }
723- pendingCount := 0
724- for _ , entry := range binding .ApprovalRecords {
725- if isApprovalPendingDecision (entry .Decision ) {
726- pendingCount ++
727- }
728- }
729- // 同一 run 仅展示一张待审批交互卡,后续审批排队等待前一条处理完成。
730- shouldSendCard = shouldSendCard && pendingCount == 1
731727 a .activeRuns [key ] = binding
732728 a .requestRuns [normalizedRequestID ] = key
733729
@@ -741,7 +737,54 @@ func (a *Adapter) markPermissionPending(sessionID string, runID string, requestI
741737 a .safeLog ("update pending approval card failed: %v" , err )
742738 }
743739 }
744- return shouldSendCard
740+ return ! (found && alreadyPending )
741+ }
742+
743+ // upsertPermissionCard 在同一 run 内复用一张审批卡片,后续审批请求覆盖刷新该卡片。
744+ func (a * Adapter ) upsertPermissionCard (
745+ ctx context.Context ,
746+ sessionID string ,
747+ runID string ,
748+ chatID string ,
749+ payload PermissionCardPayload ,
750+ ) {
751+ key := runBindingKey (sessionID , runID )
752+ normalizedRequestID := strings .TrimSpace (payload .RequestID )
753+ if key == "|" || normalizedRequestID == "" {
754+ return
755+ }
756+
757+ a .mu .RLock ()
758+ existingCardID := strings .TrimSpace (a .runPermissionCards [key ])
759+ a .mu .RUnlock ()
760+
761+ if existingCardID == "" {
762+ cardID , err := a .messenger .SendPermissionCard (ctx , chatID , payload )
763+ if err != nil {
764+ a .safeLog ("permission card create failed request_id=%s err=%v" , normalizedRequestID , err )
765+ return
766+ }
767+ cardID = strings .TrimSpace (cardID )
768+ if cardID == "" {
769+ return
770+ }
771+ a .mu .Lock ()
772+ a .runPermissionCards [key ] = cardID
773+ a .permissionCards [normalizedRequestID ] = cardID
774+ a .mu .Unlock ()
775+ a .safeLog ("permission card created request_id=%s card_id=%s run_key=%s" , normalizedRequestID , cardID , key )
776+ return
777+ }
778+
779+ a .safeLog ("permission card update start request_id=%s card_id=%s" , normalizedRequestID , existingCardID )
780+ if err := a .messenger .UpdatePendingPermissionCard (ctx , existingCardID , payload ); err != nil {
781+ a .safeLog ("permission card update failed request_id=%s card_id=%s err=%v" , normalizedRequestID , existingCardID , err )
782+ return
783+ }
784+ a .mu .Lock ()
785+ a .permissionCards [normalizedRequestID ] = existingCardID
786+ a .mu .Unlock ()
787+ a .safeLog ("permission card update done request_id=%s card_id=%s" , normalizedRequestID , existingCardID )
745788}
746789
747790// markUserQuestionPending 记录 ask_user 待回答问题,并挂接到 run 状态卡上下文。
@@ -840,6 +883,11 @@ func (a *Adapter) updateApprovalStatus(requestID string, decision string) {
840883 return
841884 }
842885 a .mu .Lock ()
886+ if alreadyDecision , resolved := a .resolvedPermissions [normalizedRequestID ]; resolved &&
887+ normalizeApprovalDecision (alreadyDecision ) == normalizedDecision {
888+ a .mu .Unlock ()
889+ return
890+ }
843891 key := a .requestRuns [normalizedRequestID ]
844892 binding , ok := a .activeRuns [key ]
845893 var resolvedApproval * approvalEntry
@@ -885,6 +933,9 @@ func (a *Adapter) updateApprovalStatus(requestID string, decision string) {
885933 statusPayload = binding .statusCardPayload ()
886934 }
887935 permCardID := strings .TrimSpace (a .permissionCards [normalizedRequestID ])
936+ if permCardID == "" {
937+ permCardID = strings .TrimSpace (a .runPermissionCards [key ])
938+ }
888939 a .resolvedPermissions [normalizedRequestID ] = normalizedDecision
889940 a .mu .Unlock ()
890941
0 commit comments