diff --git a/crates/executors/Cargo.toml b/crates/executors/Cargo.toml index 63291ec9..fc58a90d 100644 --- a/crates/executors/Cargo.toml +++ b/crates/executors/Cargo.toml @@ -8,7 +8,7 @@ utils = { path = "../utils" } tokio = { workspace = true } tokio-util = { version = "0.7", features = ["io"] } bytes = "1.0" -serde = { workspace = true } +serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } tracing = { workspace = true } toml = "0.8" diff --git a/crates/executors/src/profile.rs b/crates/executors/src/profile.rs index 9eadd9d0..6d6da80b 100644 --- a/crates/executors/src/profile.rs +++ b/crates/executors/src/profile.rs @@ -1,8 +1,8 @@ -use std::{collections::HashMap, fs, sync::RwLock}; +use std::{collections::HashMap, fs, str::FromStr, sync::RwLock}; use convert_case::{Case, Casing}; use lazy_static::lazy_static; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, de::Error as DeError}; use thiserror::Error; use ts_rs::TS; @@ -54,7 +54,7 @@ const DEFAULT_PROFILES_JSON: &str = include_str!("../default_profiles.json"); #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, TS, Hash, Eq)] pub struct ExecutorProfileId { /// The executor type (e.g., "CLAUDE_CODE", "AMP") - #[serde(alias = "profile")] + #[serde(alias = "profile", deserialize_with = "de_base_coding_agent_kebab")] // Backwards compatability with ProfileVariantIds, esp stored in DB under ExecutorAction pub executor: BaseCodingAgent, /// Optional variant name (e.g., "PLAN", "ROUTER") @@ -62,6 +62,18 @@ pub struct ExecutorProfileId { pub variant: Option, } +// Convert legacy profile/executor names from kebab-case to SCREAMING_SNAKE_CASE, can be deleted 14 days from 3/9/25 +fn de_base_coding_agent_kebab<'de, D>(de: D) -> Result +where + D: Deserializer<'de>, +{ + let raw = String::deserialize(de)?; + // kebab-case -> SCREAMING_SNAKE_CASE + let norm = raw.replace('-', "_").to_ascii_uppercase(); + BaseCodingAgent::from_str(&norm) + .map_err(|_| D::Error::custom(format!("unknown executor '{raw}' (normalized to '{norm}')"))) +} + impl ExecutorProfileId { /// Create a new executor profile ID with default variant pub fn new(executor: BaseCodingAgent) -> Self {