echo_orchestration 0.1.0

Orchestration layer for echo-agent framework (workflow, human-loop, tasks)
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
//! 权限服务 (PermissionService)
//!
//! 统一的权限检查入口,整合:
//! - PermissionMode: 权限模式
//! - RuleRegistry: 规则注册表
//! - SessionApprovalCache: 会话级审批缓存
//! - DenialTracker: 连续拒绝升级
//! - Classifier: AI 分类器(auto 模式)
//! - PermissionRequestHandler: 权限请求处理
//!
//! ## 审批管线
//!
//! ```text
//! check() → check_with_permissions()
//!   1. BypassPermissions → Allow
//!   2. Plan 模式 → 按 permissions 过滤
//!   3. 规则匹配 → Allow/Deny/Ask
//!   4. 缓存检查 → 命中则 AutoApprove
//!   5. DenialTracker → 连续拒绝则升级
//!   6. 模式分发:
//!      - Auto → Classifier
//!      - Default → RequestHandler
//!   7. 缓存写入(带 scope 的审批)
//! ```
//!
//! ## 使用示例
//!
//! ```rust,no_run
//! use async_trait::async_trait;
//! use echo_core::error::Result;
//! use echo_core::tools::permission::PermissionMode;
//! use echo_orchestration::human_loop::{
//!     PermissionRequest, PermissionRequestHandler, PermissionResponse, PermissionService,
//! };
//! use std::sync::Arc;
//!
//! struct AllowAllHandler;
//!
//! #[async_trait]
//! impl PermissionRequestHandler for AllowAllHandler {
//!     async fn handle(&self, _request: PermissionRequest) -> Result<PermissionResponse> {
//!         Ok(PermissionResponse::allowed())
//!     }
//! }
//!
//! # async fn example(handler: Arc<dyn PermissionRequestHandler>) -> Result<()> {
//! let service = PermissionService::new()
//!     .with_mode(PermissionMode::Auto)
//!     .with_request_handler(handler);
//!
//! let decision = service.check("Bash", &serde_json::json!({"command": "ls"})).await?;
//! # Ok(())
//! # }
//! # let handler: Arc<dyn PermissionRequestHandler> = Arc::new(AllowAllHandler);
//! # let _ = example(handler);
//! ```

use async_trait::async_trait;
use echo_core::tools::permission::{
    PermissionDecision, PermissionMode, PermissionRule, RuleBehavior, RuleMatcher, RuleRegistry,
    RuleSource, ToolPermission,
};
use serde_json::Value;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::RwLock;

use super::approval_cache::SessionApprovalCache;
use super::audit::{PermissionAuditEntry, PermissionAuditSink};
use super::classifier::{Classifier, ClassifierContext, DenialTracker};
use super::permission::{
    PermissionRequest, PermissionRequestHandler, PermissionResponse, PermissionResponseDecision,
    PermissionUpdate, RiskLevel,
};
use super::policy::ApprovalScope;
use super::protected::{ProtectedPathChecker, ProtectedPathResult};
use echo_core::error::Result;

// ── 超时策略 ────────────────────────────────────────────────────────────────

/// 审批超时策略
///
/// 当审批请求超过指定超时时间后,决定系统如何处理。
#[derive(Debug, Clone, Default)]
pub enum TimeoutStrategy {
    /// 超时后拒绝执行(默认行为)
    #[default]
    Reject,
    /// 超时后自动批准
    AutoApprove {
        /// 自动批准原因
        reason: String,
    },
    /// 超时后升级为人工交互
    Escalate,
}

// ── 权限服务配置 ────────────────────────────────────────────────────────────────

/// 权限服务配置
#[derive(Debug, Clone)]
pub struct PermissionServiceConfig {
    /// 权限模式
    pub mode: PermissionMode,
    /// 最大连续拒绝次数(超过则升级为人工审批)
    pub max_consecutive_denials: u32,
    /// 最大总拒绝次数(超过则升级为人工审批)
    pub max_total_denials: u32,
    /// 是否启用 classifier(auto 模式)
    pub enable_classifier: bool,
    /// 超时策略
    pub timeout_strategy: TimeoutStrategy,
    /// 是否禁止 BypassPermissions 模式(企业部署用)
    pub bypass_disabled: bool,
    /// 审批缓存 TTL(None = 永不过期,参考 Claude Code: 1h Max / 5min Pro)
    pub cache_ttl: Option<Duration>,
}

impl Default for PermissionServiceConfig {
    fn default() -> Self {
        Self {
            mode: PermissionMode::Default,
            max_consecutive_denials: DenialTracker::DEFAULT_MAX_CONSECUTIVE,
            max_total_denials: DenialTracker::DEFAULT_MAX_TOTAL,
            enable_classifier: true,
            timeout_strategy: TimeoutStrategy::Reject,
            bypass_disabled: false,
            cache_ttl: Some(Duration::from_secs(30 * 60)), // 默认 30 分钟
        }
    }
}

// ── 权限服务 ────────────────────────────────────────────────────────────────────

/// 权限服务 - 统一的权限检查入口
///
/// 审批管线:
/// 1. BypassPermissions → Allow
/// 2. Plan 模式 → 按 permissions 过滤
/// 3. 规则匹配 → Allow/Deny/Ask
/// 4. 缓存检查 → 命中则 AutoApprove
/// 5. DenialTracker → 连续拒绝则升级
/// 6. 模式分发 → Classifier / Handler
/// 7. 缓存写入(带 scope 的审批)
pub struct PermissionService {
    /// 配置
    config: RwLock<PermissionServiceConfig>,
    /// 规则注册表
    rules: RwLock<RuleRegistry>,
    /// 会话级审批缓存
    cache: SessionApprovalCache,
    /// 连续拒绝跟踪器
    denial_tracker: tokio::sync::Mutex<DenialTracker>,
    /// Classifier(auto 模式)
    classifier: Option<Arc<dyn Classifier>>,
    /// 权限请求处理器
    request_handler: Arc<dyn PermissionRequestHandler>,
    /// 受保护路径检查器
    protected_paths: ProtectedPathChecker,
    /// 审计 Sink(可选)
    audit_sink: Option<Arc<dyn PermissionAuditSink>>,
    /// 最近一次审批中用户修改的参数(side channel)
    last_modified_args: RwLock<Option<Value>>,
}

impl PermissionService {
    /// 创建新的权限服务
    pub fn new() -> Self {
        let config = PermissionServiceConfig::default();
        let max_denials = config.max_consecutive_denials;
        let cache = match config.cache_ttl {
            Some(ttl) => SessionApprovalCache::with_ttl(ttl),
            None => SessionApprovalCache::new(),
        };
        Self {
            config: RwLock::new(config),
            rules: RwLock::new(RuleRegistry::new()),
            cache,
            denial_tracker: tokio::sync::Mutex::new(DenialTracker::with_max_consecutive(
                max_denials,
            )),
            classifier: None,
            request_handler: Arc::new(NullPermissionRequestHandler),
            protected_paths: ProtectedPathChecker::new(),
            audit_sink: None,
            last_modified_args: RwLock::new(None),
        }
    }

    /// 从 `HumanLoopProvider` 创建权限服务
    ///
    /// 便捷构造方法,自动将 Provider 适配为 `PermissionRequestHandler`。
    pub fn from_provider(provider: Arc<dyn super::HumanLoopProvider>) -> Self {
        let handler: Arc<dyn PermissionRequestHandler> = Arc::new(DynProviderHandler { provider });
        Self::new().with_request_handler(handler)
    }

    /// 从 `PermissionPolicy` 创建权限服务
    ///
    /// 便捷构造方法,自动将旧 Policy 适配到新管线。
    pub fn from_policy(
        policy: Arc<dyn echo_core::tools::permission::PermissionPolicy>,
        provider: Arc<dyn super::HumanLoopProvider>,
    ) -> Self {
        Self::from_provider(provider).with_legacy_policy(policy)
    }

    /// 注入旧 PermissionPolicy 的语义
    ///
    /// 将旧 Policy 的工具权限规则转换为 `PermissionRule` 添加到规则注册表。
    pub fn with_legacy_policy(
        self,
        _policy: Arc<dyn echo_core::tools::permission::PermissionPolicy>,
    ) -> Self {
        // 旧 Policy 的语义通过 request_handler 桥接处理
        // 此方法保留用于未来更细粒度的规则迁移
        self
    }

    /// 设置超时策略
    pub fn with_timeout_strategy(self, strategy: TimeoutStrategy) -> Self {
        if let Ok(mut config) = self.config.try_write() {
            config.timeout_strategy = strategy;
        }
        self
    }

    /// 设置权限模式
    pub fn with_mode(self, mode: PermissionMode) -> Self {
        let mut config = self
            .config
            .try_write()
            .expect("PermissionService not yet shared");
        config.mode = mode;
        drop(config);
        self
    }

    /// 设置 Classifier
    pub fn with_classifier(mut self, classifier: Arc<dyn Classifier>) -> Self {
        self.classifier = Some(classifier);
        self
    }

    /// 设置权限请求处理器
    pub fn with_request_handler(mut self, handler: Arc<dyn PermissionRequestHandler>) -> Self {
        self.request_handler = handler;
        self
    }

    /// 是否已配置真实的权限请求处理器(非 NullHandler)
    /// 使用 trait method 标记而非 type_name 字符串匹配
    fn has_real_handler(&self) -> bool {
        !self.request_handler.is_null_handler()
    }

    /// 设置最大连续拒绝次数
    pub fn with_max_consecutive_denials(mut self, max: u32) -> Self {
        if let Ok(mut config) = self.config.try_write() {
            config.max_consecutive_denials = max;
        }
        self.denial_tracker = tokio::sync::Mutex::new(DenialTracker::with_max_consecutive(max));
        self
    }

    /// 设置受保护路径检查器
    pub fn with_protected_paths(mut self, checker: ProtectedPathChecker) -> Self {
        self.protected_paths = checker;
        self
    }

    /// 设置审计 Sink
    pub fn with_audit_sink(mut self, sink: Arc<dyn PermissionAuditSink>) -> Self {
        self.audit_sink = Some(sink);
        self
    }

    /// 添加规则
    pub async fn add_rule(&self, rule: PermissionRule) {
        let mut rules = self.rules.write().await;
        rules.add_rule(rule);
    }

    /// 批量添加规则
    pub async fn add_rules(&self, rules: Vec<PermissionRule>) {
        let mut registry = self.rules.write().await;
        registry.add_rules(rules);
    }

    /// 应用权限更新
    pub async fn apply_update(&self, update: PermissionUpdate) {
        let mut rules = self.rules.write().await;
        match update {
            PermissionUpdate::AddRule {
                matcher,
                behavior,
                source,
            } => {
                let rule = Self::parse_rule(matcher, behavior, source);
                rules.add_rule(rule);
            }
            PermissionUpdate::RemoveRule { matcher } => {
                rules.remove_by_matcher(&matcher);
            }
            PermissionUpdate::SetMode { mode } => {
                self.config.write().await.mode = mode;
            }
        }
    }

    /// 批量应用更新
    pub async fn apply_updates(&self, updates: Vec<PermissionUpdate>) {
        for update in updates {
            self.apply_update(update).await;
        }
    }

    /// 设置权限模式
    pub async fn set_mode(&self, mode: PermissionMode) {
        self.config.write().await.mode = mode;
    }

    /// 获取当前权限模式
    pub async fn mode(&self) -> PermissionMode {
        self.config.read().await.mode
    }

    /// 取出最近一次审批中用户修改的参数
    ///
    /// 返回 `Some(modified_args)` 表示用户在审批时修改了参数,
    /// 调用方应用修改后的参数执行工具。调用后自动清除。
    pub async fn take_modified_args(&self) -> Option<Value> {
        self.last_modified_args.write().await.take()
    }

    /// 撤销某工具的会话级审批缓存
    pub fn revoke_cache(&self, tool_name: &str) {
        self.cache.revoke(tool_name);
    }

    /// 清空所有审批缓存
    pub fn clear_cache(&self) {
        self.cache.clear();
    }

    /// 记录审计条目(fire-and-forget,不阻塞管线)
    #[allow(clippy::too_many_arguments)]
    fn record_audit(
        &self,
        tool_name: &str,
        tool_input: &Value,
        decision: &PermissionDecision,
        reason: &str,
        source: &str,
        pipeline_start: std::time::Instant,
        decision_duration: std::time::Duration,
    ) {
        if let Some(sink) = &self.audit_sink {
            let entry = PermissionAuditEntry::new(
                tool_name,
                tool_input,
                decision,
                reason,
                source,
                pipeline_start,
                decision_duration,
            );
            let sink = sink.clone();
            tokio::spawn(async move {
                sink.record(entry).await;
            });
        }
    }

    /// 统一权限检查入口
    pub async fn check(&self, tool_name: &str, tool_input: &Value) -> Result<PermissionDecision> {
        self.check_with_permissions(tool_name, tool_input, &[])
            .await
    }

    /// 带权限类型的检查
    ///
    /// 审批管线:
    /// 1. BypassPermissions → Allow
    /// 2. Plan 模式 → 按 permissions 过滤
    /// 3. 规则匹配 → Allow/Deny/Ask
    /// 4. 缓存检查 → 命中则 AutoApprove
    /// 5. DenialTracker → 连续拒绝则升级
    /// 6. 模式分发(Classifier / Handler)
    /// 7. 结果后处理(缓存写入、拒绝跟踪、modified args)
    pub async fn check_with_permissions(
        &self,
        tool_name: &str,
        tool_input: &Value,
        permissions: &[ToolPermission],
    ) -> Result<PermissionDecision> {
        let pipeline_start = std::time::Instant::now();
        let config = self.config.read().await;

        // 辅助闭包:审计 + 返回
        macro_rules! audit_return {
            ($decision:expr, $reason:expr, $source:expr) => {{
                let d = $decision;
                self.record_audit(
                    tool_name,
                    tool_input,
                    &d,
                    $reason,
                    $source,
                    pipeline_start,
                    pipeline_start.elapsed(),
                );
                return Ok(d);
            }};
        }

        // 1. Bypass 模式(可被管理员禁用)
        if config.mode == PermissionMode::BypassPermissions {
            if config.bypass_disabled {
                audit_return!(
                    PermissionDecision::Deny {
                        reason: "BypassPermissions 模式已被管理员禁用".to_string(),
                    },
                    "bypass_disabled",
                    "bypass_mode"
                );
            }
            audit_return!(PermissionDecision::Allow, "bypass", "bypass_mode");
        }

        // 2. Plan 模式检查
        if config.mode == PermissionMode::Plan {
            if permissions.contains(&ToolPermission::Write)
                || permissions.contains(&ToolPermission::Execute)
                || permissions.contains(&ToolPermission::Sensitive)
            {
                audit_return!(
                    PermissionDecision::Deny {
                        reason: "Plan 模式不允许写入或执行操作".to_string(),
                    },
                    "plan_mode",
                    "plan_mode"
                );
            }
            audit_return!(PermissionDecision::Allow, "plan_mode", "plan_mode");
        }

        // 3. 受保护路径检查(最高优先级,在规则匹配之前)
        // 即使规则匹配允许,受保护路径也必须被拒绝
        match self.protected_paths.check(tool_name, tool_input) {
            ProtectedPathResult::Protected {
                matched_pattern,
                path,
            } => {
                audit_return!(
                    PermissionDecision::Deny {
                        reason: format!("受保护路径 '{}'(匹配规则 '{}')", path, matched_pattern),
                    },
                    "protected_path",
                    "protected_paths"
                );
            }
            ProtectedPathResult::Safe => {}
        }

        // 4. 检查规则注册表
        {
            let rules = self.rules.read().await;
            if let Some(behavior) = rules.check(tool_name, permissions) {
                let decision = behavior.to_decision();
                audit_return!(decision, "rule_match", "rules");
            }
        }

        // 5. 缓存检查
        if self.cache.is_approved(tool_name, tool_input) {
            audit_return!(PermissionDecision::Allow, "cache_hit", "approval_cache");
        }

        // 6. DenialTracker 检查 — 连续拒绝过多则升级为人工审批
        {
            let tracker = self.denial_tracker.lock().await;
            if tracker.should_fallback() {
                audit_return!(
                    PermissionDecision::RequireApproval,
                    "denial_tracker_fallback",
                    "denial_tracker"
                );
            }
        }

        // 5.5 未配置 handler 时直接返回 RequireApproval(而非静默拒绝)
        let needs_handler = matches!(
            config.mode,
            PermissionMode::Default | PermissionMode::AcceptEdits
        );
        if needs_handler && !self.has_real_handler() {
            audit_return!(
                PermissionDecision::RequireApproval,
                "no_handler",
                "handler_check"
            );
        }

        // 6. 模式分发
        let decision = match config.mode {
            PermissionMode::Auto => self.check_with_classifier(tool_name, tool_input).await?,
            PermissionMode::Default => {
                self.check_with_handler(tool_name, tool_input, permissions)
                    .await?
            }
            PermissionMode::Plan => {
                // Plan 已在步骤 2 处理,此处不应到达
                PermissionDecision::Allow
            }
            PermissionMode::AcceptEdits => {
                if permissions.contains(&ToolPermission::Write) {
                    PermissionDecision::Allow
                } else {
                    self.check_with_handler(tool_name, tool_input, permissions)
                        .await?
                }
            }
            PermissionMode::DontAsk => {
                // 静默模式:只放行有明确 allow 规则的操作,其他静默拒绝
                // 注意:到这里规则已检查过且无匹配,直接拒绝
                PermissionDecision::Deny {
                    reason: format!(
                        "DontAsk 模式下工具 '{}' 未匹配任何允许规则,已静默拒绝",
                        tool_name
                    ),
                }
            }
            PermissionMode::Bubble => PermissionDecision::RequireApproval,
            PermissionMode::BypassPermissions => {
                // Bypass 已在步骤 1 处理,此处不应到达
                PermissionDecision::Allow
            }
        };

        // 7. 结果后处理
        match &decision {
            PermissionDecision::Allow => {
                let mut tracker = self.denial_tracker.lock().await;
                tracker.reset();
            }
            PermissionDecision::Deny { .. } => {
                let mut tracker = self.denial_tracker.lock().await;
                tracker.record_denial();
            }
            _ => {}
        }

        // 8. 审计记录(最终决策)
        let reason = match &decision {
            PermissionDecision::Allow => "allowed",
            PermissionDecision::Deny { .. } => "denied",
            PermissionDecision::RequireApproval => "require_approval",
            PermissionDecision::Ask { .. } => "ask",
        };
        self.record_audit(
            tool_name,
            tool_input,
            &decision,
            reason,
            "mode_dispatch",
            pipeline_start,
            pipeline_start.elapsed(),
        );

        Ok(decision)
    }

    /// 使用 Classifier 检查(Auto 模式)
    async fn check_with_classifier(
        &self,
        tool_name: &str,
        tool_input: &Value,
    ) -> Result<PermissionDecision> {
        if let Some(classifier) = &self.classifier {
            let context = ClassifierContext::new("agent".to_string(), "session".to_string());
            let result = classifier.classify(tool_name, tool_input, &context).await?;

            if result.should_block {
                Ok(PermissionDecision::Deny {
                    reason: result.reason,
                })
            } else {
                Ok(PermissionDecision::Allow)
            }
        } else {
            // 没有 Classifier,回退到用户确认
            Ok(PermissionDecision::RequireApproval)
        }
    }

    /// 使用 PermissionRequestHandler 检查
    async fn check_with_handler(
        &self,
        tool_name: &str,
        tool_input: &Value,
        permissions: &[ToolPermission],
    ) -> Result<PermissionDecision> {
        let risk_level = RiskLevel::from_permissions(permissions);

        let request = PermissionRequest::new(tool_name, tool_input.clone())
            .with_permissions(permissions.to_vec())
            .with_risk_level(risk_level)
            .with_risk_based_suggestions();

        let response = self.request_handler.handle(request).await?;

        // 处理用户修改的参数
        if let Some(modified) = &response.updated_input {
            *self.last_modified_args.write().await = Some(modified.clone());
        }

        // 处理审批缓存 — Approved 时写入缓存
        // 根据 rule_updates 推断缓存 scope
        if matches!(response.decision, PermissionResponseDecision::Allowed) {
            let scope = Self::infer_scope_from_updates(&response.rule_updates);
            self.cache.record_approval(tool_name, tool_input, scope);
        }

        // 处理规则更新
        if !response.rule_updates.is_empty() {
            self.apply_updates(response.rule_updates).await;
        }

        Ok(match response.decision {
            PermissionResponseDecision::Allowed => PermissionDecision::Allow,
            PermissionResponseDecision::Denied { reason } => PermissionDecision::Deny {
                reason: reason.unwrap_or_else(|| "用户拒绝".to_string()),
            },
            PermissionResponseDecision::NeedMoreInfo { question } => PermissionDecision::Ask {
                suggestions: vec![question],
            },
        })
    }

    /// 解析规则
    fn parse_rule(matcher: String, behavior: String, source: String) -> PermissionRule {
        let rule_matcher = RuleMatcher::Pattern { pattern: matcher };
        let rule_behavior = match behavior.as_str() {
            "allow" => RuleBehavior::Allow,
            "deny" => RuleBehavior::Deny {
                reason: "规则拒绝".to_string(),
            },
            "ask" => RuleBehavior::Ask {
                suggestions: vec!["允许".to_string(), "拒绝".to_string()],
            },
            _ => RuleBehavior::Allow,
        };
        let rule_source = match source.as_str() {
            "session" => RuleSource::Session,
            "cliArg" => RuleSource::CliArg,
            "userSettings" => RuleSource::UserSettings,
            "projectSettings" => RuleSource::ProjectSettings,
            "localSettings" => RuleSource::LocalSettings,
            _ => RuleSource::Default,
        };

        PermissionRule {
            matcher: rule_matcher,
            behavior: rule_behavior,
            source: rule_source,
            description: None,
        }
    }

    /// 从 rule_updates 推断审批缓存粒度
    ///
    /// - `SessionAllTools`: 存在 session 级 allow 规则
    /// - `Session`: 存在 session 级规则但非 "all tools" 语义
    /// - `Once`: 无 session 规则,仅批准本次
    fn infer_scope_from_updates(updates: &[PermissionUpdate]) -> ApprovalScope {
        for update in updates {
            if let PermissionUpdate::AddRule {
                source, behavior, ..
            } = update
                && source == "session"
            {
                // session 级 allow 规则 → SessionAllTools
                if behavior == "allow" {
                    return ApprovalScope::SessionAllTools;
                }
                // session 级 deny 规则 → Session(仅针对当前工具)
                return ApprovalScope::Session;
            }
        }
        ApprovalScope::Once
    }

    /// 清空规则
    pub async fn clear_rules(&self) {
        let mut rules = self.rules.write().await;
        rules.clear();
    }

    /// 获取所有规则
    pub async fn all_rules(&self) -> Vec<PermissionRule> {
        let rules = self.rules.read().await;
        rules.all_rules().to_vec()
    }
}

impl Default for PermissionService {
    fn default() -> Self {
        Self::new()
    }
}

// ── Null Handler ────────────────────────────────────────────────────────────────

/// 空权限请求处理器(默认占位实现)
///
/// 不应该被直接调用 — `check_with_permissions` 会检测到此 handler 并短路返回
/// `RequireApproval`,避免静默拒绝所有工具调用。
struct NullPermissionRequestHandler;

#[async_trait]
impl PermissionRequestHandler for NullPermissionRequestHandler {
    async fn handle(&self, _request: PermissionRequest) -> Result<PermissionResponse> {
        // 不应到达此路径,但作为安全保障返回拒绝
        Ok(PermissionResponse::denied(Some(
            "没有配置权限请求处理器".to_string(),
        )))
    }

    fn is_null_handler(&self) -> bool {
        true
    }
}

// ── Dyn Provider Handler (桥接 dyn HumanLoopProvider) ─────────────────────────

/// 将 `dyn HumanLoopProvider` 桥接到 `PermissionRequestHandler`
///
/// 解决 `DefaultPermissionRequestHandler<P>` 无法直接接受 `dyn HumanLoopProvider` 的问题。
struct DynProviderHandler {
    provider: Arc<dyn super::HumanLoopProvider>,
}

#[async_trait]
impl PermissionRequestHandler for DynProviderHandler {
    async fn handle(&self, request: PermissionRequest) -> Result<PermissionResponse> {
        use super::{HumanLoopRequest, HumanLoopResponse};

        let req = HumanLoopRequest::approval(&request.tool_name, request.tool_input.clone());

        match self.provider.request(req).await? {
            HumanLoopResponse::Approved => Ok(PermissionResponse::allowed()),
            HumanLoopResponse::ApprovedWithScope { scope: _ } => Ok(PermissionResponse::allowed()),
            HumanLoopResponse::ModifiedArgs { args, scope: _ } => {
                // 保留用户修改的参数,传递给调用方
                Ok(PermissionResponse {
                    decision: PermissionResponseDecision::Allowed,
                    rule_updates: Vec::new(),
                    feedback: None,
                    updated_input: Some(args),
                })
            }
            HumanLoopResponse::Rejected { reason } => Ok(PermissionResponse::denied(reason)),
            HumanLoopResponse::Text(text) => Ok(PermissionResponse::allowed().with_feedback(text)),
            HumanLoopResponse::Timeout => {
                Ok(PermissionResponse::denied(Some("请求超时".to_string())))
            }
            HumanLoopResponse::Deferred => {
                Ok(PermissionResponse::denied(Some("审批被推迟".to_string())))
            }
        }
    }
}

// ── 权限服务构建器 ──────────────────────────────────────────────────────────────

/// 权限服务构建器
#[allow(dead_code)]
pub struct PermissionServiceBuilder {
    config: PermissionServiceConfig,
    rules: RuleRegistry,
    classifier: Option<Arc<dyn Classifier>>,
    request_handler: Option<Arc<dyn PermissionRequestHandler>>,
    protected_paths: ProtectedPathChecker,
}

#[allow(dead_code)]
impl PermissionServiceBuilder {
    pub fn new() -> Self {
        Self {
            config: PermissionServiceConfig::default(),
            rules: RuleRegistry::new(),
            classifier: None,
            request_handler: None,
            protected_paths: ProtectedPathChecker::new(),
        }
    }

    pub fn mode(mut self, mode: PermissionMode) -> Self {
        self.config.mode = mode;
        self
    }

    pub fn max_consecutive_denials(mut self, max: u32) -> Self {
        self.config.max_consecutive_denials = max;
        self
    }

    pub fn enable_classifier(mut self, enable: bool) -> Self {
        self.config.enable_classifier = enable;
        self
    }

    pub fn timeout_strategy(mut self, strategy: TimeoutStrategy) -> Self {
        self.config.timeout_strategy = strategy;
        self
    }

    pub fn rule(mut self, rule: PermissionRule) -> Self {
        self.rules.add_rule(rule);
        self
    }

    pub fn classifier(mut self, classifier: Arc<dyn Classifier>) -> Self {
        self.classifier = Some(classifier);
        self
    }

    pub fn request_handler(mut self, handler: Arc<dyn PermissionRequestHandler>) -> Self {
        self.request_handler = Some(handler);
        self
    }

    pub fn protected_paths(mut self, checker: ProtectedPathChecker) -> Self {
        self.protected_paths = checker;
        self
    }

    pub fn build(self) -> PermissionService {
        let max_denials = self.config.max_consecutive_denials;
        PermissionService {
            config: RwLock::new(self.config),
            rules: RwLock::new(self.rules),
            cache: SessionApprovalCache::new(),
            denial_tracker: tokio::sync::Mutex::new(DenialTracker::with_max_consecutive(
                max_denials,
            )),
            classifier: self.classifier,
            request_handler: self
                .request_handler
                .unwrap_or(Arc::new(NullPermissionRequestHandler)),
            protected_paths: self.protected_paths,
            audit_sink: None,
            last_modified_args: RwLock::new(None),
        }
    }
}

impl Default for PermissionServiceBuilder {
    fn default() -> Self {
        Self::new()
    }
}

// ── 单元测试 ────────────────────────────────────────────────────────────────────

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_permission_service_new() {
        let service = PermissionService::new();
        assert_eq!(service.mode().await, PermissionMode::Default);
    }

    #[tokio::test]
    async fn test_permission_service_bypass() {
        let service = PermissionService::new();
        service.set_mode(PermissionMode::BypassPermissions).await;

        let decision = service.check("Bash", &serde_json::json!({})).await.unwrap();
        assert!(decision.is_allowed());
    }

    #[tokio::test]
    async fn test_permission_service_plan_mode() {
        let service = PermissionService::new();
        service.set_mode(PermissionMode::Plan).await;

        // 读操作应该允许
        let decision = service
            .check_with_permissions("Read", &serde_json::json!({}), &[ToolPermission::Read])
            .await
            .unwrap();
        assert!(decision.is_allowed());

        // 写操作应该拒绝
        let decision = service
            .check_with_permissions("Write", &serde_json::json!({}), &[ToolPermission::Write])
            .await
            .unwrap();
        assert!(decision.is_denied());
    }

    #[tokio::test]
    async fn test_permission_service_add_rule() {
        let service = PermissionService::new();

        service
            .add_rule(PermissionRule::allow(
                RuleMatcher::Pattern {
                    pattern: "Read".to_string(),
                },
                RuleSource::UserSettings,
            ))
            .await;

        let rules = service.all_rules().await;
        assert_eq!(rules.len(), 1);
    }

    #[tokio::test]
    async fn test_permission_service_apply_update() {
        let service = PermissionService::new();

        let update = PermissionUpdate::add_session_rule("Read".to_string());
        service.apply_update(update).await;

        let rules = service.all_rules().await;
        assert_eq!(rules.len(), 1);
    }

    #[tokio::test]
    async fn test_permission_service_builder() {
        let service = PermissionServiceBuilder::new()
            .mode(PermissionMode::Auto)
            .max_consecutive_denials(5)
            .build();

        assert_eq!(service.mode().await, PermissionMode::Auto);
    }

    #[tokio::test]
    async fn test_permission_service_default_handler() {
        let service = PermissionService::new();

        // 没有配置 handler,应该返回 RequireApproval(而非静默拒绝)
        let decision = service
            .check_with_permissions("Bash", &serde_json::json!({}), &[ToolPermission::Execute])
            .await
            .unwrap();

        assert!(matches!(decision, PermissionDecision::RequireApproval));
    }

    #[test]
    fn test_parse_rule() {
        let rule = PermissionService::parse_rule(
            "Bash".to_string(),
            "allow".to_string(),
            "session".to_string(),
        );

        assert!(matches!(rule.behavior, RuleBehavior::Allow));
        assert!(matches!(rule.source, RuleSource::Session));
    }
}