@@ -1335,39 +1335,18 @@ func (r *LocalRuntime) processToolCalls(ctx context.Context, sess *session.Sessi
13351335//
13361336// The approval flow considers (in order):
13371337//
1338- // 1. Session-level permissions (if configured) - checked first
1339- // a. Per-tool settings (Tools map) - most specific, checked first
1340- // b. Pattern-based Allow/Deny - broader rules, checked as fallback
1338+ // 1. Session-level permissions (if configured) - pattern-based Allow/Deny rules
13411339// 2. Team-level permissions config - checked second
13421340// 3. sess.ToolsApproved (--yolo flag) - auto-approve all
13431341// 4. tool.Annotations.ReadOnlyHint - auto-approve read-only tools
13441342// 5. Default: ask for user confirmation
13451343//
1346- // Example session permissions configurations :
1344+ // Example session permissions configuration :
13471345//
1348- // // Per-tool settings - granular control per tool
1349- // sess.Permissions = &session.PermissionsConfig{
1350- // Tools: map[string]session.ToolPermission{
1351- // "shell": {Enabled: ptr(true), Mode: "ask"}, // require confirmation
1352- // "filesystem": {Enabled: ptr(true), Mode: "always_allow"}, // auto-approve
1353- // "dangerous": {Enabled: ptr(false)}, // disabled
1354- // },
1355- // }
1356- //
1357- // // Pattern-based rules - apply to multiple tools at once
13581346// sess.Permissions = &session.PermissionsConfig{
13591347// Allow: []string{"read_*", "think"}, // auto-approve matching tools
13601348// Deny: []string{"shell", "exec_*"}, // block matching tools
13611349// }
1362- //
1363- // // Mixed: per-tool settings take priority over patterns
1364- // sess.Permissions = &session.PermissionsConfig{
1365- // Tools: map[string]session.ToolPermission{
1366- // "shell": {Enabled: ptr(true), Mode: "ask"}, // overrides Deny pattern
1367- // },
1368- // Allow: []string{"*"}, // applies to tools not in Tools map
1369- // Deny: []string{"shell"}, // ignored for shell since it's in Tools map
1370- // }
13711350func (r * LocalRuntime ) executeWithApproval (
13721351 ctx context.Context ,
13731352 sess * session.Session ,
@@ -1389,81 +1368,50 @@ func (r *LocalRuntime) executeWithApproval(
13891368 }
13901369 }
13911370
1392- // requiresConfirmation tracks if per-tool settings require user confirmation
1393- // (skipping pattern-based rules and other auto-approve checks)
1394- requiresConfirmation := false
1395-
13961371 // 1. Check session-level permissions first (if configured)
13971372 if sess .Permissions != nil {
1398- // 1a. Check Tools map first (new per-tool settings)
1399- if perm := sess .Permissions .GetToolPermission (toolName ); perm != nil {
1400- // Check if tool is disabled
1401- if ! sess .Permissions .IsToolEnabled (toolName ) {
1402- slog .Debug ("Tool disabled by session permissions" , "tool" , toolName , "session_id" , sess .ID )
1403- r .addToolErrorResponse (ctx , sess , toolCall , tool , events , a , fmt .Sprintf ("Tool '%s' is disabled by session permissions." , toolName ))
1404- return false
1405- }
1406- // Check permission mode
1407- mode := sess .Permissions .GetToolMode (toolName )
1408- switch mode {
1409- case session .PermissionModeAlwaysAllow :
1410- slog .Debug ("Tool auto-approved by session per-tool permissions" , "tool" , toolName , "mode" , mode , "session_id" , sess .ID )
1411- runTool ()
1412- return false
1413- case session .PermissionModeAsk :
1414- default :
1415- slog .Debug ("Tool requires confirmation by session per-tool permissions " , "tool" , toolName , "mode" , mode , "session_id" , sess .ID )
1416- requiresConfirmation = true
1417- }
1418- }
1419-
1420- // 1b. Fall back to pattern-based Allow/Deny rules (only if not already handled by Tools map)
1421- if ! requiresConfirmation {
1422- sessionChecker := permissions .NewChecker (& latest.PermissionsConfig {
1423- Allow : sess .Permissions .Allow ,
1424- Deny : sess .Permissions .Deny ,
1425- })
1426- decision := sessionChecker .CheckWithArgs (toolName , toolArgs )
1427- switch decision {
1428- case permissions .Deny :
1429- slog .Debug ("Tool denied by session permissions" , "tool" , toolName , "session_id" , sess .ID )
1430- r .addToolErrorResponse (ctx , sess , toolCall , tool , events , a , fmt .Sprintf ("Tool '%s' is denied by session permissions." , toolName ))
1431- return false
1432- case permissions .Allow :
1433- slog .Debug ("Tool auto-approved by session permissions" , "tool" , toolName , "session_id" , sess .ID )
1434- runTool ()
1435- return false
1436- case permissions .Ask :
1437- // Fall through to team permissions
1438- }
1373+ sessionChecker := permissions .NewChecker (& latest.PermissionsConfig {
1374+ Allow : sess .Permissions .Allow ,
1375+ Deny : sess .Permissions .Deny ,
1376+ })
1377+ decision := sessionChecker .CheckWithArgs (toolName , toolArgs )
1378+ switch decision {
1379+ case permissions .Deny :
1380+ slog .Debug ("Tool denied by session permissions" , "tool" , toolName , "session_id" , sess .ID )
1381+ r .addToolErrorResponse (ctx , sess , toolCall , tool , events , a , fmt .Sprintf ("Tool '%s' is denied by session permissions." , toolName ))
1382+ return false
1383+ case permissions .Allow :
1384+ slog .Debug ("Tool auto-approved by session permissions" , "tool" , toolName , "session_id" , sess .ID )
1385+ runTool ()
1386+ return false
1387+ case permissions .Ask :
1388+ // Fall through to team permissions
14391389 }
14401390 }
14411391
1442- // 2. Check team-level permissions config (skip if per-tool ask mode is set)
1443- if ! requiresConfirmation {
1444- if permChecker := r .team .Permissions (); permChecker != nil {
1445- decision := permChecker .CheckWithArgs (toolName , toolArgs )
1446- switch decision {
1447- case permissions .Deny :
1448- slog .Debug ("Tool denied by team permissions config" , "tool" , toolName , "session_id" , sess .ID )
1449- r .addToolErrorResponse (ctx , sess , toolCall , tool , events , a , fmt .Sprintf ("Tool '%s' is denied by permissions configuration." , toolName ))
1450- return false
1451- case permissions .Allow :
1452- slog .Debug ("Tool auto-approved by team permissions config" , "tool" , toolName , "session_id" , sess .ID )
1453- runTool ()
1454- return false
1455- case permissions .Ask :
1456- // Fall through to normal approval flow
1457- }
1458- }
1459-
1460- // Check --yolo flag or read-only hint (skip if per-tool ask mode is set)
1461- if sess .ToolsApproved || tool .Annotations .ReadOnlyHint {
1392+ // 2. Check team-level permissions config
1393+ if permChecker := r .team .Permissions (); permChecker != nil {
1394+ decision := permChecker .CheckWithArgs (toolName , toolArgs )
1395+ switch decision {
1396+ case permissions .Deny :
1397+ slog .Debug ("Tool denied by team permissions config" , "tool" , toolName , "session_id" , sess .ID )
1398+ r .addToolErrorResponse (ctx , sess , toolCall , tool , events , a , fmt .Sprintf ("Tool '%s' is denied by permissions configuration." , toolName ))
1399+ return false
1400+ case permissions .Allow :
1401+ slog .Debug ("Tool auto-approved by team permissions config" , "tool" , toolName , "session_id" , sess .ID )
14621402 runTool ()
14631403 return false
1404+ case permissions .Ask :
1405+ // Fall through to normal approval flow
14641406 }
14651407 }
14661408
1409+ // 3. Check --yolo flag or read-only hint
1410+ if sess .ToolsApproved || tool .Annotations .ReadOnlyHint {
1411+ runTool ()
1412+ return false
1413+ }
1414+
14671415 // Ask user for confirmation
14681416 slog .Debug ("Tools not approved, waiting for resume" , "tool" , toolCall .Function .Name , "session_id" , sess .ID )
14691417 events <- ToolCallConfirmation (toolCall , tool , a .Name ())
0 commit comments