feat(workstate): actions contrôlées sur le work-state (Lot D backend)
Ajoute un module d'actions contrôlées (`workstate/actions.rs`) côté application et l'expose via des commandes Tauri : DTO camelCase, câblage commands/state/lib. Les actions valident leurs invariants avant d'agir sur le read-model. - application : use cases d'actions contrôlées + intégration au work-state. - app-tauri : commandes dédiées, DTO et enregistrement dans le handler. Tests verts : application workstate_actions (7), workstate (21), app-tauri dto_agents (25), list_live_agents_r0b (5), cargo check OK. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -9,19 +9,20 @@ use tauri::State;
|
||||
|
||||
use crate::dto::DismissEmbedderSuggestionRequestDto;
|
||||
use application::{
|
||||
AppError, AssignSkillToAgentInput, ChangeAgentProfileInput, CloseProjectInput,
|
||||
CreateAgentInput, CreateLayoutInput, CreateMemoryInput, CreateSkillInput, DeleteAgentInput,
|
||||
DeleteEmbedderProfileInput, DeleteLayoutInput, DeleteMemoryInput, DeleteSkillInput,
|
||||
DeleteTemplateInput, DetectAgentDriftInput, GetMemoryInput, GetProjectWorkStateInput,
|
||||
GitBranchesInput, GitCheckoutInput, GitCommitInput, GitGraphInput, GitInitInput, GitLogInput,
|
||||
GitStagePathInput, GitStatusInput, InspectConversationInput, LaunchAgentInput, ListAgentsInput,
|
||||
ListLayoutsInput, ListMemoriesInput, ListResumableAgentsInput, ListSkillsInput, LiveSessions,
|
||||
LoadLayoutInput, McpRuntime, MutateLayoutInput, OpenProjectInput, ReadAgentContextInput,
|
||||
ReadMemoryIndexInput, ReadProjectContextInput, RecallMemoryInput, ReconcileLayoutsInput,
|
||||
RenameLayoutInput, ResolveAgentPermissionsInput, ResolveMemoryLinksInput, SetActiveLayoutInput,
|
||||
SnapshotRunningAgentsInput, SyncAgentWithTemplateInput, UnassignSkillFromAgentInput,
|
||||
UpdateAgentContextInput, UpdateAgentPermissionsInput, UpdateMemoryInput,
|
||||
UpdateProjectContextInput, UpdateProjectPermissionsInput, UpdateSkillInput,
|
||||
AppError, AssignSkillToAgentInput, AttachLiveAgentInput, ChangeAgentProfileInput,
|
||||
CloseProjectInput, CreateAgentInput, CreateLayoutInput, CreateMemoryInput, CreateSkillInput,
|
||||
DeleteAgentInput, DeleteEmbedderProfileInput, DeleteLayoutInput, DeleteMemoryInput,
|
||||
DeleteSkillInput, DeleteTemplateInput, DetectAgentDriftInput, GetMemoryInput,
|
||||
GetProjectWorkStateInput, GitBranchesInput, GitCheckoutInput, GitCommitInput, GitGraphInput,
|
||||
GitInitInput, GitLogInput, GitStagePathInput, GitStatusInput, InspectConversationInput,
|
||||
LaunchAgentInput, ListAgentsInput, ListLayoutsInput, ListMemoriesInput,
|
||||
ListResumableAgentsInput, ListSkillsInput, LiveSessions, LoadLayoutInput, McpRuntime,
|
||||
MutateLayoutInput, OpenProjectInput, ReadAgentContextInput, ReadMemoryIndexInput,
|
||||
ReadProjectContextInput, RecallMemoryInput, ReconcileLayoutsInput, RenameLayoutInput,
|
||||
ResolveAgentPermissionsInput, ResolveMemoryLinksInput, SetActiveLayoutInput,
|
||||
SnapshotRunningAgentsInput, StopLiveAgentInput, SyncAgentWithTemplateInput,
|
||||
UnassignSkillFromAgentInput, UpdateAgentContextInput, UpdateAgentPermissionsInput,
|
||||
UpdateMemoryInput, UpdateProjectContextInput, UpdateProjectPermissionsInput, UpdateSkillInput,
|
||||
};
|
||||
use domain::ports::PtyHandle;
|
||||
|
||||
@ -29,11 +30,11 @@ use crate::dto::{
|
||||
parse_agent_id, parse_close_terminal, parse_delete_profile, parse_layout_id, parse_memory_slug,
|
||||
parse_node_id, parse_profile_id, parse_project_id, parse_session_id, parse_skill_id,
|
||||
parse_template_id, parse_ticket_id, AgentDriftListDto, AgentDto, AgentListDto,
|
||||
AssignSkillRequestDto, AttachLiveAgentRequestDto, ChangeAgentProfileDto,
|
||||
ChangeAgentProfileRequestDto, ConfigureProfilesRequestDto, ConversationDetailsDto,
|
||||
CreateAgentFromTemplateRequestDto, CreateAgentRequestDto, CreateLayoutRequestDto,
|
||||
CreateLayoutResultDto, CreateMemoryRequestDto, CreateProjectRequestDto, CreateSkillRequestDto,
|
||||
CreateTemplateRequestDto, DeleteLayoutRequestDto, DeleteLayoutResultDto,
|
||||
AssignSkillRequestDto, AttachLiveAgentRequestDto, AttachLiveAgentResponseDto,
|
||||
ChangeAgentProfileDto, ChangeAgentProfileRequestDto, ConfigureProfilesRequestDto,
|
||||
ConversationDetailsDto, CreateAgentFromTemplateRequestDto, CreateAgentRequestDto,
|
||||
CreateLayoutRequestDto, CreateLayoutResultDto, CreateMemoryRequestDto, CreateProjectRequestDto,
|
||||
CreateSkillRequestDto, CreateTemplateRequestDto, DeleteLayoutRequestDto, DeleteLayoutResultDto,
|
||||
DeliveredDelegationRequestDto, DetectProfilesRequestDto, DetectProfilesResponseDto,
|
||||
EffectivePermissionsDto, EmbedderEnginesDto, EmbedderProfileDto, EmbedderProfileListDto,
|
||||
ErrorDto, FirstRunStateDto, FrontAttachedRequestDto, GitBranchesDto, GitCheckoutRequestDto,
|
||||
@ -46,7 +47,8 @@ use crate::dto::{
|
||||
ReattachResultDto, RecallMemoryRequestDto, RenameLayoutRequestDto, ReplyChunk,
|
||||
ResizeTerminalRequestDto, ResolveAgentPermissionsRequestDto, ResumableAgentListDto,
|
||||
SaveEmbedderProfileRequestDto, SaveProfileRequestDto, SetActiveLayoutRequestDto, SkillDto,
|
||||
SkillListDto, SyncAgentWithTemplateRequestDto, SyncResultDto, TemplateDto, TemplateListDto,
|
||||
SkillListDto, StopLiveAgentRequestDto, StopLiveAgentResponseDto,
|
||||
SyncAgentWithTemplateRequestDto, SyncResultDto, TemplateDto, TemplateListDto,
|
||||
TerminalClosedDto, TerminalSessionDto, UnassignSkillRequestDto, UpdateAgentContextRequestDto,
|
||||
UpdateAgentPermissionsRequestDto, UpdateMemoryRequestDto, UpdateProjectContextRequestDto,
|
||||
UpdateProjectPermissionsRequestDto, UpdateSkillRequestDto, UpdateTemplateRequestDto,
|
||||
@ -1036,20 +1038,42 @@ pub fn list_live_agents(
|
||||
pub async fn attach_live_agent(
|
||||
request: AttachLiveAgentRequestDto,
|
||||
state: State<'_, AppState>,
|
||||
) -> Result<LiveAgentListDto, ErrorDto> {
|
||||
let _project = resolve_project(&request.project_id, &state).await?;
|
||||
) -> Result<AttachLiveAgentResponseDto, ErrorDto> {
|
||||
let project = resolve_project(&request.project_id, &state).await?;
|
||||
let agent_id = parse_agent_id(&request.agent_id)?;
|
||||
let node_id = parse_node_id(&request.node_id)?;
|
||||
let session = state
|
||||
.terminal_sessions
|
||||
.rebind_agent_node(&agent_id, node_id)
|
||||
.ok_or_else(|| AppError::NotFound(format!("running session for agent {agent_id}")))?;
|
||||
state
|
||||
.attach_live_agent
|
||||
.execute(AttachLiveAgentInput {
|
||||
project,
|
||||
agent_id,
|
||||
node_id,
|
||||
})
|
||||
.map(AttachLiveAgentResponseDto::from)
|
||||
.map_err(ErrorDto::from)
|
||||
}
|
||||
|
||||
Ok(LiveAgentListDto::from_pairs(vec![(
|
||||
agent_id,
|
||||
session.node_id,
|
||||
session.id,
|
||||
)]))
|
||||
/// `stop_live_agent` — tear down an already-running agent's live session by agent
|
||||
/// id, polymorphically (PTY kill or structured shutdown), without removing the
|
||||
/// agent, its tickets, conversation summary or handoff.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns an [`ErrorDto`] (`INVALID` for malformed ids, `NOT_FOUND` if the
|
||||
/// project/agent is unknown or the agent has no live session, `PROCESS` on a
|
||||
/// kill/shutdown failure).
|
||||
#[tauri::command]
|
||||
pub async fn stop_live_agent(
|
||||
request: StopLiveAgentRequestDto,
|
||||
state: State<'_, AppState>,
|
||||
) -> Result<StopLiveAgentResponseDto, ErrorDto> {
|
||||
let project = resolve_project(&request.project_id, &state).await?;
|
||||
let agent_id = parse_agent_id(&request.agent_id)?;
|
||||
state
|
||||
.stop_live_agent
|
||||
.execute(StopLiveAgentInput { project, agent_id })
|
||||
.await
|
||||
.map(StopLiveAgentResponseDto::from)
|
||||
.map_err(ErrorDto::from)
|
||||
}
|
||||
|
||||
/// `read_agent_context` — read an agent's Markdown context.
|
||||
|
||||
@ -9,10 +9,10 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use application::{
|
||||
AgentTicketState, AppError, ConversationPreviewStatus, ConversationTurnWorkPreview,
|
||||
ConversationWorkSummary, CreateProjectInput, CreateProjectOutput, GitGraphOutput, HealthInput,
|
||||
HealthReport, LayoutKind, ListProjectsOutput, LiveSessionKind, OpenProjectOutput,
|
||||
ProjectWorkState, TicketWorkSource, TicketWorkStatus,
|
||||
AgentTicketState, AppError, AttachLiveAgentOutput, ConversationPreviewStatus,
|
||||
ConversationTurnWorkPreview, ConversationWorkSummary, CreateProjectInput, CreateProjectOutput,
|
||||
GitGraphOutput, HealthInput, HealthReport, LayoutKind, ListProjectsOutput, LiveSessionKind,
|
||||
OpenProjectOutput, ProjectWorkState, StopLiveAgentOutput, TicketWorkSource, TicketWorkStatus,
|
||||
};
|
||||
use domain::{AgentBusyState, Project, ProjectId, TurnRole};
|
||||
|
||||
@ -1751,6 +1751,64 @@ pub struct AttachLiveAgentRequestDto {
|
||||
pub node_id: String,
|
||||
}
|
||||
|
||||
/// Response DTO for `attach_live_agent`: the rebound live session's coordinates.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AttachLiveAgentResponseDto {
|
||||
/// The rebound agent's id.
|
||||
pub agent_id: String,
|
||||
/// The node now hosting the session view.
|
||||
pub node_id: String,
|
||||
/// The (unchanged) live session id.
|
||||
pub session_id: String,
|
||||
/// Runtime family that owns the session (`pty`/`structured`).
|
||||
pub kind: LiveWorkSessionKindDto,
|
||||
}
|
||||
|
||||
impl From<AttachLiveAgentOutput> for AttachLiveAgentResponseDto {
|
||||
fn from(out: AttachLiveAgentOutput) -> Self {
|
||||
Self {
|
||||
agent_id: out.agent_id.to_string(),
|
||||
node_id: out.node_id.to_string(),
|
||||
session_id: out.session_id.to_string(),
|
||||
kind: out.kind.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Request DTO for `stop_live_agent`: tear down an already-running agent's live
|
||||
/// session by agent id (no spawn, agent entity preserved).
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StopLiveAgentRequestDto {
|
||||
/// Id of the owning project (validated for symmetry and future scoping).
|
||||
pub project_id: String,
|
||||
/// Id of the running agent to stop.
|
||||
pub agent_id: String,
|
||||
}
|
||||
|
||||
/// Response DTO for `stop_live_agent`: the torn-down session's coordinates.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StopLiveAgentResponseDto {
|
||||
/// The stopped agent's id.
|
||||
pub agent_id: String,
|
||||
/// The id of the session that was torn down.
|
||||
pub session_id: String,
|
||||
/// Runtime family that owned the session (`pty`/`structured`).
|
||||
pub kind: LiveWorkSessionKindDto,
|
||||
}
|
||||
|
||||
impl From<StopLiveAgentOutput> for StopLiveAgentResponseDto {
|
||||
fn from(out: StopLiveAgentOutput) -> Self {
|
||||
Self {
|
||||
agent_id: out.agent_id.to_string(),
|
||||
session_id: out.session_id.to_string(),
|
||||
kind: out.kind.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses an agent-id string (UUID) coming from the frontend.
|
||||
///
|
||||
/// # Errors
|
||||
|
||||
@ -166,6 +166,7 @@ pub fn run() {
|
||||
commands::get_project_work_state,
|
||||
commands::list_live_agents,
|
||||
commands::attach_live_agent,
|
||||
commands::stop_live_agent,
|
||||
commands::read_agent_context,
|
||||
commands::update_agent_context,
|
||||
commands::delete_agent,
|
||||
|
||||
@ -12,22 +12,23 @@ use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use application::{
|
||||
AgentResumer, AppError, AssignSkillToAgent, ChangeAgentProfile, CheckEmbedderSuggestion,
|
||||
CloseProject, CloseTab, CloseTerminal, ConfigureProfiles, ContextGuardUseCases,
|
||||
CreateAgentFromScratch, CreateAgentFromTemplate, CreateLayout, CreateMemory, CreateProject,
|
||||
CreateSkill, CreateTemplate, DeleteAgent, DeleteEmbedderProfile, DeleteLayout, DeleteMemory,
|
||||
DeleteProfile, DeleteSkill, DeleteTemplate, DescribeEmbedderEngines, DetectAgentDrift,
|
||||
DetectProfiles, DismissEmbedderSuggestion, FirstRunState, GetMemory, GetProjectPermissions,
|
||||
GetProjectWorkState, GitBranches, GitCheckout, GitCommit, GitGraph, GitInit, GitLog, GitStage,
|
||||
GitStatus, GitUnstage, HealthUseCase, InspectConversation, LaunchAgent, LaunchAgentInput,
|
||||
ListAgents, ListAgentsInput, ListEmbedderProfiles, ListLayouts, ListMemories, ListProfiles,
|
||||
ListProjects, ListResumableAgents, ListSkills, ListTemplates, LiveAgentRegistry, LiveSessions,
|
||||
LoadLayout, McpRuntime, MoveTabToNewWindow, MutateLayout, OnnxModelView, OpenProject,
|
||||
OpenTerminal, OrchestratorService, PermissionProjectorRegistry, ProposeContext,
|
||||
ReadAgentContext, ReadContext, ReadMemory, ReadMemoryIndex, ReadProjectContext, RecallMemory,
|
||||
ReconcileLayouts, RecordTurn, RecordTurnProvider, ReferenceProfiles, RenameLayout,
|
||||
ResizeTerminal, ResolveAgentPermissions, ResolveMemoryLinks, SaveEmbedderProfile, SaveProfile,
|
||||
SessionLimitService, SetActiveLayout, SnapshotRunningAgents, StructuredSessions,
|
||||
AgentResumer, AppError, AssignSkillToAgent, AttachLiveAgent, ChangeAgentProfile,
|
||||
CheckEmbedderSuggestion, CloseProject, CloseTab, CloseTerminal, ConfigureProfiles,
|
||||
ContextGuardUseCases, CreateAgentFromScratch, CreateAgentFromTemplate, CreateLayout,
|
||||
CreateMemory, CreateProject, CreateSkill, CreateTemplate, DeleteAgent, DeleteEmbedderProfile,
|
||||
DeleteLayout, DeleteMemory, DeleteProfile, DeleteSkill, DeleteTemplate,
|
||||
DescribeEmbedderEngines, DetectAgentDrift, DetectProfiles, DismissEmbedderSuggestion,
|
||||
FirstRunState, GetMemory, GetProjectPermissions, GetProjectWorkState, GitBranches, GitCheckout,
|
||||
GitCommit, GitGraph, GitInit, GitLog, GitStage, GitStatus, GitUnstage, HealthUseCase,
|
||||
InspectConversation, LaunchAgent, LaunchAgentInput, ListAgents, ListAgentsInput,
|
||||
ListEmbedderProfiles, ListLayouts, ListMemories, ListProfiles, ListProjects,
|
||||
ListResumableAgents, ListSkills, ListTemplates, LiveAgentRegistry, LiveSessions, LoadLayout,
|
||||
McpRuntime, MoveTabToNewWindow, MutateLayout, OnnxModelView, OpenProject, OpenTerminal,
|
||||
OrchestratorService, PermissionProjectorRegistry, ProposeContext, ReadAgentContext,
|
||||
ReadContext, ReadMemory, ReadMemoryIndex, ReadProjectContext, RecallMemory, ReconcileLayouts,
|
||||
RecordTurn, RecordTurnProvider, ReferenceProfiles, RenameLayout, ResizeTerminal,
|
||||
ResolveAgentPermissions, ResolveMemoryLinks, SaveEmbedderProfile, SaveProfile,
|
||||
SessionLimitService, SetActiveLayout, SnapshotRunningAgents, StopLiveAgent, StructuredSessions,
|
||||
SuggestedThisSession, SyncAgentWithTemplate, TerminalSessions, UnassignSkillFromAgent,
|
||||
UpdateAgentContext, UpdateAgentPermissions, UpdateMemory, UpdateProjectContext,
|
||||
UpdateProjectPermissions, UpdateSkill, UpdateTemplate, WriteMemory, WriteToTerminal,
|
||||
@ -351,6 +352,10 @@ pub struct AppState {
|
||||
pub list_resumable_agents: Arc<ListResumableAgents>,
|
||||
/// Read-only live/busy state for the project's manifest agents.
|
||||
pub get_project_work_state: Arc<GetProjectWorkState>,
|
||||
/// Rebinds an already-running agent's live session to a visible cell (Lot D).
|
||||
pub attach_live_agent: Arc<AttachLiveAgent>,
|
||||
/// Tears down an already-running agent's live session by agent id (Lot D).
|
||||
pub stop_live_agent: Arc<StopLiveAgent>,
|
||||
/// Best-effort inspection of a conversation (last topic + token indicator)
|
||||
/// for the resume popup (T7). Optional/extensible: backed by a `Vec` of
|
||||
/// [`domain::ports::SessionInspector`]s; an empty/missing match yields empty
|
||||
@ -1066,6 +1071,14 @@ impl AppState {
|
||||
as Arc<dyn application::ConversationLogProvider>,
|
||||
),
|
||||
);
|
||||
// Lot D — actions contrôlées agent-level sur les sessions vivantes : attach
|
||||
// (rebind de la cellule-vue, zéro spawn) et stop (kill PTY via la primitive
|
||||
// `CloseTerminal` existante / shutdown structuré). Aucune création de session.
|
||||
let attach_live_agent = Arc::new(AttachLiveAgent::new(Arc::clone(&live_sessions)));
|
||||
let stop_live_agent = Arc::new(StopLiveAgent::new(
|
||||
Arc::clone(&live_sessions),
|
||||
Arc::clone(&close_terminal),
|
||||
));
|
||||
|
||||
// --- Limites de session des agents (ARCHITECTURE §21, LS7) ---
|
||||
// Service pur-ports « détecter → planifier → reprendre » câblé sur l'existant :
|
||||
@ -1233,6 +1246,8 @@ impl AppState {
|
||||
change_agent_profile,
|
||||
list_resumable_agents,
|
||||
get_project_work_state,
|
||||
attach_live_agent,
|
||||
stop_live_agent,
|
||||
inspect_conversation,
|
||||
project_store,
|
||||
get_project_permissions,
|
||||
|
||||
Reference in New Issue
Block a user