vtcode_core/core/agent/
session_config.rs1use std::path::Path;
2
3use anyhow::Result;
4use hashbrown::HashMap;
5
6use crate::config::VTCodeConfig;
7use crate::config::loader::ConfigManager;
8use crate::config::loader::layers::ConfigLayerMetadata;
9use crate::core::agent::features::FeatureSet;
10
11#[derive(Debug, Clone)]
17pub struct ResolvedSessionConfig {
18 effective: VTCodeConfig,
19 features: FeatureSet,
20 origins: HashMap<String, ConfigLayerMetadata>,
21}
22
23impl ResolvedSessionConfig {
24 pub fn from_manager(manager: &ConfigManager) -> Self {
26 let (_, origins) = manager.layer_stack().effective_config_with_origins();
27 let effective = manager.config().clone();
28 Self {
29 features: FeatureSet::from_config(Some(&effective)),
30 effective,
31 origins,
32 }
33 }
34
35 pub fn from_config(config: VTCodeConfig) -> Self {
37 let features = FeatureSet::from_config(Some(&config));
38 Self {
39 effective: config,
40 features,
41 origins: HashMap::new(),
42 }
43 }
44
45 pub fn load_from_workspace(workspace: impl AsRef<Path>) -> Result<Self> {
47 let manager = ConfigManager::load_from_workspace(workspace)?;
48 Ok(Self::from_manager(&manager))
49 }
50
51 pub fn effective(&self) -> &VTCodeConfig {
53 &self.effective
54 }
55
56 pub fn origins(&self) -> &HashMap<String, ConfigLayerMetadata> {
58 &self.origins
59 }
60
61 pub fn features(&self) -> &FeatureSet {
63 &self.features
64 }
65
66 pub fn origin_for(&self, path: &str) -> Option<&ConfigLayerMetadata> {
68 self.origins.get(path)
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::ResolvedSessionConfig;
75 use crate::config::loader::ConfigManager;
76 use anyhow::Result;
77 use std::fs;
78 use tempfile::TempDir;
79
80 #[test]
81 fn captures_origin_metadata_from_workspace_config() -> Result<()> {
82 let temp = TempDir::new()?;
83 fs::write(
84 temp.path().join("vtcode.toml"),
85 "[agent]\nprovider = \"openai\"\n\n[workspace]\nuse_root_config = true\n",
86 )?;
87
88 let snapshot = ResolvedSessionConfig::load_from_workspace(temp.path())?;
89
90 assert_eq!(snapshot.effective().agent.provider, "openai");
91 assert!(snapshot.origin_for("agent.provider").is_some(), "expected origin metadata for agent.provider");
92
93 Ok(())
94 }
95
96 #[test]
97 fn snapshot_is_immutable_after_disk_changes() -> Result<()> {
98 let temp = TempDir::new()?;
99 let config_path = temp.path().join("vtcode.toml");
100 fs::write(&config_path, "[agent]\nprovider = \"openai\"\n\n[workspace]\nuse_root_config = true\n")?;
101
102 let snapshot = ResolvedSessionConfig::load_from_workspace(temp.path())?;
103 fs::write(&config_path, "[agent]\nprovider = \"anthropic\"\n\n[workspace]\nuse_root_config = true\n")?;
104
105 assert_eq!(snapshot.effective().agent.provider, "openai");
106
107 ConfigManager::invalidate_workspace_cache(temp.path());
108 let refreshed = ResolvedSessionConfig::load_from_workspace(temp.path())?;
109 assert_eq!(refreshed.effective().agent.provider, "anthropic");
110
111 Ok(())
112 }
113}