summaryrefslogtreecommitdiff
path: root/makima/src/daemon/task/manager.rs
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-01-19 02:26:09 +0000
committersoryu <soryu@soryu.co>2026-01-19 02:26:09 +0000
commit786510379bed060db2b3742b7dfca671552d2c34 (patch)
tree432d0b575a64ef0fb2cb10a86cd916b5fbc16909 /makima/src/daemon/task/manager.rs
parentb64eddc8c2f250cdcbacae18cce107bf4c86f9f4 (diff)
downloadsoryu-786510379bed060db2b3742b7dfca671552d2c34.tar.gz
soryu-786510379bed060db2b3742b7dfca671552d2c34.zip
Make sure tasks can continue
Diffstat (limited to 'makima/src/daemon/task/manager.rs')
-rw-r--r--makima/src/daemon/task/manager.rs108
1 files changed, 96 insertions, 12 deletions
diff --git a/makima/src/daemon/task/manager.rs b/makima/src/daemon/task/manager.rs
index 029d026..80b7039 100644
--- a/makima/src/daemon/task/manager.rs
+++ b/makima/src/daemon/task/manager.rs
@@ -1171,6 +1171,8 @@ impl TaskManager {
contract_id,
is_supervisor,
autonomous_loop,
+ resume_session,
+ conversation_history,
} => {
tracing::info!(
task_id = %task_id,
@@ -1183,6 +1185,7 @@ impl TaskManager {
is_orchestrator = is_orchestrator,
is_supervisor = is_supervisor,
autonomous_loop = autonomous_loop,
+ resume_session = resume_session,
target_repo_path = ?target_repo_path,
completion_action = ?completion_action,
continue_from_task_id = ?continue_from_task_id,
@@ -1195,7 +1198,8 @@ impl TaskManager {
task_id, task_name, plan, repo_url, base_branch, target_branch,
parent_task_id, depth, is_orchestrator, is_supervisor,
target_repo_path, completion_action, continue_from_task_id,
- copy_files, contract_id, autonomous_loop
+ copy_files, contract_id, autonomous_loop, resume_session,
+ conversation_history,
).await?;
}
DaemonCommand::PauseTask { task_id } => {
@@ -1275,6 +1279,8 @@ impl TaskManager {
None, // copy_files
contract_id,
false, // autonomous_loop - supervisors don't use this
+ false, // resume_session - respawning from scratch
+ None, // conversation_history - not needed for fresh respawn
).await {
tracing::error!(
task_id = %task_id,
@@ -1490,6 +1496,8 @@ impl TaskManager {
copy_files: Option<Vec<String>>,
contract_id: Option<Uuid>,
autonomous_loop: bool,
+ resume_session: bool,
+ conversation_history: Option<serde_json::Value>,
) -> TaskResult<()> {
tracing::info!(task_id = %task_id, is_orchestrator = is_orchestrator, is_supervisor = is_supervisor, depth = depth, "=== SPAWN_TASK START ===");
@@ -1566,7 +1574,8 @@ impl TaskManager {
if let Err(e) = inner.run_task(
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
+ continue_from_task_id, copy_files, contract_id, autonomous_loop, resume_session,
+ conversation_history,
).await {
tracing::error!(task_id = %task_id, error = %e, "Task execution failed");
inner.mark_failed(task_id, &e.to_string()).await;
@@ -2909,11 +2918,39 @@ impl TaskManagerInner {
copy_files: Option<Vec<String>>,
contract_id: Option<Uuid>,
autonomous_loop: bool,
+ resume_session: bool,
+ conversation_history: Option<serde_json::Value>,
) -> Result<(), DaemonError> {
- tracing::info!(task_id = %task_id, is_orchestrator = is_orchestrator, is_supervisor = is_supervisor, "=== RUN_TASK START ===");
+ tracing::info!(task_id = %task_id, is_orchestrator = is_orchestrator, is_supervisor = is_supervisor, resume_session = resume_session, "=== RUN_TASK START ===");
+
+ // If resuming session, try to find existing worktree first
+ let existing_worktree = if resume_session {
+ match self.find_worktree_for_task(task_id).await {
+ Ok(path) => {
+ tracing::info!(task_id = %task_id, path = %path.display(), "Found existing worktree for session resume");
+ Some(path)
+ }
+ Err(e) => {
+ tracing::warn!(task_id = %task_id, error = %e, "No existing worktree found for resume, will create new");
+ None
+ }
+ }
+ } else {
+ None
+ };
// Determine working directory
- let working_dir = if let Some(ref source) = repo_source {
+ let has_existing_worktree = existing_worktree.is_some();
+ let working_dir = if let Some(existing) = existing_worktree {
+ // Reuse existing worktree for session resume
+ let msg = DaemonMessage::task_output(
+ task_id,
+ format!("Resuming session in existing worktree: {}\n", existing.display()),
+ false,
+ );
+ let _ = self.ws_tx.send(msg).await;
+ existing
+ } else if let Some(ref source) = repo_source {
if is_new_repo_request(source) {
// Explicit new repo request: new:// or new://project-name
tracing::info!(
@@ -3388,14 +3425,61 @@ impl TaskManagerInner {
// Clone extra_env for use in autonomous loop iterations
let extra_env_for_loop = extra_env.clone();
- tracing::debug!(task_id = %task_id, has_system_prompt = system_prompt.is_some(), "Calling process_manager.spawn()...");
- let mut process = self.process_manager
- .spawn_with_system_prompt(&working_dir, &full_plan, extra_env, system_prompt.as_deref())
- .await
- .map_err(|e| {
- tracing::error!(task_id = %task_id, error = %e, "Failed to spawn Claude process");
- DaemonError::Task(TaskError::SetupFailed(e.to_string()))
- })?;
+ tracing::debug!(task_id = %task_id, has_system_prompt = system_prompt.is_some(), resume_session = resume_session, "Calling process_manager.spawn()...");
+ let mut process = if resume_session {
+ // Use --continue flag to resume from previous session
+ // Build continuation prompt based on whether worktree exists
+ let continuation_prompt = if has_existing_worktree {
+ // Worktree exists: Claude's session state should work
+ format!(
+ "Resuming previous session. Continue from where you left off.\n\n{}",
+ full_plan
+ )
+ } else if let Some(ref history) = conversation_history {
+ // Worktree missing: inject conversation history as context
+ let history_str = serde_json::to_string_pretty(history).unwrap_or_default();
+ format!(
+ "Resuming previous session. Here is the conversation history from the previous session:\n\n\
+ <previous_conversation>\n{}\n</previous_conversation>\n\n\
+ Continue from where you left off with this task:\n\n{}",
+ history_str,
+ full_plan
+ )
+ } else {
+ // No history available: just the plan
+ format!("Resuming with plan:\n\n{}", full_plan)
+ };
+
+ let resume_msg = if has_existing_worktree {
+ "Using --continue to resume previous conversation...\n"
+ } else if conversation_history.is_some() {
+ "Worktree not found. Resuming with injected conversation history...\n"
+ } else {
+ "Resuming without conversation history (worktree not found)...\n"
+ };
+ let msg = DaemonMessage::task_output(
+ task_id,
+ resume_msg.to_string(),
+ false,
+ );
+ let _ = self.ws_tx.send(msg).await;
+
+ self.process_manager
+ .spawn_continue(&working_dir, &continuation_prompt, extra_env, system_prompt.as_deref())
+ .await
+ .map_err(|e| {
+ tracing::error!(task_id = %task_id, error = %e, "Failed to spawn Claude process with --continue");
+ DaemonError::Task(TaskError::SetupFailed(e.to_string()))
+ })?
+ } else {
+ self.process_manager
+ .spawn_with_system_prompt(&working_dir, &full_plan, extra_env, system_prompt.as_deref())
+ .await
+ .map_err(|e| {
+ tracing::error!(task_id = %task_id, error = %e, "Failed to spawn Claude process");
+ DaemonError::Task(TaskError::SetupFailed(e.to_string()))
+ })?
+ };
// Register the process PID for graceful shutdown tracking
if let Some(pid) = process.id() {