feat(infra): tmp partagé inter-projets géré par IdeA (handles opaques, leases, GC, projection sandbox) — #189 (QA verte)
- Domaine: port ManagedTempStore + modèle (handle opaque, kind Directory/File, état Active/Released, allocation/lease, expiry). - Application: use cases Allocate/Resolve/Release/GcManagedTemp, horodatés via Clock. - Infrastructure: impl FS FsManagedTempStore sous app_data_dir/managed-tmp (root global inter-projets, métadonnées par handle, GC des leases expirés/libérés). - Exposition MCP contrôlée (ManagedTempToolProvider, catalogue) + câblage sandbox: les agents résolvent un handle au lieu d'inventer un path libre. - Tests: application/infrastructure managed_temp.
This commit is contained in:
@ -13,24 +13,24 @@ use std::sync::{Arc, Mutex};
|
||||
|
||||
use application::orchestrator::UpdateProjectContext as GuardedUpdateProjectContext;
|
||||
use application::{
|
||||
AddIssueAttachment, AgentResumer, AgentWakeService, AppError, AssignIssueAgent,
|
||||
AssignSkillToAgent, AssignTicketToSprint, AttachLiveAgent, AuthenticateSession,
|
||||
BackgroundCommandArchive, BulkDeleteIssues, BulkUpdateIssuePriority, BulkUpdateIssueStatus,
|
||||
CancelBackgroundTask, ChangeAgentProfile, CheckEmbedderSuggestion, ClearChatDraft,
|
||||
CloneOpenCodeProfileFromSeed, CloneProfileFromSeed, CloseProject, CloseTab, CloseTerminal,
|
||||
CloseTicketAssistant, ConfigureProfiles, ContextGuardUseCases, CreateAgentFromScratch,
|
||||
CreateAgentFromTemplate, CreateIssue, CreateLayout, CreateMemory, CreateProject, CreateSkill,
|
||||
CreateSprint, CreateTemplate, DeleteAgent, DeleteEmbedderProfile, DeleteIssue, DeleteLayout,
|
||||
DeleteMemory, DeleteModelArtifact, DeleteModelServer, DeleteProfile, DeleteSkill, DeleteSprint,
|
||||
DeleteTemplate, DescribeEmbedderEngines, DetectAgentDrift, DetectProfiles,
|
||||
DismissEmbedderSuggestion, EnsureLocalModelServer, ExecuteSlashCommand, FirstRunState,
|
||||
GetAppExitWorkGuardState, GetLiveStateLean, GetMemory, GetProjectPermissions,
|
||||
GetProjectSystemPermissions, GetProjectWorkState, GitBranches, GitCheckout, GitCommit,
|
||||
GitGraph, GitInit, GitLog, GitStage, GitStatus, GitUnstage, HarvestMemoryFromTurn,
|
||||
HealthUseCase, ImportChatAttachments, InspectConversation, InstallPluginFromArchive,
|
||||
InstallPluginFromDirectory, JsonPluginManifestValidator, LaunchAgent, LaunchAgentInput,
|
||||
LinkIssues, ListAgents, ListAgentsInput, ListClaudeModels, ListCodexModels, ListDevices,
|
||||
ListEmbedderProfiles, ListIssues, ListLayouts, ListMemories, ListModelServers,
|
||||
AddIssueAttachment, AgentResumer, AgentWakeService, AllocateManagedTemp, AppError,
|
||||
AssignIssueAgent, AssignSkillToAgent, AssignTicketToSprint, AttachLiveAgent,
|
||||
AuthenticateSession, BackgroundCommandArchive, BulkDeleteIssues, BulkUpdateIssuePriority,
|
||||
BulkUpdateIssueStatus, CancelBackgroundTask, ChangeAgentProfile, CheckEmbedderSuggestion,
|
||||
ClearChatDraft, CloneOpenCodeProfileFromSeed, CloneProfileFromSeed, CloseProject, CloseTab,
|
||||
CloseTerminal, CloseTicketAssistant, ConfigureProfiles, ContextGuardUseCases,
|
||||
CreateAgentFromScratch, CreateAgentFromTemplate, CreateIssue, CreateLayout, CreateMemory,
|
||||
CreateProject, CreateSkill, CreateSprint, CreateTemplate, DeleteAgent, DeleteEmbedderProfile,
|
||||
DeleteIssue, DeleteLayout, DeleteMemory, DeleteModelArtifact, DeleteModelServer, DeleteProfile,
|
||||
DeleteSkill, DeleteSprint, DeleteTemplate, DescribeEmbedderEngines, DetectAgentDrift,
|
||||
DetectProfiles, DismissEmbedderSuggestion, EnsureLocalModelServer, ExecuteSlashCommand,
|
||||
FirstRunState, GcManagedTemp, GetAppExitWorkGuardState, GetLiveStateLean, GetMemory,
|
||||
GetProjectPermissions, GetProjectSystemPermissions, GetProjectWorkState, GitBranches,
|
||||
GitCheckout, GitCommit, GitGraph, GitInit, GitLog, GitStage, GitStatus, GitUnstage,
|
||||
HarvestMemoryFromTurn, HealthUseCase, ImportChatAttachments, InspectConversation,
|
||||
InstallPluginFromArchive, InstallPluginFromDirectory, JsonPluginManifestValidator, LaunchAgent,
|
||||
LaunchAgentInput, LinkIssues, ListAgents, ListAgentsInput, ListClaudeModels, ListCodexModels,
|
||||
ListDevices, ListEmbedderProfiles, ListIssues, ListLayouts, ListMemories, ListModelServers,
|
||||
ListOpenCodeProviders, ListPluginRuntimeContributions, ListPlugins, ListProfiles, ListProjects,
|
||||
ListResumableAgents, ListSkills, ListSlashCommands, ListSprints, ListTemplates,
|
||||
LiveAgentRegistry, LiveSessions, LiveStateLeanProvider, LiveStateProvider,
|
||||
@ -44,21 +44,21 @@ use application::{
|
||||
ReadIssueAttachment, ReadIssueCarnet, ReadMcpToolPermissions, ReadMemory, ReadMemoryIndex,
|
||||
ReadProjectContext, ReadSkill, ReadTemplate, RecallMemory, ReconcileLayouts,
|
||||
ReconcileLiveState, ReconcileLiveStateInput, ReconcilePluginMcpServers, RecordTurn,
|
||||
RecordTurnProvider, ReferenceProfiles, RenameDevice, RenameLayout, RenameSprint,
|
||||
ReorderSprints, ResizeTerminal, ResolveAgentCapabilities, ResolveAgentPermissions,
|
||||
ResolveAgentSystemPermissions, ResolveMemoryLinks, RestoreOpenWindows, RetryBackgroundTask,
|
||||
ReviewPluginPackage, RevokeAllDevices, RevokeDevice, RotateConversationLog, SaveChatDraft,
|
||||
SaveEmbedderProfile, SaveModelServer, SaveOpenCodeProviderProfile, SaveProfile,
|
||||
SessionLimitService, SetActiveLayout, SetPluginEnabled, SlashCommandRegistry,
|
||||
SnapshotOpenWindows, SnapshotRunningAgents, SpawnBackgroundCommand, StopLiveAgent,
|
||||
StructuredRoutingMode, StructuredSessions, SuggestedThisSession, SyncAgentWithTemplate,
|
||||
TerminalSessions, TouchDevice, UnassignSkillFromAgent, UnassignTicketFromSprint,
|
||||
UninstallPlugin, UnlinkIssues, UpdateAgentContext, UpdateAgentEffort,
|
||||
UpdateAgentMcpToolPermissions, UpdateAgentPermissions, UpdateAgentSystemPermissions,
|
||||
UpdateIssue, UpdateIssueCarnet, UpdateLiveState, UpdateMemory, UpdateProjectContext,
|
||||
UpdateProjectMcpToolPermissions, UpdateProjectPermissions, UpdateProjectSystemPermissions,
|
||||
UpdateSkill, UpdateTemplate, WakeSessionProvider, WriteMemory, WriteToTerminal,
|
||||
AGENT_MEMORY_RECALL_BUDGET,
|
||||
RecordTurnProvider, ReferenceProfiles, ReleaseManagedTemp, RenameDevice, RenameLayout,
|
||||
RenameSprint, ReorderSprints, ResizeTerminal, ResolveAgentCapabilities,
|
||||
ResolveAgentPermissions, ResolveAgentSystemPermissions, ResolveManagedTemp, ResolveMemoryLinks,
|
||||
RestoreOpenWindows, RetryBackgroundTask, ReviewPluginPackage, RevokeAllDevices, RevokeDevice,
|
||||
RotateConversationLog, SaveChatDraft, SaveEmbedderProfile, SaveModelServer,
|
||||
SaveOpenCodeProviderProfile, SaveProfile, SessionLimitService, SetActiveLayout,
|
||||
SetPluginEnabled, SlashCommandRegistry, SnapshotOpenWindows, SnapshotRunningAgents,
|
||||
SpawnBackgroundCommand, StopLiveAgent, StructuredRoutingMode, StructuredSessions,
|
||||
SuggestedThisSession, SyncAgentWithTemplate, TerminalSessions, TouchDevice,
|
||||
UnassignSkillFromAgent, UnassignTicketFromSprint, UninstallPlugin, UnlinkIssues,
|
||||
UpdateAgentContext, UpdateAgentEffort, 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::{
|
||||
@ -66,11 +66,11 @@ use domain::ports::{
|
||||
AgentWakePort, AssistantContextProvider, BackgroundTaskPortError, BackgroundTaskRunner,
|
||||
BackgroundTaskStore, ChatAttachmentStore, ChatDraftStore, Clock, DeviceSessionStore, Embedder,
|
||||
EmbedderEnvInspector, EmbedderProfileStore, EmbedderPromptStore, EnvironmentReader, EventBus,
|
||||
FileSystem, GitPort, IdGenerator, IssueNumberAllocator, IssueStore, McpToolPermissionStore,
|
||||
MemoryRecall, MemoryStore, ModelArtifactDownloader, PermissionStore, PluginManifestValidator,
|
||||
PluginMcpSupervisor, PluginPackageStore, PluginRegistryStore, PluginStorageStore,
|
||||
ProcessSpawner, ProfileStore, ProjectStore, PtyHandle, PtyPort, RuntimePermissionProbe,
|
||||
ScheduledTask, Scheduler, SecretStore, SkillStore, SprintStore,
|
||||
FileSystem, GitPort, IdGenerator, IssueNumberAllocator, IssueStore, ManagedTempStore,
|
||||
McpToolPermissionStore, MemoryRecall, MemoryStore, ModelArtifactDownloader, PermissionStore,
|
||||
PluginManifestValidator, PluginMcpSupervisor, PluginPackageStore, PluginRegistryStore,
|
||||
PluginStorageStore, ProcessSpawner, ProfileStore, ProjectStore, PtyHandle, PtyPort,
|
||||
RuntimePermissionProbe, ScheduledTask, Scheduler, SecretStore, SkillStore, SprintStore,
|
||||
StructuredSessionEnvironmentPreparer, SystemPermissionStore, TemplateStore, ToolInvoker,
|
||||
WakeError, WakeReason, WindowStateStore,
|
||||
};
|
||||
@ -94,7 +94,7 @@ use infrastructure::{
|
||||
EmbeddedCompatibilityMatrix, EmbedderEnvProbe, ExternalMcpPluginSupervisor,
|
||||
FsAssistantContextStore, FsBackgroundTaskStore, FsChatAttachmentStore, FsChatDraftStore,
|
||||
FsConversationLog, FsDeviceSessionStore, FsEmbedderProfileStore, FsEmbedderPromptStore,
|
||||
FsHandoffStore, FsIssueNumberAllocator, FsIssueStore, FsLiveStateStore,
|
||||
FsHandoffStore, FsIssueNumberAllocator, FsIssueStore, FsLiveStateStore, FsManagedTempStore,
|
||||
FsMcpToolPermissionStore, FsMemoryStore, FsModelServerRegistry, FsOrchestratorWatcher,
|
||||
FsPermissionStore, FsPluginPackageStore, FsPluginRegistryStore, FsPluginStorageStore,
|
||||
FsProfileStore, FsProjectStore, FsProviderSessionStore, FsSecretStore, FsSkillStore,
|
||||
@ -102,8 +102,8 @@ use infrastructure::{
|
||||
HeuristicHandoffSummarizer, HfModelArtifactDownloader, HttpOpenAiCompatibleProbe,
|
||||
HttpProviderModelCatalogue, IdeaiContextStore, InMemoryConversationRegistry, InMemoryMailbox,
|
||||
InMemoryPairAttemptLimiter, LlamaCppRuntime, LocalEnvironmentReader, LocalFileSystem,
|
||||
LocalManagedProcess, LocalProcessSpawner, McpServer, MediatedInbox, NaiveMemoryRecall,
|
||||
OrchestratorWatchHandle, PortablePtyAdapter, ProcessCliVersionReader,
|
||||
LocalManagedProcess, LocalProcessSpawner, ManagedTempToolProvider, McpServer, MediatedInbox,
|
||||
NaiveMemoryRecall, OrchestratorWatchHandle, PortablePtyAdapter, ProcessCliVersionReader,
|
||||
ReadOnlyRuntimePermissionProbe, RwFileGuard, StructuredSessionFactory, SystemClock,
|
||||
SystemMillisClock, TemplateToolProvider, TicketAssistantEnvironmentPreparer,
|
||||
TicketToolProvider, TokioBroadcastEventBus, TokioScheduler, ToolPolicyRegistry, UuidGenerator,
|
||||
@ -900,6 +900,48 @@ impl AgentResumer for AppAgentResumer {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct LateBoundManagedTempToolProvider {
|
||||
inner: Arc<Mutex<Option<Arc<dyn ManagedTempToolProvider>>>>,
|
||||
}
|
||||
|
||||
impl LateBoundManagedTempToolProvider {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn bind(&self, inner: Arc<dyn ManagedTempToolProvider>) {
|
||||
*self.inner.lock().expect("managed temp tool mutex poisoned") = Some(inner);
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ManagedTempToolProvider for LateBoundManagedTempToolProvider {
|
||||
async fn handle_managed_temp_tool(
|
||||
&self,
|
||||
project: &Project,
|
||||
requester: &str,
|
||||
name: &str,
|
||||
arguments: Value,
|
||||
) -> Result<Value, infrastructure::ManagedTempToolError> {
|
||||
let inner = self
|
||||
.inner
|
||||
.lock()
|
||||
.expect("managed temp tool mutex poisoned")
|
||||
.clone()
|
||||
.ok_or_else(|| {
|
||||
infrastructure::ManagedTempToolError::new(
|
||||
"UNAVAILABLE",
|
||||
"managed temp tool provider not bound".to_owned(),
|
||||
)
|
||||
})?;
|
||||
inner
|
||||
.handle_managed_temp_tool(project, requester, name, arguments)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
/// Everything a driving adapter needs at runtime.
|
||||
///
|
||||
/// Use cases are stored behind `Arc` so handlers clone cheaply. The concrete
|
||||
@ -1054,6 +1096,14 @@ pub struct BackendCore {
|
||||
pub mark_issue_attachment_summarized: Arc<MarkIssueAttachmentSummarized>,
|
||||
/// Import durable agent/chat attachments.
|
||||
pub import_chat_attachments: Arc<ImportChatAttachments>,
|
||||
/// Allocate an IdeA-managed temporary file/directory.
|
||||
pub allocate_managed_temp: Arc<AllocateManagedTemp>,
|
||||
/// Resolve an active IdeA-managed temporary handle.
|
||||
pub resolve_managed_temp: Arc<ResolveManagedTemp>,
|
||||
/// Release an IdeA-managed temporary handle.
|
||||
pub release_managed_temp: Arc<ReleaseManagedTemp>,
|
||||
/// Garbage-collect released or expired managed temporary allocations.
|
||||
pub gc_managed_temp: Arc<GcManagedTemp>,
|
||||
/// Read one persisted custom chat draft.
|
||||
pub read_chat_draft: Arc<ReadChatDraft>,
|
||||
/// Save one persisted custom chat draft.
|
||||
@ -1084,6 +1134,8 @@ pub struct BackendCore {
|
||||
pub(crate) ticket_tool_provider: Arc<dyn TicketToolProvider>,
|
||||
/// MCP provider for the public `idea_template_*` tools.
|
||||
pub(crate) template_tool_provider: Arc<dyn TemplateToolProvider>,
|
||||
/// MCP provider for the public `idea_managed_temp_*` tools.
|
||||
pub(crate) managed_temp_tool_provider: Arc<dyn ManagedTempToolProvider>,
|
||||
/// Open an ephemeral AI assistant chat bound to one ticket.
|
||||
pub open_ticket_assistant: Arc<OpenTicketAssistant>,
|
||||
/// Close an ephemeral AI assistant chat bound to one ticket.
|
||||
@ -1341,6 +1393,8 @@ pub struct BackendCore {
|
||||
pub ticket_tool_binder: Arc<LateBoundTicketToolProvider>,
|
||||
/// Late-bound concrete template MCP provider supplied by the driving adapter.
|
||||
pub template_tool_binder: Arc<LateBoundTemplateToolProvider>,
|
||||
/// Late-bound concrete managed-temp MCP provider supplied by the driving adapter.
|
||||
pub managed_temp_tool_binder: Arc<LateBoundManagedTempToolProvider>,
|
||||
}
|
||||
|
||||
/// Backend decision for attaching a UI subscriber to a background task.
|
||||
@ -1753,6 +1807,8 @@ impl BackendCore {
|
||||
let chat_attachment_store = Arc::new(FsChatAttachmentStore::new());
|
||||
let chat_attachment_store_port =
|
||||
Arc::clone(&chat_attachment_store) as Arc<dyn ChatAttachmentStore>;
|
||||
let managed_temp_store = Arc::new(FsManagedTempStore::new(app_data_dir.clone()));
|
||||
let managed_temp_store_port = Arc::clone(&managed_temp_store) as Arc<dyn ManagedTempStore>;
|
||||
let chat_draft_store = Arc::new(FsChatDraftStore::new());
|
||||
let chat_draft_store_port = Arc::clone(&chat_draft_store) as Arc<dyn ChatDraftStore>;
|
||||
let create_issue = Arc::new(CreateIssue::new(
|
||||
@ -1813,6 +1869,23 @@ impl BackendCore {
|
||||
Arc::clone(&ids) as Arc<dyn IdGenerator>,
|
||||
Arc::clone(&clock) as Arc<dyn Clock>,
|
||||
));
|
||||
let allocate_managed_temp = Arc::new(AllocateManagedTemp::new(
|
||||
Arc::clone(&managed_temp_store_port),
|
||||
Arc::clone(&ids) as Arc<dyn IdGenerator>,
|
||||
Arc::clone(&clock) as Arc<dyn Clock>,
|
||||
));
|
||||
let resolve_managed_temp = Arc::new(ResolveManagedTemp::new(
|
||||
Arc::clone(&managed_temp_store_port),
|
||||
Arc::clone(&clock) as Arc<dyn Clock>,
|
||||
));
|
||||
let release_managed_temp = Arc::new(ReleaseManagedTemp::new(
|
||||
Arc::clone(&managed_temp_store_port),
|
||||
Arc::clone(&clock) as Arc<dyn Clock>,
|
||||
));
|
||||
let gc_managed_temp = Arc::new(GcManagedTemp::new(
|
||||
Arc::clone(&managed_temp_store_port),
|
||||
Arc::clone(&clock) as Arc<dyn Clock>,
|
||||
));
|
||||
let read_chat_draft = Arc::new(ReadChatDraft::new(
|
||||
Arc::clone(&store_port),
|
||||
Arc::clone(&chat_draft_store_port),
|
||||
@ -1885,6 +1958,9 @@ impl BackendCore {
|
||||
let ticket_tool_provider: Arc<dyn TicketToolProvider> = ticket_tool_binder.clone();
|
||||
let template_tool_binder = Arc::new(LateBoundTemplateToolProvider::new());
|
||||
let template_tool_provider: Arc<dyn TemplateToolProvider> = template_tool_binder.clone();
|
||||
let managed_temp_tool_binder = Arc::new(LateBoundManagedTempToolProvider::new());
|
||||
let managed_temp_tool_provider: Arc<dyn ManagedTempToolProvider> =
|
||||
managed_temp_tool_binder.clone();
|
||||
let tool_policy_registry = Arc::new(ToolPolicyRegistry::new());
|
||||
let tool_policy_store = Arc::clone(&tool_policy_registry) as Arc<dyn AgentToolPolicyStore>;
|
||||
let system_permission_store = Arc::new(FsSystemPermissionStore::new(Arc::clone(&fs_port)));
|
||||
@ -2958,6 +3034,7 @@ impl BackendCore {
|
||||
Arc::clone(&mcp_tool_permission_store_port),
|
||||
Arc::clone(&ticket_tool_provider),
|
||||
Arc::clone(&template_tool_provider),
|
||||
Arc::clone(&managed_temp_tool_provider),
|
||||
)) as Arc<dyn ToolInvoker>);
|
||||
|
||||
let stop_live_agent = Arc::new(
|
||||
@ -3061,6 +3138,10 @@ impl BackendCore {
|
||||
read_issue_attachment,
|
||||
mark_issue_attachment_summarized,
|
||||
import_chat_attachments,
|
||||
allocate_managed_temp,
|
||||
resolve_managed_temp,
|
||||
release_managed_temp,
|
||||
gc_managed_temp,
|
||||
read_chat_draft,
|
||||
save_chat_draft,
|
||||
clear_chat_draft,
|
||||
@ -3076,6 +3157,7 @@ impl BackendCore {
|
||||
unassign_ticket_from_sprint,
|
||||
ticket_tool_provider,
|
||||
template_tool_provider,
|
||||
managed_temp_tool_provider,
|
||||
open_ticket_assistant,
|
||||
close_ticket_assistant,
|
||||
tool_policy_registry,
|
||||
@ -3183,6 +3265,7 @@ impl BackendCore {
|
||||
plugin_manifest_validator: Arc::clone(&plugin_manifest_validator),
|
||||
ticket_tool_binder,
|
||||
template_tool_binder,
|
||||
managed_temp_tool_binder,
|
||||
}
|
||||
}
|
||||
|
||||
@ -3274,6 +3357,7 @@ impl BackendCore {
|
||||
.with_ready_sink(ready_sink)
|
||||
.with_ticket_tools(Arc::clone(&self.ticket_tool_provider))
|
||||
.with_template_tools(Arc::clone(&self.template_tool_provider))
|
||||
.with_managed_temp_tools(Arc::clone(&self.managed_temp_tool_provider))
|
||||
.with_tool_policies(Arc::clone(&self.tool_policy_registry))
|
||||
.with_mcp_tool_permissions(Arc::clone(&self.mcp_tool_permission_store)),
|
||||
endpoint,
|
||||
|
||||
Reference in New Issue
Block a user