summaryrefslogtreecommitdiff
path: root/makima/src/server/handlers/mesh_chat.rs
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-01-11 05:52:14 +0000
committersoryu <soryu@soryu.co>2026-01-15 00:21:16 +0000
commit87044a747b47bd83249d61a45842c7f7b2eae56d (patch)
treeef2000ce79ffcc2723ef841acef5aa1deb1d5378 /makima/src/server/handlers/mesh_chat.rs
parent077820c4167c168072d217a1b01df840463a12a8 (diff)
downloadsoryu-87044a747b47bd83249d61a45842c7f7b2eae56d.tar.gz
soryu-87044a747b47bd83249d61a45842c7f7b2eae56d.zip
Contract system
Diffstat (limited to 'makima/src/server/handlers/mesh_chat.rs')
-rw-r--r--makima/src/server/handlers/mesh_chat.rs124
1 files changed, 124 insertions, 0 deletions
diff --git a/makima/src/server/handlers/mesh_chat.rs b/makima/src/server/handlers/mesh_chat.rs
index 5d6d2ee..3f650bc 100644
--- a/makima/src/server/handlers/mesh_chat.rs
+++ b/makima/src/server/handlers/mesh_chat.rs
@@ -930,6 +930,46 @@ async fn handle_mesh_request(
merge_mode,
priority,
} => {
+ // Subtasks inherit contract_id from parent task
+ let contract_id = if let Some(parent_id) = parent_task_id {
+ match repository::get_task(pool, parent_id).await {
+ Ok(Some(parent_task)) => {
+ match parent_task.contract_id {
+ Some(cid) => cid,
+ None => {
+ return MeshRequestResult {
+ success: false,
+ message: "Parent task has no contract_id".to_string(),
+ data: None,
+ };
+ }
+ }
+ }
+ Ok(None) => {
+ return MeshRequestResult {
+ success: false,
+ message: format!("Parent task {} not found", parent_id),
+ data: None,
+ };
+ }
+ Err(e) => {
+ return MeshRequestResult {
+ success: false,
+ message: format!("Failed to look up parent task: {}", e),
+ data: None,
+ };
+ }
+ }
+ } else {
+ // Root tasks created via LLM chat require a contract_id
+ // TODO: Add contract_id to create_task tool definition
+ return MeshRequestResult {
+ success: false,
+ message: "Cannot create root task without contract_id. Use parent_task_id to create subtasks.".to_string(),
+ data: None,
+ };
+ };
+
// Check if repository_url matches a daemon's working directory (for this owner)
let is_daemon_working_dir = repository_url.as_ref().map(|url| {
state.daemon_connections.iter().any(|entry| {
@@ -962,6 +1002,7 @@ async fn handle_mesh_request(
};
let create_req = CreateTaskRequest {
+ contract_id,
name: name.clone(),
description: None,
plan,
@@ -975,6 +1016,8 @@ async fn handle_mesh_request(
completion_action,
continue_from_task_id: None,
copy_files: None,
+ is_supervisor: false,
+ checkpoint_sha: None,
};
match repository::create_task_for_owner(pool, owner_id, create_req).await {
@@ -1074,6 +1117,8 @@ async fn handle_mesh_request(
completion_action: task.completion_action.clone(),
continue_from_task_id: task.continue_from_task_id,
copy_files: task.copy_files.as_ref().and_then(|v| serde_json::from_value(v.clone()).ok()),
+ contract_id: task.contract_id,
+ is_supervisor: task.is_supervisor,
};
match state.send_daemon_command(target_daemon_id, command).await {
@@ -1610,6 +1655,9 @@ async fn handle_mesh_request(
crate::db::models::BodyElement::Image { src, alt, caption } => {
json!({ "type": "image", "src": src, "alt": alt, "caption": caption })
}
+ crate::db::models::BodyElement::Markdown { content } => {
+ json!({ "type": "markdown", "content": content })
+ }
}
})
.collect();
@@ -1640,6 +1688,9 @@ async fn handle_mesh_request(
}).collect();
Some(list_text.join("\n"))
}
+ crate::db::models::BodyElement::Markdown { content } => {
+ Some(content.clone())
+ }
_ => None,
}
})
@@ -1976,6 +2027,79 @@ async fn handle_mesh_request(
},
}
}
+
+ // Supervisor-only tools - these should be handled via the supervisor.sh script,
+ // not through the mesh chat. Return an informative error.
+ MeshToolRequest::GetAllContractTasks { contract_id } => {
+ MeshRequestResult {
+ success: false,
+ message: format!(
+ "get_all_contract_tasks is a supervisor-only tool. Use supervisor.sh to access this functionality. Contract: {}",
+ contract_id
+ ),
+ data: None,
+ }
+ }
+ MeshToolRequest::WaitForTaskCompletion { task_id, timeout_seconds } => {
+ MeshRequestResult {
+ success: false,
+ message: format!(
+ "wait_for_task_completion is a supervisor-only tool. Use supervisor.sh to access this functionality. Task: {}, Timeout: {}s",
+ task_id, timeout_seconds
+ ),
+ data: None,
+ }
+ }
+ MeshToolRequest::ReadTaskWorktree { task_id, file_path } => {
+ MeshRequestResult {
+ success: false,
+ message: format!(
+ "read_task_worktree is a supervisor-only tool. Use supervisor.sh to access this functionality. Task: {}, Path: {}",
+ task_id, file_path
+ ),
+ data: None,
+ }
+ }
+ MeshToolRequest::SpawnTask { name, plan, parent_task_id, checkpoint_sha, .. } => {
+ MeshRequestResult {
+ success: false,
+ message: format!(
+ "spawn_task is a supervisor-only tool. Only the contract supervisor can spawn new tasks. Task name: {}",
+ name
+ ),
+ data: None,
+ }
+ }
+ MeshToolRequest::CreateCheckpoint { task_id, message } => {
+ MeshRequestResult {
+ success: false,
+ message: format!(
+ "create_checkpoint is a supervisor-only tool. Use supervisor.sh to access this functionality. Task: {}, Message: {}",
+ task_id, message
+ ),
+ data: None,
+ }
+ }
+ MeshToolRequest::ListTaskCheckpoints { task_id } => {
+ MeshRequestResult {
+ success: false,
+ message: format!(
+ "list_task_checkpoints is a supervisor-only tool. Use supervisor.sh to access this functionality. Task: {}",
+ task_id
+ ),
+ data: None,
+ }
+ }
+ MeshToolRequest::GetTaskTree { task_id } => {
+ MeshRequestResult {
+ success: false,
+ message: format!(
+ "get_task_tree is a supervisor-only tool. Use supervisor.sh to access this functionality. Task: {}",
+ task_id
+ ),
+ data: None,
+ }
+ }
}
}