Files
galaxy/app/src/ai/local_harness_setup.rs
T

99 lines
3.4 KiB
Rust

use warp_cli::agent::Harness;
use crate::features::FeatureFlag;
#[cfg(not(target_family = "wasm"))]
use crate::util::path::resolve_executable;
/// Tooltip shown when a local harness is product-enabled but its CLI is missing.
pub(crate) const LOCAL_HARNESS_INSTALLATION_REQUIRED_TOOLTIP: &str =
"Install Claude Code to use this local harness.";
pub(crate) const LOCAL_CODEX_HARNESS_INSTALLATION_REQUIRED_TOOLTIP: &str =
"Install Codex to use this local harness.";
pub(crate) const LOCAL_CODEX_HARNESS_DISABLED_MESSAGE: &str =
"Local Codex child agents are temporarily disabled.";
/// Client-side readiness for using a harness in local orchestration.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum LocalHarnessSetupState {
/// The harness is product-enabled and its required local CLI is installed.
Ready,
/// The harness is intentionally unavailable in the product.
ProductDisabled { message: &'static str },
/// The harness is product-enabled but the required local CLI is missing.
MissingHarness { tooltip: &'static str },
}
impl LocalHarnessSetupState {
/// Returns whether the harness can be selected in local orchestration controls.
pub(crate) fn is_selectable(self) -> bool {
matches!(self, Self::Ready)
}
}
/// Returns the product-level disabled reason for a local harness.
pub(crate) fn local_harness_product_disabled_message(harness: Harness) -> Option<&'static str> {
match harness {
Harness::Codex if !local_codex_harness_is_enabled() => {
Some(LOCAL_CODEX_HARNESS_DISABLED_MESSAGE)
}
Harness::Oz | Harness::Claude | Harness::OpenCode | Harness::Gemini | Harness::Unknown => {
None
}
Harness::Codex => None,
}
}
fn local_codex_harness_is_enabled() -> bool {
FeatureFlag::LocalClaudeCodexChildHarnesses.is_enabled()
}
/// Returns whether a local harness is exposed by product policy.
pub(crate) fn local_harness_is_product_enabled(harness: Harness) -> bool {
local_harness_product_disabled_message(harness).is_none()
}
/// Returns the current local setup state for a harness.
pub(crate) fn local_harness_setup_state(harness: Harness) -> LocalHarnessSetupState {
local_harness_setup_state_with_cli_resolver(harness, local_cli_is_installed)
}
fn local_harness_setup_state_with_cli_resolver(
harness: Harness,
cli_is_installed: impl Fn(&str) -> bool,
) -> LocalHarnessSetupState {
if let Some(message) = local_harness_product_disabled_message(harness) {
return LocalHarnessSetupState::ProductDisabled { message };
}
match harness {
Harness::Claude if !cli_is_installed("claude") => LocalHarnessSetupState::MissingHarness {
tooltip: LOCAL_HARNESS_INSTALLATION_REQUIRED_TOOLTIP,
},
Harness::Codex if !cli_is_installed("codex") => LocalHarnessSetupState::MissingHarness {
tooltip: LOCAL_CODEX_HARNESS_INSTALLATION_REQUIRED_TOOLTIP,
},
Harness::Oz
| Harness::Claude
| Harness::OpenCode
| Harness::Gemini
| Harness::Codex
| Harness::Unknown => LocalHarnessSetupState::Ready,
}
}
fn local_cli_is_installed(command: &str) -> bool {
#[cfg(not(target_family = "wasm"))]
{
resolve_executable(command).is_some()
}
#[cfg(target_family = "wasm")]
{
let _ = command;
false
}
}
#[cfg(test)]
#[path = "local_harness_setup_tests.rs"]
mod tests;