feat(background): déclencheur in-app des tâches de fond via MCP idea_run_in_background (BE-1+BE-2)

Ferme la boucle B8 côté surface agent : un agent peut lancer une commande
en tâche de fond depuis l'app, sans dépendance à un déclencheur externe.

- domain : variante OrchestratorCommand::RunInBackground.
- infrastructure : outil MCP idea_run_in_background (déclaration + schéma +
  map_tool_call), tests de mapping et compteur de catalogue.
- application : handler → SpawnBackgroundCommand (owner=requester, WakeOwner).
- app-tauri : câblage .with_spawn_background_command + catalogue MCP 23→24.

Tests verts : domain 452 / application 467 / infrastructure 489 /
app-tauri 236, build --release vert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-03 22:44:48 +02:00
parent 41278bd632
commit f08dae62eb
6 changed files with 542 additions and 20 deletions

View File

@ -41,12 +41,11 @@ use application::{
use async_trait::async_trait;
use domain::ports::{
AgentContextStore, AgentRuntime, AgentSession, AgentSessionFactory, AgentWakePort,
BackgroundTaskPortError, BackgroundTaskRunner, BackgroundTaskStore,
Clock, Embedder, EmbedderEnvInspector,
EmbedderProfileStore, EmbedderPromptStore, EventBus, FileSystem, GitPort, IdGenerator,
IssueNumberAllocator, IssueStore, MemoryRecall, MemoryStore, PermissionStore, ProcessSpawner,
ProfileStore, ProjectStore, PtyPort, ScheduledTask, Scheduler, SkillStore, TemplateStore,
WakeError, WakeReason,
BackgroundTaskPortError, BackgroundTaskRunner, BackgroundTaskStore, Clock, Embedder,
EmbedderEnvInspector, EmbedderProfileStore, EmbedderPromptStore, EventBus, FileSystem, GitPort,
IdGenerator, IssueNumberAllocator, IssueStore, MemoryRecall, MemoryStore, PermissionStore,
ProcessSpawner, ProfileStore, ProjectStore, PtyPort, ScheduledTask, Scheduler, SkillStore,
TemplateStore, WakeError, WakeReason,
};
use domain::profile::{
AgentProfile, ContextInjection, McpConfigStrategy, McpTransport, StructuredAdapter,
@ -61,9 +60,9 @@ use serde_json::{json, Map, Value};
use uuid::Uuid;
use infrastructure::{
AdaptiveMemoryRecall, BackgroundCompletionSink, BackgroundTaskReadyToDeliver,
ClaudePermissionProjector, ClaudeTranscriptInspector, CliAgentRuntime,
CodexPermissionProjector, CommandBackgroundRunner, embedder_from_profile, EmbedderEnvProbe,
embedder_from_profile, AdaptiveMemoryRecall, BackgroundCompletionSink,
BackgroundTaskReadyToDeliver, ClaudePermissionProjector, ClaudeTranscriptInspector,
CliAgentRuntime, CodexPermissionProjector, CommandBackgroundRunner, EmbedderEnvProbe,
FsBackgroundTaskStore, FsConversationLog, FsEmbedderProfileStore, FsEmbedderPromptStore,
FsHandoffStore, FsIssueNumberAllocator, FsIssueStore, FsLiveStateStore, FsMemoryStore,
FsOrchestratorWatcher, FsPermissionStore, FsProfileStore, FsProjectStore,
@ -1992,7 +1991,10 @@ impl AppState {
.with_background_tasks(
Arc::clone(&background_tasks_port),
Arc::clone(&clock) as Arc<dyn Clock>,
),
)
// Producteur B8 côté agent MCP : `idea_run_in_background` passe par le
// même use case que la commande Tauri, sans aller-retour frontend.
.with_spawn_background_command(Arc::clone(&spawn_background_command)),
);
let stop_live_agent = Arc::new(
@ -4104,6 +4106,7 @@ mod mcp_serve_peer_tests {
for expected in [
"idea_list_agents",
"idea_ask_agent",
"idea_run_in_background",
"idea_launch_agent",
"idea_stop_agent",
"idea_update_context",
@ -4140,8 +4143,8 @@ mod mcp_serve_peer_tests {
assert!(!names.contains(&"idea_reply"));
assert_eq!(
tools.len(),
23,
"exactly the twenty-three exposed idea_* tools; got {names:?}"
24,
"exactly the twenty-four exposed idea_* tools; got {names:?}"
);
drop(client); // EOF ⇒ serve loop ends