summaryrefslogtreecommitdiff
path: root/makima/src/daemon/task
diff options
context:
space:
mode:
Diffstat (limited to 'makima/src/daemon/task')
-rw-r--r--makima/src/daemon/task/manager.rs106
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,