@@ -14,7 +14,8 @@ import (
1414type Mode string
1515
1616const (
17- ModeNormal Mode = "normal"
17+ ModeStrict Mode = "strict"
18+ ModeBalanced Mode = "balanced"
1819 ModeAcceptEdits Mode = "accept_edits"
1920 ModeTrust Mode = "trust"
2021 ModePlan Mode = "plan" // 兼容旧调用;新代码应使用 SetPlanMode
@@ -82,7 +83,6 @@ type HookRequest struct {
8283
8384type AuditEntry struct {
8485 Time time.Time
85- Profile Profile
8686 Mode Mode
8787 PlanMode bool
8888 Tool string
@@ -101,7 +101,6 @@ type FilesystemRoots struct {
101101type Engine struct {
102102 workspace string
103103 fsRoots FilesystemRoots
104- profile Profile
105104 rules * RuleSet
106105
107106 mu sync.RWMutex
@@ -115,7 +114,7 @@ type Engine struct {
115114 toolMeta map [string ]ToolMetadata
116115}
117116
118- func NewEngine (cwd string , profile Profile , rules * RuleSet , onAudit func (AuditEntry )) (* Engine , error ) {
117+ func NewEngine (cwd string , mode Mode , rules * RuleSet , onAudit func (AuditEntry )) (* Engine , error ) {
119118 store , err := NewStore (config .ApprovalsPath (cwd ))
120119 if err != nil {
121120 return nil , fmt .Errorf ("load approvals: %w" , err )
@@ -126,9 +125,8 @@ func NewEngine(cwd string, profile Profile, rules *RuleSet, onAudit func(AuditEn
126125 ReadRoots : []string {cwd },
127126 WriteRoots : []string {cwd },
128127 },
129- profile : profile ,
130128 rules : rules ,
131- mode : ModeNormal ,
129+ mode : mode ,
132130 sessionAllow : make (map [string ]storedEntry ),
133131 store : store ,
134132 onAudit : onAudit ,
@@ -311,7 +309,7 @@ const (
311309)
312310
313311func (e * Engine ) decide (info toolInfo , mode Mode , planMode bool ) (ruleAction , string ) {
314- // Deny rules have highest priority — override even ProfileOff and cached approvals .
312+ // Deny rules have highest priority — override everything .
315313 var ruleResult ruleAction
316314 var ruleMatched bool
317315 if e .rules != nil {
@@ -321,8 +319,7 @@ func (e *Engine) decide(info toolInfo, mode Mode, planMode bool) (ruleAction, st
321319 }
322320 }
323321
324- // Paths outside configured roots always require explicit approval,
325- // regardless of profile, skill allows, or cached approvals.
322+ // Paths outside configured roots always require explicit approval.
326323 if info .outsideRoots {
327324 return ruleAsk , info .reason
328325 }
@@ -337,19 +334,16 @@ func (e *Engine) decide(info toolInfo, mode Mode, planMode bool) (ruleAction, st
337334 }
338335 }
339336
340- if e .profile == ProfileOff {
341- return ruleAllow , ""
342- }
343-
344337 if e .allowed (info .key ) || e .allowedSession (info .capability ) {
345338 return ruleAllow , ""
346339 }
347340
348- // Allow rules take precedence over mode/profile defaults.
341+ // Allow rules take precedence over mode defaults.
349342 if ruleMatched && ruleResult == ruleAllow {
350343 return ruleAllow , "allowed by permission rule"
351344 }
352345
346+ // Plan mode enforces read-only regardless of mode.
353347 if planMode {
354348 switch info .capability {
355349 case CapRead , CapInternal :
@@ -358,31 +352,33 @@ func (e *Engine) decide(info toolInfo, mode Mode, planMode bool) (ruleAction, st
358352 return ruleDeny , "plan mode is read-only"
359353 }
360354 }
361- if mode == ModeTrust {
362- return ruleAllow , ""
363- }
364- if mode == ModeAcceptEdits && info .capability == CapWrite {
365- return ruleAllow , "auto-approved in accept-edits mode"
366- }
367355
368- switch e .profile {
369- case ProfileStrict :
356+ // Mode-based decisions (strict → balanced → accept-edits → trust).
357+ switch mode {
358+ case ModeTrust :
359+ return ruleAllow , ""
360+ case ModeAcceptEdits :
361+ switch info .capability {
362+ case CapRead , CapInternal , CapWrite :
363+ return ruleAllow , ""
364+ default :
365+ return ruleAsk , "approval required for side effects"
366+ }
367+ case ModeStrict :
370368 switch info .capability {
371369 case CapRead , CapInternal :
372370 return ruleAllow , ""
373371 case CapWrite :
374- return ruleAsk , "strict profile requires approval for writes"
372+ return ruleAsk , "strict mode requires approval for writes"
375373 default :
376- return ruleDeny , "strict profile denies this capability"
374+ return ruleDeny , "strict mode denies this capability"
377375 }
378- default :
376+ default : // ModeBalanced
379377 switch info .capability {
380378 case CapRead , CapInternal :
381379 return ruleAllow , ""
382- case CapWrite , CapExec , CapHook , CapNetwork , CapSubagent , CapUnknown :
383- return ruleAsk , "approval required for side effects"
384380 default :
385- return ruleAsk , "approval required"
381+ return ruleAsk , "approval required for side effects "
386382 }
387383 }
388384}
@@ -498,7 +494,6 @@ func (e *Engine) audit(info toolInfo, mode Mode, planMode bool, decision string,
498494 }
499495 e .onAudit (AuditEntry {
500496 Time : time .Now (),
501- Profile : e .profile ,
502497 Mode : mode ,
503498 PlanMode : planMode ,
504499 Tool : info .tool ,
0 commit comments