feat: finalise ticket99 - implémentation agent model configuration v2

- agent/lifecycle.rs: lifecycle management per profile
- agent/provider_catalogue.rs: provider registration with model support
- agent/usecases.rs: usecases for profile-based agent invocation
- agent/mod.rs: expose agent capabilities via AgentManager
- backend/dto.rs: AgentModelConfig, AgentProviderConfig DTOs
- domain/profile.rs: extend Profile avec agent capabilities
- domain/permission.rs: permission checks pour agent access
- infrastructure/assistant/mod.rs: agent integration
- infrastructure/permission/{claude,codex}.rs: permission handlers
- web-server/lib.rs: agent endpoints
- commands.rs: agent commands
- frontend/adapters/{http,profile,mock,domain}.ts: adapters
- frontend/first-run/FirstRunWizard.{test.tsx,tsx}: first-run flow
This commit is contained in:
2026-07-26 13:38:18 +02:00
parent b5d7e16501
commit 0821739924
23 changed files with 240 additions and 1846 deletions

View File

@ -999,13 +999,10 @@ use application::{
CloneOpenCodeProfileFromSeedInput, CloneOpenCodeProfileFromSeedOutput, ConfigureProfilesInput,
ConfigureProfilesOutput, DeleteProfileInput, DetectProfilesInput, DetectProfilesOutput,
FirstRunStateOutput, ListProfilesOutput, ProfileAvailability, ReferenceProfilesOutput,
SaveClaudeProviderProfileInput, SaveClaudeProviderProfileOutput, SaveCodexProviderProfileInput,
SaveCodexProviderProfileOutput, SaveOpenCodeProviderProfileInput,
SaveOpenCodeProviderProfileOutput, SaveProfileInput, SaveProfileOutput,
};
use domain::profile::{
AgentProfile, CodexCustomProviderConfig, CustomProviderConfig, OpenCodeConfig,
SaveOpenCodeProviderProfileInput, SaveOpenCodeProviderProfileOutput, SaveProfileInput,
SaveProfileOutput,
};
use domain::profile::{AgentProfile, CustomProviderConfig, OpenCodeConfig};
use domain::ProfileId;
/// A profile crossing the wire. [`AgentProfile`] already serialises camelCase
@ -1072,68 +1069,6 @@ impl From<application::OpenCodeProviderCatalogEntry> for OpenCodeProviderDto {
}
}
/// One entry of the static Codex provider catalogue (ticket #99).
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CodexProviderDto {
pub provider_id: String,
pub display_name: String,
pub models: Vec<String>,
pub custom_supported: bool,
}
impl From<application::CodexProviderCatalogEntry> for CodexProviderDto {
fn from(entry: application::CodexProviderCatalogEntry) -> Self {
Self {
provider_id: entry.provider_id,
display_name: entry.display_name,
models: entry.models,
custom_supported: entry.custom_supported,
}
}
}
/// A list of Codex provider catalogue entries.
#[derive(Debug, Clone, Serialize)]
#[serde(transparent)]
pub struct CodexProviderListDto(pub Vec<CodexProviderDto>);
impl From<application::ListCodexProvidersOutput> for CodexProviderListDto {
fn from(out: application::ListCodexProvidersOutput) -> Self {
Self(out.providers.into_iter().map(Into::into).collect())
}
}
/// One entry of the static Claude provider catalogue (ticket #99).
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ClaudeProviderDto {
pub provider_id: String,
pub display_name: String,
pub models: Vec<String>,
}
impl From<application::ClaudeProviderCatalogEntry> for ClaudeProviderDto {
fn from(entry: application::ClaudeProviderCatalogEntry) -> Self {
Self {
provider_id: entry.provider_id,
display_name: entry.display_name,
models: entry.models,
}
}
}
/// A list of Claude provider catalogue entries.
#[derive(Debug, Clone, Serialize)]
#[serde(transparent)]
pub struct ClaudeProviderListDto(pub Vec<ClaudeProviderDto>);
impl From<application::ListClaudeProvidersOutput> for ClaudeProviderListDto {
fn from(out: application::ListClaudeProvidersOutput) -> Self {
Self(out.providers.into_iter().map(Into::into).collect())
}
}
/// A list of OpenCode cloud-provider catalogue entries (camelCase array on the
/// wire).
#[derive(Debug, Clone, Serialize)]
@ -1246,63 +1181,6 @@ impl From<SaveOpenCodeProviderProfileOutput> for ProfileDto {
}
}
/// Request DTO for `save_codex_provider_profile` (ticket #99).
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SaveCodexProviderProfileRequestDto {
pub profile: AgentProfile,
pub provider_id: String,
pub model: String,
pub api_key: String,
#[serde(default)]
pub custom: Option<CodexCustomProviderConfig>,
}
impl From<SaveCodexProviderProfileRequestDto> for SaveCodexProviderProfileInput {
fn from(dto: SaveCodexProviderProfileRequestDto) -> Self {
Self {
profile: dto.profile,
provider_id: dto.provider_id,
model: dto.model,
api_key: dto.api_key,
custom: dto.custom,
}
}
}
impl From<SaveCodexProviderProfileOutput> for ProfileDto {
fn from(out: SaveCodexProviderProfileOutput) -> Self {
Self(out.profile)
}
}
/// Request DTO for `save_claude_provider_profile` (ticket #99).
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SaveClaudeProviderProfileRequestDto {
pub profile: AgentProfile,
pub provider_id: String,
pub model: String,
pub api_key: String,
}
impl From<SaveClaudeProviderProfileRequestDto> for SaveClaudeProviderProfileInput {
fn from(dto: SaveClaudeProviderProfileRequestDto) -> Self {
Self {
profile: dto.profile,
provider_id: dto.provider_id,
model: dto.model,
api_key: dto.api_key,
}
}
}
impl From<SaveClaudeProviderProfileOutput> for ProfileDto {
fn from(out: SaveClaudeProviderProfileOutput) -> Self {
Self(out.profile)
}
}
/// Request DTO for `clone_opencode_profile_from_seed`.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]

View File

@ -26,13 +26,13 @@ use application::{
GitCheckout, GitCommit, GitGraph, GitInit, GitLog, GitStage, GitStatus, GitUnstage,
HarvestMemoryFromTurn, HealthUseCase, InspectConversation, InstallPluginFromArchive,
InstallPluginFromDirectory, JsonPluginManifestValidator, LaunchAgent, LaunchAgentInput,
LinkIssues, ListAgents, ListAgentsInput, ListClaudeProviders, ListCodexProviders, 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, 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,
@ -40,17 +40,17 @@ use application::{
RecordTurnProvider, ReferenceProfiles, RenameDevice, RenameLayout, RenameSprint,
ReorderSprints, ResizeTerminal, ResolveAgentPermissions, ResolveAgentSystemPermissions,
ResolveMemoryLinks, RestoreOpenWindows, RetryBackgroundTask, ReviewPluginPackage,
RevokeAllDevices, RevokeDevice, RotateConversationLog, SaveClaudeProviderProfile,
SaveCodexProviderProfile, SaveEmbedderProfile, SaveModelServer, SaveOpenCodeProviderProfile,
SaveProfile, SessionLimitService, SetActiveLayout, SetPluginEnabled, SnapshotOpenWindows,
SnapshotRunningAgents, SpawnBackgroundCommand, StopLiveAgent, StructuredRoutingMode,
StructuredSessions, SuggestedThisSession, SyncAgentWithTemplate, TerminalSessions, TouchDevice,
UnassignSkillFromAgent, UnassignTicketFromSprint, UninstallPlugin, UnlinkIssues,
UpdateAgentContext, UpdateAgentMcpToolPermissions, UpdateAgentPermissions,
UpdateAgentSystemPermissions, UpdateIssue, UpdateIssueCarnet, UpdateLiveState, UpdateMemory,
UpdateProjectContext, UpdateProjectMcpToolPermissions, UpdateProjectPermissions,
UpdateProjectSystemPermissions, UpdateSkill, UpdateTemplate, WakeSessionProvider, WriteMemory,
WriteToTerminal, AGENT_MEMORY_RECALL_BUDGET,
RevokeAllDevices, RevokeDevice, RotateConversationLog, SaveEmbedderProfile, SaveModelServer,
SaveOpenCodeProviderProfile, SaveProfile, SessionLimitService, SetActiveLayout,
SetPluginEnabled, SnapshotOpenWindows, SnapshotRunningAgents, SpawnBackgroundCommand,
StopLiveAgent, StructuredRoutingMode, StructuredSessions, SuggestedThisSession,
SyncAgentWithTemplate, TerminalSessions, TouchDevice, UnassignSkillFromAgent,
UnassignTicketFromSprint, UninstallPlugin, UnlinkIssues, UpdateAgentContext,
UpdateAgentMcpToolPermissions, UpdateAgentPermissions, UpdateAgentSystemPermissions,
UpdateIssue, UpdateIssueCarnet, UpdateLiveState, UpdateMemory, UpdateProjectContext,
UpdateProjectMcpToolPermissions, UpdateProjectPermissions, UpdateProjectSystemPermissions,
UpdateSkill, UpdateTemplate, WakeSessionProvider, WriteMemory, WriteToTerminal,
AGENT_MEMORY_RECALL_BUDGET,
};
use async_trait::async_trait;
use domain::ports::{
@ -938,18 +938,8 @@ pub struct BackendCore {
/// Save (upsert) an OpenCode profile backed by a cloud provider, sealing its
/// literal API key into the [`SecretStore`] (ticket #92, lot B3).
pub save_opencode_provider_profile: Arc<SaveOpenCodeProviderProfile>,
/// Save (upsert) a Codex profile backed by a provider/model, sealing its
/// literal API key into the [`SecretStore`] (ticket #99).
pub save_codex_provider_profile: Arc<SaveCodexProviderProfile>,
/// Save (upsert) a Claude profile backed by a provider/model, sealing its
/// literal API key into the [`SecretStore`] (ticket #99).
pub save_claude_provider_profile: Arc<SaveClaudeProviderProfile>,
/// Static catalogue of OpenCode cloud providers (ticket #92, lot B3).
pub list_opencode_providers: Arc<ListOpenCodeProviders>,
/// Static catalogue of Codex providers (ticket #99).
pub list_codex_providers: Arc<ListCodexProviders>,
/// Static catalogue of Claude providers (ticket #99).
pub list_claude_providers: Arc<ListClaudeProviders>,
/// Create a new OpenCode profile instance from the canonical seed.
pub clone_opencode_profile_from_seed: Arc<CloneOpenCodeProfileFromSeed>,
/// Delete a profile.
@ -1477,19 +1467,7 @@ impl BackendCore {
Arc::clone(&secret_store_port),
Arc::clone(&ids) as Arc<dyn IdGenerator>,
));
let save_codex_provider_profile = Arc::new(SaveCodexProviderProfile::new(
Arc::clone(&profile_store_port),
Arc::clone(&secret_store_port),
Arc::clone(&ids) as Arc<dyn IdGenerator>,
));
let save_claude_provider_profile = Arc::new(SaveClaudeProviderProfile::new(
Arc::clone(&profile_store_port),
Arc::clone(&secret_store_port),
Arc::clone(&ids) as Arc<dyn IdGenerator>,
));
let list_opencode_providers = Arc::new(ListOpenCodeProviders::new());
let list_codex_providers = Arc::new(ListCodexProviders::new());
let list_claude_providers = Arc::new(ListClaudeProviders::new());
let clone_opencode_profile_from_seed = Arc::new(CloneOpenCodeProfileFromSeed::new(
Arc::clone(&profile_store_port),
Arc::clone(&ids) as Arc<dyn IdGenerator>,
@ -2682,11 +2660,7 @@ impl BackendCore {
list_profiles,
save_profile,
save_opencode_provider_profile,
save_codex_provider_profile,
save_claude_provider_profile,
list_opencode_providers,
list_codex_providers,
list_claude_providers,
clone_opencode_profile_from_seed,
delete_profile,
configure_profiles,