@@ -73,13 +73,20 @@ type Service interface {
7373 SubscribeNotifications(ctx context.Context) <-chan pubsub.Event[PermissionNotification]
7474}
7575
76+// PermissionKey is a composite key for session permission lookups.
77+type PermissionKey struct {
78+ SessionID string
79+ ToolName string
80+ Action string
81+ Path string
82+}
83+
7684type permissionService struct {
7785 *pubsub.Broker[PermissionRequest]
7886
7987 notificationBroker *pubsub.Broker[PermissionNotification]
8088 workingDir string
81โ sessionPermissions []PermissionRequest
82โ sessionPermissionsMu sync.RWMutex
89+ sessionPermissions *csync.Map[PermissionKey, bool]
8390 pendingRequests *csync.Map[string, chan bool]
8491 autoApproveSessions map[string]bool
8592 autoApproveSessionsMu sync.RWMutex
@@ -102,9 +109,12 @@ func (s *permissionService) GrantPersistent(permission PermissionRequest) {
102109 respCh <- true
103110 }
104111
105โ s.sessionPermissionsMu.Lock()
106โ s.sessionPermissions = append(s.sessionPermissions, permission)
107โ s.sessionPermissionsMu.Unlock()
112+ s.sessionPermissions.Set(PermissionKey{
113+ SessionID: permission.SessionID,
114+ ToolName: permission.ToolName,
115+ Action: permission.Action,
116+ Path: permission.Path,
117+ }, true)
108118
109119 s.activeRequestMu.Lock()
110120 if s.activeRequest != nil && s.activeRequest.ID == permission.ID {
@@ -171,12 +181,13 @@ func (s *permissionService) Request(ctx context.Context, opts CreatePermissionRe
171181 return true, nil
172182 }
173183
184+ s.requestMu.Lock()
185+ defer s.requestMu.Unlock()
186+
174187 // tell the UI that a permission was requested
175188 s.notificationBroker.Publish(pubsub.CreatedEvent, PermissionNotification{
176189 ToolCallID: opts.ToolCallID,
177190 })
178โ s.requestMu.Lock()
179โ defer s.requestMu.Unlock()
180191
181192 s.autoApproveSessionsMu.RLock()
182193 autoApprove := s.autoApproveSessions[opts.SessionID]
@@ -214,18 +225,18 @@ func (s *permissionService) Request(ctx context.Context, opts CreatePermissionRe
214225 Params: opts.Params,
215226 }
216227
217โ s.sessionPermissionsMu.RLock()
218โ for _, p := range s.sessionPermissions {
219โ if p.ToolName == permission.ToolName && p.Action == permission.Action && p.SessionID == permission.SessionID && p.Path == permission.Path {
220โ s.sessionPermissionsMu.RUnlock()
221โ s.notificationBroker.Publish(pubsub.CreatedEvent, PermissionNotification{
222โ ToolCallID: opts.ToolCallID,
223โ Granted: true,
224โ })
225โ return true, nil
226โ }
228+ if _, ok := s.sessionPermissions.Get(PermissionKey{
229+ SessionID: permission.SessionID,
230+ ToolName: permission.ToolName,
231+ Action: permission.Action,
232+ Path: permission.Path,
233+ }); ok {
234+ s.notificationBroker.Publish(pubsub.CreatedEvent, PermissionNotification{
235+ ToolCallID: opts.ToolCallID,
236+ Granted: true,
237+ })
238+ return true, nil
227239 }
228โ s.sessionPermissionsMu.RUnlock()
229240
230241 s.activeRequestMu.Lock()
231242 s.activeRequest = &permission
@@ -269,7 +280,7 @@ func NewPermissionService(workingDir string, skip bool, allowedTools []string) S
269280 Broker: pubsub.NewBroker[PermissionRequest](),
270281 notificationBroker: pubsub.NewBroker[PermissionNotification](),
271282 workingDir: workingDir,
272โ sessionPermissions: make([]PermissionRequest, 0),
283+ sessionPermissions: csync.NewMap[PermissionKey, bool](),
273284 autoApproveSessions: make(map[string]bool),
274285 skip: skip,
275286 allowedTools: allowedTools,