Add local status line provider configuration (#61211)

## What changed

Add `tui.status_line_provider` to select `"default"` or an MCP provider identified by `server` and `tool`. Replace provider identities as a unit across configuration layers so partial overrides cannot inherit half an identity.

Ignore this setting from project configuration and packaged defaults when computing effective values and origins, while retaining strict validation. Preserve relative path resolution when an ignored provider value is malformed.

Carry the selection into TUI local settings and refresh it on configuration reload. Use the client's configuration home when rebuilding settings for remote sessions.

## Testing

Add coverage for provider precedence, incomplete profile overrides, malformed values and diagnostics, relative path resolution, and local preference preservation when resuming remote sessions.

GitOrigin-RevId: 0777d754d46cf009569ff0c90fd7426f8b7650bf
This commit is contained in:
Joe Savona
2026-11-13 00:28:10 +00:00
committed by copyberry
parent f562a7b3e2
commit 8cdabf23ae
21 changed files with 490 additions and 43 deletions
+5 -1
View File
@@ -1653,7 +1653,11 @@ pub fn resolve_relative_paths_in_config_toml(
}
// The typed round trip resolves AbsolutePathBuf fields against this layer's directory.
let _guard = AbsolutePathBufGuard::new(base_dir);
let Ok(resolved) = ConfigToml::try_from(value_from_config_toml.clone()) else {
// Provider identities contain no paths. An ignored malformed identity must not
// prevent an unrelated path in this layer from being resolved against its directory.
let path_input =
crate::config_layer_source::without_status_line_provider(&value_from_config_toml);
let Ok(resolved) = ConfigToml::try_from(path_input.into_owned()) else {
return Ok(value_from_config_toml);
};
drop(_guard);
+61
View File
@@ -564,6 +564,67 @@ impl ExecutorFileSystem for TestFileSystem {
}
}
#[tokio::test]
async fn project_diagnostic_uses_the_current_file_and_ignores_its_provider() -> anyhow::Result<()> {
let dir = tempdir()?;
let path = dir.path().join(CONFIG_TOML_FILE);
let layer = ConfigLayerEntry::new(
ConfigLayerSource::Project {
dot_codex_folder: AbsolutePathBuf::from_absolute_path(dir.path())?,
},
TomlValue::Table(Default::default()),
);
std::fs::write(
&path,
"[tui]\nstatus_line_provider = 42\nvim_mode_default = 43\n",
)?;
let error = first_layer_config_error_from_entries(&[layer])
.await
.expect("invalid vim setting");
assert_eq!((error.path, error.range.start.line), (path, 3));
assert!(error.message.contains("43"), "{}", error.message);
Ok(())
}
#[tokio::test]
async fn packaged_status_line_provider_is_ignored_but_strictly_validated() -> anyhow::Result<()> {
let dir = tempdir()?;
let path = AbsolutePathBuf::resolve_path_against_base("defaults.toml", dir.path());
std::fs::write(&path, "[tui]\nstatus_line_provider = 42\n")?;
let load = |strict_config| {
load_config_layers_state(
&TestFileSystem,
dir.path(),
/*cwd*/ None,
&[],
ConfigLoadOptions {
loader_overrides: LoaderOverrides {
packaged_defaults_path: Some(path.clone()),
..LoaderOverrides::without_managed_config_for_tests()
},
strict_config,
..Default::default()
},
&crate::NoopThreadConfigLoader,
)
};
let stack = load(/*strict_config*/ false).await?;
assert_eq!(stack.effective_config().get("tui"), None);
let error = load(/*strict_config*/ true)
.await
.expect_err("malformed packaged provider");
let diagnostic = error
.get_ref()
.and_then(|error| error.downcast_ref::<crate::ConfigLoadError>())
.expect("packaged config diagnostic")
.config_error();
assert_eq!(
(diagnostic.path.as_path(), diagnostic.range.start.line),
(path.as_path(), 2)
);
Ok(())
}
#[tokio::test]
async fn packaged_defaults_have_lower_precedence_than_existing_config_layers() {
let tmp = tempdir().expect("tempdir");