feat: finalise multi-profil Codex/Claude avec catalogue de modèles

- Backend : clone_profile_from_seed généralisé (non OpenCode)
- Backend : catalogue static Claude/Codex (3 modèles chacun, 1 recommandé)
- Backend : commandes Tauri list_claude_models/list_codex_models
- Frontend : ProfilesSettings refonte en onglets Codex/Claude + create/duplicate/edit/delete
- Frontend : ModelSelect searchable partagé + fallback saisie manuelle
- Frontend : assignation agent nom · modèle
- Tests QA : 4 profils modèles distincts (2 Claude, 2 Codex) assignés à agents
This commit is contained in:
2026-07-26 14:56:26 +02:00
parent c807a70fea
commit ea7ea71230
21 changed files with 1298 additions and 109 deletions

View File

@ -1047,6 +1047,57 @@ impl From<CloneOpenCodeProfileFromSeedOutput> for ProfileDto {
}
}
impl From<application::CloneProfileFromSeedOutput> for ProfileDto {
fn from(out: application::CloneProfileFromSeedOutput) -> Self {
Self(out.profile)
}
}
/// One entry of a curated structured-profile model catalogue.
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ProfileModelCatalogEntryDto {
/// Structured adapter this model belongs to.
pub adapter: domain::profile::StructuredAdapter,
/// Exact model identifier to persist on `AgentProfile.model`.
pub model_id: String,
/// Human-readable label for picker display.
pub display_name: String,
/// Extra search tokens useful to the frontend.
pub aliases: Vec<String>,
/// Whether this entry is the conservative default suggestion.
pub recommended: bool,
}
impl From<application::ProfileModelCatalogEntry> for ProfileModelCatalogEntryDto {
fn from(entry: application::ProfileModelCatalogEntry) -> Self {
Self {
adapter: entry.adapter,
model_id: entry.model_id,
display_name: entry.display_name,
aliases: entry.aliases,
recommended: entry.recommended,
}
}
}
/// A list of curated structured-profile models.
#[derive(Debug, Clone, Serialize)]
#[serde(transparent)]
pub struct ProfileModelCatalogDto(pub Vec<ProfileModelCatalogEntryDto>);
impl From<application::ListClaudeModelsOutput> for ProfileModelCatalogDto {
fn from(out: application::ListClaudeModelsOutput) -> Self {
Self(out.models.into_iter().map(Into::into).collect())
}
}
impl From<application::ListCodexModelsOutput> for ProfileModelCatalogDto {
fn from(out: application::ListCodexModelsOutput) -> Self {
Self(out.models.into_iter().map(Into::into).collect())
}
}
/// One entry of the static OpenCode cloud-provider catalogue (ticket #92, lot B3).
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
@ -1202,6 +1253,30 @@ impl From<CloneOpenCodeProfileFromSeedRequestDto> for CloneOpenCodeProfileFromSe
}
}
/// Request DTO for `clone_profile_from_seed`.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CloneProfileFromSeedRequestDto {
/// Id of the persisted or reference profile to clone.
pub seed_profile_id: domain::ids::ProfileId,
/// Optional display name for the new profile.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
/// Optional model override. When omitted, the seed model is copied.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
}
impl From<CloneProfileFromSeedRequestDto> for application::CloneProfileFromSeedInput {
fn from(dto: CloneProfileFromSeedRequestDto) -> Self {
Self {
seed_profile_id: dto.seed_profile_id,
name: dto.name,
model: dto.model,
}
}
}
/// Request DTO for `configure_profiles` (closes the first run).
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]

View File

@ -15,24 +15,24 @@ use application::{
AgentResumer, AgentWakeService, AppError, AssignIssueAgent, AssignSkillToAgent,
AssignTicketToSprint, AttachLiveAgent, AuthenticateSession, BackgroundCommandArchive,
CancelBackgroundTask, ChangeAgentProfile, CheckEmbedderSuggestion,
CloneOpenCodeProfileFromSeed, CloseProject, CloseTab, CloseTerminal, CloseTicketAssistant,
ConfigureProfiles, ContextGuardUseCases, CreateAgentFromScratch, CreateAgentFromTemplate,
CreateIssue, CreateLayout, CreateMemory, CreateProject, CreateSkill, CreateSprint,
CreateTemplate, DeleteAgent, DeleteEmbedderProfile, DeleteIssue, DeleteLayout, DeleteMemory,
DeleteModelServer, DeleteProfile, DeleteSkill, DeleteSprint, DeleteTemplate,
CloneOpenCodeProfileFromSeed, CloneProfileFromSeed, CloseProject, CloseTab, CloseTerminal,
CloseTicketAssistant, ConfigureProfiles, ContextGuardUseCases, CreateAgentFromScratch,
CreateAgentFromTemplate, CreateIssue, CreateLayout, CreateMemory, CreateProject, CreateSkill,
CreateSprint, CreateTemplate, DeleteAgent, DeleteEmbedderProfile, DeleteIssue, DeleteLayout,
DeleteMemory, DeleteModelServer, DeleteProfile, DeleteSkill, DeleteSprint, DeleteTemplate,
DescribeEmbedderEngines, DetectAgentDrift, DetectProfiles, DismissEmbedderSuggestion,
EnsureLocalModelServer, FirstRunState, GetAppExitWorkGuardState, GetLiveStateLean, GetMemory,
GetProjectPermissions, GetProjectSystemPermissions, GetProjectWorkState, GitBranches,
GitCheckout, GitCommit, GitGraph, GitInit, GitLog, GitStage, GitStatus, GitUnstage,
HarvestMemoryFromTurn, HealthUseCase, InspectConversation, InstallPluginFromArchive,
InstallPluginFromDirectory, JsonPluginManifestValidator, LaunchAgent, LaunchAgentInput,
LinkIssues, ListAgents, ListAgentsInput, ListDevices, ListEmbedderProfiles, ListIssues,
ListLayouts, ListMemories, ListModelServers, ListOpenCodeProviders,
ListPluginRuntimeContributions, ListPlugins, ListProfiles, ListProjects, ListResumableAgents,
ListSkills, ListSprints, ListTemplates, LiveAgentRegistry, LiveSessions, LiveStateLeanProvider,
LiveStateProvider, LiveStateReadProvider, LoadLayout, McpRuntime, McpToolPermissionCatalogue,
MoveTabToNewWindow, MutateLayout, OnnxModelView, OpenProject, OpenTerminal,
OpenTicketAssistant, OrchestratorService, PairAttemptLimiter, PairDevice,
LinkIssues, ListAgents, ListAgentsInput, ListClaudeModels, ListCodexModels, ListDevices,
ListEmbedderProfiles, ListIssues, ListLayouts, ListMemories, ListModelServers,
ListOpenCodeProviders, ListPluginRuntimeContributions, ListPlugins, ListProfiles, ListProjects,
ListResumableAgents, ListSkills, ListSprints, ListTemplates, LiveAgentRegistry, LiveSessions,
LiveStateLeanProvider, LiveStateProvider, LiveStateReadProvider, LoadLayout, McpRuntime,
McpToolPermissionCatalogue, MoveTabToNewWindow, MutateLayout, OnnxModelView, OpenProject,
OpenTerminal, OpenTicketAssistant, OrchestratorService, PairAttemptLimiter, PairDevice,
PermissionProjectorRegistry, ProposeContext, ReadAgentContext, ReadContext,
ReadConversationPage, ReadIssue, ReadIssueCarnet, ReadMcpToolPermissions, ReadMemory,
ReadMemoryIndex, ReadProjectContext, ReadSkill, ReadTemplate, RecallMemory, ReconcileLayouts,
@ -940,6 +940,12 @@ pub struct BackendCore {
pub save_opencode_provider_profile: Arc<SaveOpenCodeProviderProfile>,
/// Static catalogue of OpenCode cloud providers (ticket #92, lot B3).
pub list_opencode_providers: Arc<ListOpenCodeProviders>,
/// Static curated Claude model catalogue.
pub list_claude_models: Arc<ListClaudeModels>,
/// Static curated Codex model catalogue.
pub list_codex_models: Arc<ListCodexModels>,
/// Create a new profile instance from a persisted/reference seed.
pub clone_profile_from_seed: Arc<CloneProfileFromSeed>,
/// Create a new OpenCode profile instance from the canonical seed.
pub clone_opencode_profile_from_seed: Arc<CloneOpenCodeProfileFromSeed>,
/// Delete a profile.
@ -1468,6 +1474,12 @@ impl BackendCore {
Arc::clone(&ids) as Arc<dyn IdGenerator>,
));
let list_opencode_providers = Arc::new(ListOpenCodeProviders::new());
let list_claude_models = Arc::new(ListClaudeModels::new());
let list_codex_models = Arc::new(ListCodexModels::new());
let clone_profile_from_seed = Arc::new(CloneProfileFromSeed::new(
Arc::clone(&profile_store_port),
Arc::clone(&ids) as Arc<dyn IdGenerator>,
));
let clone_opencode_profile_from_seed = Arc::new(CloneOpenCodeProfileFromSeed::new(
Arc::clone(&profile_store_port),
Arc::clone(&ids) as Arc<dyn IdGenerator>,
@ -2661,6 +2673,9 @@ impl BackendCore {
save_profile,
save_opencode_provider_profile,
list_opencode_providers,
list_claude_models,
list_codex_models,
clone_profile_from_seed,
clone_opencode_profile_from_seed,
delete_profile,
configure_profiles,