fix: livrable tickets #70 #100 #102 — UX surface scoping

- #70: implémentation suppression modèles locaux téléchargés
- #100: correction scroll OpenCode
- #102: correction fit TUI après switch/layout
- memory note scoping UX
This commit is contained in:
2026-07-26 19:42:06 +02:00
parent 4321d048ac
commit 02603441c1
31 changed files with 1589 additions and 152 deletions

View File

@ -1343,7 +1343,10 @@ impl From<FirstRunStateOutput> for FirstRunStateDto {
// Local model servers (B35)
// ---------------------------------------------------------------------------
use application::{ListModelServersOutput, SaveModelServerInput, SaveModelServerOutput};
use application::{
ListModelServersOutput, ModelArtifactView, ModelServerListItem, SaveModelServerInput,
SaveModelServerOutput,
};
use domain::model_server::{
ExecutablePath, HfModelRef, LlamaCppOptions, LocalModelRef, LocalModelServerConfig,
LocalModelServerKind, ModelPath, ModelServerEndpoint, ModelSource,
@ -1492,6 +1495,9 @@ pub struct ModelServerConfigDto {
/// Optional readiness warmup deadline override in seconds.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub warmup_deadline_secs: Option<u64>,
/// Derived local artifact cache state.
#[serde(default)]
pub artifact: ModelArtifactDto,
}
impl ModelServerConfigDto {
@ -1516,6 +1522,7 @@ impl ModelServerConfigDto {
auto_start: config.auto_start,
stop_policy: config.stop_policy.into(),
warmup_deadline_secs: config.warmup_deadline_secs,
artifact: ModelArtifactDto::NotManaged,
}
}
@ -1563,6 +1570,53 @@ impl ModelServerConfigDto {
}
}
/// Local model artifact cache state on the IPC wire.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", tag = "state")]
pub enum ModelArtifactDto {
/// The configured source is not managed by IdeA's downloader.
NotManaged,
/// The configured source is managed but not present in cache.
Missing,
/// A download/prepare operation is currently running for this server.
Downloading,
/// The configured source is present in cache.
Downloaded {
/// Local artifact path.
path: String,
/// Total on-disk size when known.
#[serde(default, skip_serializing_if = "Option::is_none")]
size_bytes: Option<u64>,
},
}
impl Default for ModelArtifactDto {
fn default() -> Self {
Self::NotManaged
}
}
impl From<ModelArtifactView> for ModelArtifactDto {
fn from(view: ModelArtifactView) -> Self {
match view {
ModelArtifactView::NotManaged => Self::NotManaged,
ModelArtifactView::Missing => Self::Missing,
ModelArtifactView::Downloading => Self::Downloading,
ModelArtifactView::Downloaded { path, size_bytes } => {
Self::Downloaded { path, size_bytes }
}
}
}
}
impl From<ModelServerListItem> for ModelServerConfigDto {
fn from(item: ModelServerListItem) -> Self {
let mut dto = Self::from_domain(item.config);
dto.artifact = item.artifact.into();
dto
}
}
/// Response DTO for `preview_model_server_command`.
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
@ -1585,7 +1639,7 @@ impl From<ListModelServersOutput> for ModelServerConfigListDto {
Self(
out.servers
.into_iter()
.map(ModelServerConfigDto::from_domain)
.map(ModelServerConfigDto::from)
.collect(),
)
}

View File

@ -19,15 +19,15 @@ use application::{
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, ListClaudeModels, ListCodexModels, ListDevices,
ListEmbedderProfiles, ListIssues, ListLayouts, ListMemories, ListModelServers,
DeleteMemory, DeleteModelArtifact, 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, ListClaudeModels, ListCodexModels,
ListDevices, ListEmbedderProfiles, ListIssues, ListLayouts, ListMemories, ListModelServers,
ListOpenCodeProviders, ListPluginRuntimeContributions, ListPlugins, ListProfiles, ListProjects,
ListResumableAgents, ListSkills, ListSprints, ListTemplates, LiveAgentRegistry, LiveSessions,
LiveStateLeanProvider, LiveStateProvider, LiveStateReadProvider, LoadLayout, McpRuntime,
@ -59,8 +59,8 @@ use domain::ports::{
BackgroundTaskStore, Clock, DeviceSessionStore, Embedder, EmbedderEnvInspector,
EmbedderProfileStore, EmbedderPromptStore, EventBus, FileSystem, GitPort, IdGenerator,
IssueNumberAllocator, IssueStore, McpToolPermissionStore, MemoryRecall, MemoryStore,
PermissionStore, PluginManifestValidator, PluginMcpSupervisor, PluginPackageStore,
PluginRegistryStore, ProcessSpawner, ProfileStore, ProjectStore, PtyPort,
ModelArtifactDownloader, PermissionStore, PluginManifestValidator, PluginMcpSupervisor,
PluginPackageStore, PluginRegistryStore, ProcessSpawner, ProfileStore, ProjectStore, PtyPort,
RuntimePermissionProbe, ScheduledTask, Scheduler, SecretStore, SkillStore, SprintStore,
StructuredSessionEnvironmentPreparer, SystemPermissionStore, TemplateStore, ToolInvoker,
WakeError, WakeReason, WindowStateStore,
@ -965,6 +965,8 @@ pub struct BackendCore {
pub save_model_server: Arc<SaveModelServer>,
/// Deletes local model server configurations when unused.
pub delete_model_server: Arc<DeleteModelServer>,
/// Deletes managed local model artifacts without deleting server configs.
pub delete_model_artifact: Arc<DeleteModelArtifact>,
/// The local PTY adapter, kept port-typed so driving adapters can subscribe
/// output and route it through their own transport bridge.
pub pty_port: Arc<dyn PtyPort>,
@ -1513,25 +1515,31 @@ impl BackendCore {
let model_artifact_downloader = Arc::new(HfModelArtifactDownloader::new(
app_data_dir.join("hf-model-artifacts"),
));
let model_server_probe_port = Arc::new(HttpOpenAiCompatibleProbe::default())
as Arc<dyn domain::ports::ModelServerProbe>;
let ensure_local_model_server = Arc::new(
EnsureLocalModelServer::new(
Arc::clone(&model_server_registry) as Arc<dyn domain::ports::ModelServerRegistry>,
Arc::new(HttpOpenAiCompatibleProbe::default())
as Arc<dyn domain::ports::ModelServerProbe>,
Arc::clone(&model_server_probe_port),
Arc::new(LocalManagedProcess::new()) as Arc<dyn domain::ports::ManagedProcess>,
Arc::new(LlamaCppRuntime::new()) as Arc<dyn domain::ports::ModelServerRuntime>,
Arc::clone(&fs_port),
Arc::clone(&events_port),
)
.with_model_artifact_downloader(
model_artifact_downloader as Arc<dyn domain::ports::ModelArtifactDownloader>,
),
.with_model_artifact_downloader(Arc::clone(&model_artifact_downloader)
as Arc<dyn domain::ports::ModelArtifactDownloader>),
);
let model_server_registry_port =
Arc::clone(&model_server_registry) as Arc<dyn domain::ports::ModelServerRegistry>;
let list_model_servers = Arc::new(ListModelServers::new(Arc::clone(
&model_server_registry_port,
)));
let model_artifact_downloader_port =
Arc::clone(&model_artifact_downloader) as Arc<dyn ModelArtifactDownloader>;
let model_artifact_download_tracker = Arc::clone(&ensure_local_model_server)
as Arc<dyn application::ModelArtifactDownloadTracker>;
let list_model_servers = Arc::new(
ListModelServers::new(Arc::clone(&model_server_registry_port))
.with_model_artifact_downloader(Arc::clone(&model_artifact_downloader_port))
.with_download_tracker(Arc::clone(&model_artifact_download_tracker)),
);
let save_model_server = Arc::new(SaveModelServer::new(Arc::clone(
&model_server_registry_port,
)));
@ -2387,6 +2395,16 @@ impl BackendCore {
Arc::clone(&terminal_sessions),
Arc::clone(&structured_sessions),
));
let delete_model_artifact = Arc::new(DeleteModelArtifact::new(
Arc::clone(&model_server_registry_port),
Arc::clone(&model_server_probe_port),
Arc::clone(&model_artifact_downloader_port),
Arc::clone(&model_artifact_download_tracker),
Arc::clone(&profile_store_port),
Arc::clone(&store_port),
Arc::clone(&contexts_port),
Arc::clone(&live_sessions) as Arc<dyn LiveAgentRegistry>,
));
// Réconciliation du live-state au reboot : repasse en `idle` les lignes
// fantômes (working/waiting/blocked) dont la session n'est plus vivante,
// selon le MÊME registre de liveness que `GetProjectWorkState`. Provider
@ -2699,6 +2717,7 @@ impl BackendCore {
list_model_servers,
save_model_server,
delete_model_server,
delete_model_artifact,
pty_port,
terminal_sessions,
event_bus,