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
//! Overseer construction helpers for daemon startup.
//!
//! Why: the overseer strategy (deterministic vs. composite-LLM) is resolved
//! once at startup from the installed framework policy; isolating the resolution
//! logic keeps `DaemonState::new` and `DaemonState::with_paths` tidy.
//! What: [`OverseerBuild`] bundles the composed `dyn Overseer`, its handler
//! name, and the optional standalone LLM chat handler; [`build_overseer`]
//! assembles it from a loaded [`OverseerConfig`]; helper fns load optimizer
//! and overseer configs from the framework root.
//! Test: `overseer_is_deterministic_without_llm`, `overseer_falls_back_when_llm_key_missing`.
use PathBuf;
use Arc;
use crateDeterministicOverseer;
use crateOverseer;
use crateOverseerConfig;
use crateFrameworkPaths;
use crateAuditLogger;
use crateOptimizerConfig;
/// The overseer strategy plus the optional standalone LLM chat handler.
///
/// Why: [`build_overseer`] resolves both the `dyn Overseer` used for hook
/// oversight and — when an OpenRouter key is present — a concrete
/// [`LlmOverseer`] reused for the `POST /llm/chat` endpoint; returning them as
/// one named struct keeps both daemon constructors aligned.
/// What: the composed overseer, its handler name, and `Some(LlmOverseer)` when
/// LLM chat is available.
/// Test: `overseer_is_deterministic_without_llm`.
pub
/// Read the optimizer policy from the installed framework, never failing.
///
/// Why: daemon startup must not abort because the framework is not installed
/// or its policy file is malformed; a sensible default keeps the daemon usable.
/// What: loads `~/.trusty-mpm/framework/hooks/optimizer.toml` via
/// [`OptimizerConfig::load_from_file`], logging and falling back to
/// `OptimizerConfig::default()` on any error.
/// Test: `new_reads_default_when_optimizer_file_missing`,
/// `reload_optimizer_config_picks_up_file_changes`.
pub
/// Build the session overseer from the installed framework policy.
///
/// Why: oversight is framework-managed and opt-in; daemon startup must reflect
/// `~/.trusty-mpm/framework/hooks/overseer.toml` (or a safe disabled default
/// when it is absent) without ever failing to construct.
/// What: loads [`OverseerConfig`] from [`FrameworkPaths::overseer_config`] and
/// builds the overseer via [`build_overseer`]; a missing/malformed file yields
/// the disabled default config (handled inside `OverseerConfig::load_from`).
/// Test: `new_overseer_is_disabled_when_file_missing`.
pub
/// Assemble the overseer strategy from a loaded [`OverseerConfig`].
///
/// Why: the daemon may run rule-based oversight alone, or compose it with the
/// LLM overseer when `[llm] enabled = true` *and* an API key is present.
/// Deciding the strategy in one place keeps `new()` / `with_paths()` aligned.
/// What: always builds a [`DeterministicOverseer`]; when the LLM section is
/// enabled and the configured API key resolves, wraps both in a
/// [`CompositeOverseer`] (deterministic first, LLM for uncertain cases).
/// Returns the overseer, its handler name, and the standalone LLM chat handler.
/// Test: `overseer_is_deterministic_without_llm`,
/// `overseer_falls_back_when_llm_key_missing`.
pub
/// Resolve the daemon's logs directory (`~/.trusty-mpm/logs`).
///
/// Why: the audit logger writes under a single well-known directory; resolving
/// it via the home directory keeps it consistent with the framework root.
/// What: returns `<home>/.trusty-mpm/logs`, falling back to `./.trusty-mpm/logs`
/// when the home directory cannot be determined.
/// Test: exercised indirectly by `new_builds_audit_logger`.
pub
/// Build an [`AuditLogger`] under `root/logs`.
///
/// Why: both `DaemonState::new` and `DaemonState::with_paths` need an audit
/// logger; centralising the construction avoids duplication. Delegates to
/// [`logs_dir`] for directory resolution so the two stay in sync.
/// What: returns `Arc<AuditLogger>` rooted at `root.join("logs")`.
/// Test: `audit_logger_is_accessible`.
pub