diff options
Diffstat (limited to 'makima/src/daemon/task/manager.rs')
| -rw-r--r-- | makima/src/daemon/task/manager.rs | 106 |
1 files changed, 102 insertions, 4 deletions
diff --git a/makima/src/daemon/task/manager.rs b/makima/src/daemon/task/manager.rs index 8abff3f..bbcf661 100644 --- a/makima/src/daemon/task/manager.rs +++ b/makima/src/daemon/task/manager.rs @@ -995,6 +995,8 @@ pub struct ManagedTask { pub concurrency_key: Uuid, /// Whether to run in autonomous loop mode. pub autonomous_loop: bool, + /// Whether the contract is in local-only mode (skips automatic completion actions). + pub local_only: bool, /// Time task was created. pub created_at: Instant, /// Time task started running. @@ -1692,6 +1694,7 @@ impl TaskManager { conversation_history, patch_data, patch_base_sha, + local_only, } => { tracing::info!( task_id = %task_id, @@ -1718,7 +1721,7 @@ impl TaskManager { parent_task_id, depth, is_orchestrator, is_supervisor, target_repo_path, completion_action, continue_from_task_id, copy_files, contract_id, autonomous_loop, resume_session, - conversation_history, patch_data, patch_base_sha, + conversation_history, patch_data, patch_base_sha, local_only, ).await?; } DaemonCommand::PauseTask { task_id } => { @@ -1796,6 +1799,7 @@ impl TaskManager { let target_repo_path = task.target_repo_path.clone(); let completion_action = task.completion_action.clone(); let contract_id = task.contract_id; + let local_only = task.local_only; // Spawn in background to not block the command handler tokio::spawn(async move { @@ -1818,6 +1822,7 @@ impl TaskManager { None, // conversation_history - not needed for fresh respawn None, // patch_data - not available for respawn None, // patch_base_sha - not available for respawn + local_only, ).await { tracing::error!( task_id = %task_id, @@ -2009,6 +2014,10 @@ impl TaskManager { tracing::info!(source_dir = ?source_dir, "Inheriting git config"); self.handle_inherit_git_config(source_dir).await?; } + DaemonCommand::CreateExportPatch { task_id, base_sha } => { + tracing::info!(task_id = %task_id, base_sha = ?base_sha, "Creating export patch"); + self.handle_create_export_patch(task_id, base_sha).await?; + } DaemonCommand::RestartDaemon => { tracing::info!("Received restart command from server, initiating daemon restart..."); // Shutdown all running tasks gracefully @@ -2046,6 +2055,7 @@ impl TaskManager { conversation_history: Option<serde_json::Value>, patch_data: Option<String>, patch_base_sha: Option<String>, + local_only: bool, ) -> TaskResult<()> { tracing::info!(task_id = %task_id, is_orchestrator = is_orchestrator, is_supervisor = is_supervisor, depth = depth, patch_available = patch_data.is_some(), "=== SPAWN_TASK START ==="); @@ -2096,6 +2106,7 @@ impl TaskManager { contract_id, concurrency_key, autonomous_loop, + local_only, created_at: Instant::now(), started_at: None, completed_at: None, @@ -2122,7 +2133,7 @@ impl TaskManager { task_id, task_name, plan, repo_url, base_branch, target_branch, is_orchestrator, is_supervisor, target_repo_path, completion_action, continue_from_task_id, copy_files, contract_id, autonomous_loop, resume_session, - conversation_history, patch_data, patch_base_sha, + conversation_history, patch_data, patch_base_sha, local_only, ).await { tracing::error!(task_id = %task_id, error = %e, "Task execution failed"); inner.mark_failed(task_id, &e.to_string()).await; @@ -2729,6 +2740,86 @@ impl TaskManager { Ok(()) } + /// Handle CreateExportPatch command. + /// + /// Creates an uncompressed, human-readable git patch for export. + async fn handle_create_export_patch( + &self, + task_id: Uuid, + base_sha: Option<String>, + ) -> Result<(), DaemonError> { + // Get task's worktree path + let worktree_result = self.get_task_worktree_path(task_id).await; + + let msg = match worktree_result { + Ok(worktree_path) => { + // Create the export patch + match storage::create_export_patch(&worktree_path, base_sha.as_deref()).await { + Ok(result) => { + tracing::info!( + task_id = %task_id, + files_count = result.files_count, + lines_added = result.lines_added, + lines_removed = result.lines_removed, + base_commit_sha = %result.base_commit_sha, + "Export patch created successfully" + ); + + DaemonMessage::ExportPatchCreated { + task_id, + success: true, + patch_content: Some(result.patch_content), + files_count: Some(result.files_count), + lines_added: Some(result.lines_added), + lines_removed: Some(result.lines_removed), + base_commit_sha: Some(result.base_commit_sha), + error: None, + } + } + Err(e) => { + tracing::warn!( + task_id = %task_id, + error = %e, + "Failed to create export patch" + ); + + DaemonMessage::ExportPatchCreated { + task_id, + success: false, + patch_content: None, + files_count: None, + lines_added: None, + lines_removed: None, + base_commit_sha: None, + error: Some(e.to_string()), + } + } + } + } + Err(e) => { + tracing::warn!( + task_id = %task_id, + error = %e, + "Failed to get worktree path for export patch" + ); + + DaemonMessage::ExportPatchCreated { + task_id, + success: false, + patch_content: None, + files_count: None, + lines_added: None, + lines_removed: None, + base_commit_sha: None, + error: Some(format!("Task not found or has no worktree: {}", e)), + } + } + }; + + let _ = self.ws_tx.send(msg).await; + Ok(()) + } + /// Handle ReadRepoFile command. /// /// Reads a file from a repository on the daemon's filesystem and sends @@ -3570,6 +3661,7 @@ impl TaskManagerInner { conversation_history: Option<serde_json::Value>, patch_data: Option<String>, patch_base_sha: Option<String>, + local_only: bool, ) -> Result<(), DaemonError> { tracing::info!(task_id = %task_id, is_orchestrator = is_orchestrator, is_supervisor = is_supervisor, resume_session = resume_session, has_patch = patch_data.is_some(), "=== RUN_TASK START ==="); @@ -4704,9 +4796,15 @@ impl TaskManagerInner { } } - // Execute completion action if task succeeded + // Execute completion action if task succeeded (skip in local_only mode) let completion_result = if success { - if let Some(ref action) = completion_action { + if local_only { + tracing::info!( + task_id = %task_id, + "Skipping completion action - contract is in local_only mode" + ); + Ok(None) + } else if let Some(ref action) = completion_action { if action != "none" { self.execute_completion_action( task_id, |
