@@ -95,10 +95,6 @@ type rootModel struct {
9595 rpc * daemonRPCClient
9696 rpcConnected bool
9797
98- // localGrantCache stores granted permissions for the current session (non-RPC only).
99- // Used by WithGrantCache to allow non-blocking permission resumption in local mode.
100- localGrantCache map [string ]map [string ]bool
101-
10298 // currentSessionID tracks the active session ID used in RPC messages.
10399 currentSessionID string
104100}
@@ -1024,7 +1020,6 @@ func (m *rootModel) Update(e tea.Msg) (tea.Model, tea.Cmd) {
10241020
10251021 case clientmsg.ChoiceSelectedMsg :
10261022 m .statusBar .CurrentState = i18n .T ("client.status.idle" )
1027- toolName := m .permBar .ToolName
10281023 m .permBar = permission.PermissionBar {}
10291024
10301025 if msg .Index < 0 {
@@ -1033,30 +1028,27 @@ func (m *rootModel) Update(e tea.Msg) (tea.Model, tea.Cmd) {
10331028 }
10341029
10351030 if msg .Index == permission .PermissionAllow {
1031+ // Permission resumption is now driven entirely by the
1032+ // PermissionAllow / PermissionDeny magic words. The runtime
1033+ // intercepts the magic word, drains session.PendingPermission,
1034+ // and runs (Allow) or denies (Deny) the tool.
1035+ //
1036+ // The legacy non-blocking "store-then-resend" flow (which relied
1037+ // on a server-side GrantCache) has been removed. We keep the
1038+ // resend step so the LLM gets a fresh turn to call the tool
1039+ // again with the magic word now in flight as a user message —
1040+ // the runtime will resolve it before re-running the tool.
10361041 if m .rpcConnected {
1037- // RPC path: call execution.resume to store grant in daemon cache,
1038- // then resend last user message to re-enter the LLM loop.
1039- if m .currentSessionID != "" && toolName != "" {
1040- go func () {
1041- _ , _ = m .rpc .client .Call (context .Background (), "execution.resume" , map [string ]any {
1042- "session_id" : m .currentSessionID ,
1043- "tool_name" : toolName ,
1044- })
1045- }()
1046- // Resend last user message silently.
1047- if lastMsg := m .getLastUserMessage (); lastMsg != "" {
1048- m .rpcSendMessage (lastMsg )
1049- }
1042+ // RPC path: resend the last user message. The user message
1043+ // should already carry the PermissionAllow magic word
1044+ // (set by the chat store when the user clicked Approve).
1045+ if lastMsg := m .getLastUserMessage (); lastMsg != "" {
1046+ m .rpcSendMessage (lastMsg )
10501047 }
10511048 } else {
1052- // Local path: store in local grant cache, then resend.
1053- if m .localGrantCache == nil {
1054- m .localGrantCache = make (map [string ]map [string ]bool )
1055- }
1056- if m .localGrantCache [m .currentSessionID ] == nil {
1057- m .localGrantCache [m .currentSessionID ] = make (map [string ]bool )
1058- }
1059- m.localGrantCache [m.currentSessionID ][toolName ] = true
1049+ // Local path: resend last user message through the
1050+ // program. The user message carries the PermissionAllow
1051+ // magic word that the runtime intercepts.
10601052 if lastMsg := m .getLastUserMessage (); lastMsg != "" {
10611053 m .program .Send (clientmsg.UserSendMsg {Text : lastMsg })
10621054 }
@@ -1290,14 +1282,13 @@ func (m *rootModel) handleSend(e clientmsg.UserSendMsg) (tea.Model, tea.Cmd) {
12901282 TokensUsed : tokenUsage ,
12911283 })
12921284 })
1293- // Set up local grant cache for non-blocking permission resumption.
1294- rt .WithGrantCache (func (sessionID , toolName string ) bool {
1295- if m .localGrantCache == nil {
1296- return false
1297- }
1298- tools , ok := m .localGrantCache [sessionID ]
1299- return ok && tools [toolName ]
1300- })
1285+ // NOTE: Old WithGrantCache / localGrantCache non-blocking permission
1286+ // flow has been removed. Permission resumption now flows through
1287+ // the PermissionAllow / PermissionDeny magic words (see
1288+ // agents.resolvePermissionMagicWord). The runtime intercepts the
1289+ // magic word before it reaches the LLM, drains
1290+ // session.PendingPermission, and runs the tool (Allow) or appends
1291+ // a "Permission Denied" result (Deny).
13011292
13021293 ask .OnAskUserPending (func (d events.AskUserPendingData ) {
13031294 m .pendingAskUserData = & d
0 commit comments