2025-12-12 12:33:17 -03:00
|
|
|
//! Auto Task API Handlers
|
|
|
|
|
//!
|
|
|
|
|
//! This module provides the HTTP API endpoints for the Auto Task system,
|
|
|
|
|
//! enabling the UI to interact with the Intent Compiler, execution engine,
|
|
|
|
|
//! safety layer, and MCP client.
|
|
|
|
|
|
|
|
|
|
use crate::basic::keywords::auto_task::{
|
|
|
|
|
AutoTask, AutoTaskStatus, ExecutionMode, PendingApproval, PendingDecision, TaskPriority,
|
|
|
|
|
};
|
Fix truncated files, implement TODOs, remove all compilation errors and warnings
- Complete truncated auto_task.rs, autotask_api.rs, intent_compiler.rs, mcp_client.rs, safety_layer.rs
- Add missing structs: RiskFactor, ResourceUsage, TaskError, RollbackState, TaskSchedule, HealthStatus
- Add missing IntentCompiler methods: call_llm, assess_risks, estimate_resources, check_ambiguity, store_compiled_intent
- Implement SET ATTENDANT STATUS database storage
- Implement queue position calculation in transfer_to_human
- Add llm_tokens to ResourceEstimate
- Fix all unused imports and variables
- Add proper derives (Copy, PartialOrd) where needed
2025-12-12 17:33:11 -03:00
|
|
|
use crate::basic::keywords::intent_compiler::IntentCompiler;
|
2025-12-12 12:33:17 -03:00
|
|
|
use crate::basic::keywords::safety_layer::{SafetyLayer, SimulationResult};
|
|
|
|
|
use crate::shared::state::AppState;
|
|
|
|
|
use axum::{
|
|
|
|
|
extract::{Path, Query, State},
|
|
|
|
|
http::StatusCode,
|
|
|
|
|
response::IntoResponse,
|
Fix truncated files, implement TODOs, remove all compilation errors and warnings
- Complete truncated auto_task.rs, autotask_api.rs, intent_compiler.rs, mcp_client.rs, safety_layer.rs
- Add missing structs: RiskFactor, ResourceUsage, TaskError, RollbackState, TaskSchedule, HealthStatus
- Add missing IntentCompiler methods: call_llm, assess_risks, estimate_resources, check_ambiguity, store_compiled_intent
- Implement SET ATTENDANT STATUS database storage
- Implement queue position calculation in transfer_to_human
- Add llm_tokens to ResourceEstimate
- Fix all unused imports and variables
- Add proper derives (Copy, PartialOrd) where needed
2025-12-12 17:33:11 -03:00
|
|
|
Json,
|
2025-12-12 12:33:17 -03:00
|
|
|
};
|
Fix truncated files, implement TODOs, remove all compilation errors and warnings
- Complete truncated auto_task.rs, autotask_api.rs, intent_compiler.rs, mcp_client.rs, safety_layer.rs
- Add missing structs: RiskFactor, ResourceUsage, TaskError, RollbackState, TaskSchedule, HealthStatus
- Add missing IntentCompiler methods: call_llm, assess_risks, estimate_resources, check_ambiguity, store_compiled_intent
- Implement SET ATTENDANT STATUS database storage
- Implement queue position calculation in transfer_to_human
- Add llm_tokens to ResourceEstimate
- Fix all unused imports and variables
- Add proper derives (Copy, PartialOrd) where needed
2025-12-12 17:33:11 -03:00
|
|
|
use chrono::Utc;
|
2025-12-12 12:33:17 -03:00
|
|
|
use log::{error, info, trace};
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
use uuid::Uuid;
|
|
|
|
|
|
|
|
|
|
// REQUEST/RESPONSE TYPES
|
|
|
|
|
|
|
|
|
|
/// Request to compile an intent into an executable plan
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
|
pub struct CompileIntentRequest {
|
|
|
|
|
pub intent: String,
|
|
|
|
|
pub execution_mode: Option<String>,
|
|
|
|
|
pub priority: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Response from intent compilation
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
|
pub struct CompileIntentResponse {
|
|
|
|
|
pub success: bool,
|
|
|
|
|
pub plan_id: Option<String>,
|
|
|
|
|
pub plan_name: Option<String>,
|
|
|
|
|
pub plan_description: Option<String>,
|
|
|
|
|
pub steps: Vec<PlanStepResponse>,
|
|
|
|
|
pub alternatives: Vec<AlternativeResponse>,
|
|
|
|
|
pub confidence: f64,
|
|
|
|
|
pub risk_level: String,
|
|
|
|
|
pub estimated_duration_minutes: i32,
|
|
|
|
|
pub estimated_cost: f64,
|
|
|
|
|
pub resource_estimate: ResourceEstimateResponse,
|
|
|
|
|
pub basic_program: Option<String>,
|
|
|
|
|
pub requires_approval: bool,
|
|
|
|
|
pub mcp_servers: Vec<String>,
|
|
|
|
|
pub external_apis: Vec<String>,
|
|
|
|
|
pub risks: Vec<RiskResponse>,
|
|
|
|
|
pub error: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
|
pub struct PlanStepResponse {
|
|
|
|
|
pub id: String,
|
|
|
|
|
pub order: i32,
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub description: String,
|
|
|
|
|
pub keywords: Vec<String>,
|
|
|
|
|
pub priority: String,
|
|
|
|
|
pub risk_level: String,
|
|
|
|
|
pub estimated_minutes: i32,
|
|
|
|
|
pub requires_approval: bool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
|
pub struct AlternativeResponse {
|
|
|
|
|
pub id: String,
|
|
|
|
|
pub description: String,
|
|
|
|
|
pub confidence: f64,
|
|
|
|
|
pub pros: Vec<String>,
|
|
|
|
|
pub cons: Vec<String>,
|
|
|
|
|
pub estimated_cost: Option<f64>,
|
|
|
|
|
pub estimated_time_hours: Option<f64>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
|
pub struct ResourceEstimateResponse {
|
|
|
|
|
pub compute_hours: f64,
|
|
|
|
|
pub storage_gb: f64,
|
|
|
|
|
pub api_calls: i32,
|
|
|
|
|
pub llm_tokens: i32,
|
|
|
|
|
pub estimated_cost_usd: f64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
|
pub struct RiskResponse {
|
|
|
|
|
pub id: String,
|
|
|
|
|
pub category: String,
|
|
|
|
|
pub description: String,
|
|
|
|
|
pub probability: f64,
|
|
|
|
|
pub impact: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Request to execute a compiled plan
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
|
pub struct ExecutePlanRequest {
|
|
|
|
|
pub plan_id: String,
|
|
|
|
|
pub execution_mode: Option<String>,
|
|
|
|
|
pub priority: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Response from plan execution
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
|
pub struct ExecutePlanResponse {
|
|
|
|
|
pub success: bool,
|
|
|
|
|
pub task_id: Option<String>,
|
|
|
|
|
pub status: Option<String>,
|
|
|
|
|
pub error: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Query parameters for listing tasks
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
|
pub struct ListTasksQuery {
|
|
|
|
|
pub filter: Option<String>,
|
|
|
|
|
pub status: Option<String>,
|
|
|
|
|
pub priority: Option<String>,
|
|
|
|
|
pub limit: Option<i32>,
|
|
|
|
|
pub offset: Option<i32>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Auto task stats response
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
|
pub struct AutoTaskStatsResponse {
|
|
|
|
|
pub total: i32,
|
|
|
|
|
pub running: i32,
|
|
|
|
|
pub pending: i32,
|
|
|
|
|
pub completed: i32,
|
|
|
|
|
pub failed: i32,
|
|
|
|
|
pub pending_approval: i32,
|
|
|
|
|
pub pending_decision: i32,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Task action response
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
|
pub struct TaskActionResponse {
|
|
|
|
|
pub success: bool,
|
|
|
|
|
pub message: Option<String>,
|
|
|
|
|
pub error: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Decision submission request
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
|
pub struct DecisionRequest {
|
|
|
|
|
pub decision_id: String,
|
|
|
|
|
pub option_id: Option<String>,
|
|
|
|
|
pub skip: Option<bool>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Approval action request
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
|
pub struct ApprovalRequest {
|
|
|
|
|
pub approval_id: String,
|
|
|
|
|
pub action: String, // "approve", "reject", "defer"
|
|
|
|
|
pub comment: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Simulation response
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
|
pub struct SimulationResponse {
|
|
|
|
|
pub success: bool,
|
|
|
|
|
pub confidence: f64,
|
|
|
|
|
pub risk_score: f64,
|
|
|
|
|
pub risk_level: String,
|
|
|
|
|
pub step_outcomes: Vec<StepOutcomeResponse>,
|
|
|
|
|
pub impact: ImpactResponse,
|
|
|
|
|
pub side_effects: Vec<SideEffectResponse>,
|
|
|
|
|
pub recommendations: Vec<RecommendationResponse>,
|
|
|
|
|
pub error: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
|
pub struct StepOutcomeResponse {
|
|
|
|
|
pub step_id: String,
|
|
|
|
|
pub step_name: String,
|
|
|
|
|
pub would_succeed: bool,
|
|
|
|
|
pub success_probability: f64,
|
|
|
|
|
pub failure_modes: Vec<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
|
pub struct ImpactResponse {
|
|
|
|
|
pub risk_score: f64,
|
|
|
|
|
pub risk_level: String,
|
|
|
|
|
pub data_impact: DataImpactResponse,
|
|
|
|
|
pub cost_impact: CostImpactResponse,
|
|
|
|
|
pub time_impact: TimeImpactResponse,
|
|
|
|
|
pub security_impact: SecurityImpactResponse,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
|
pub struct DataImpactResponse {
|
|
|
|
|
pub records_created: i32,
|
|
|
|
|
pub records_modified: i32,
|
|
|
|
|
pub records_deleted: i32,
|
|
|
|
|
pub tables_affected: Vec<String>,
|
|
|
|
|
pub reversible: bool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
|
pub struct CostImpactResponse {
|
|
|
|
|
pub api_costs: f64,
|
|
|
|
|
pub compute_costs: f64,
|
|
|
|
|
pub storage_costs: f64,
|
|
|
|
|
pub total_estimated_cost: f64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
|
pub struct TimeImpactResponse {
|
|
|
|
|
pub estimated_duration_seconds: i32,
|
|
|
|
|
pub blocking: bool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
|
pub struct SecurityImpactResponse {
|
|
|
|
|
pub risk_level: String,
|
|
|
|
|
pub credentials_accessed: Vec<String>,
|
|
|
|
|
pub external_systems: Vec<String>,
|
|
|
|
|
pub concerns: Vec<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
|
pub struct SideEffectResponse {
|
|
|
|
|
pub effect_type: String,
|
|
|
|
|
pub description: String,
|
|
|
|
|
pub severity: String,
|
|
|
|
|
pub mitigation: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
|
pub struct RecommendationResponse {
|
|
|
|
|
pub id: String,
|
|
|
|
|
pub recommendation_type: String,
|
|
|
|
|
pub description: String,
|
|
|
|
|
pub action: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// API HANDLERS
|
|
|
|
|
|
|
|
|
|
/// POST /api/autotask/compile - Compile an intent into an execution plan
|
|
|
|
|
pub async fn compile_intent_handler(
|
|
|
|
|
State(state): State<Arc<AppState>>,
|
|
|
|
|
Json(request): Json<CompileIntentRequest>,
|
|
|
|
|
) -> impl IntoResponse {
|
Fix truncated files, implement TODOs, remove all compilation errors and warnings
- Complete truncated auto_task.rs, autotask_api.rs, intent_compiler.rs, mcp_client.rs, safety_layer.rs
- Add missing structs: RiskFactor, ResourceUsage, TaskError, RollbackState, TaskSchedule, HealthStatus
- Add missing IntentCompiler methods: call_llm, assess_risks, estimate_resources, check_ambiguity, store_compiled_intent
- Implement SET ATTENDANT STATUS database storage
- Implement queue position calculation in transfer_to_human
- Add llm_tokens to ResourceEstimate
- Fix all unused imports and variables
- Add proper derives (Copy, PartialOrd) where needed
2025-12-12 17:33:11 -03:00
|
|
|
info!(
|
|
|
|
|
"Compiling intent: {}",
|
|
|
|
|
&request.intent[..request.intent.len().min(100)]
|
|
|
|
|
);
|
2025-12-12 12:33:17 -03:00
|
|
|
|
|
|
|
|
// Get session from state (in real implementation, extract from auth)
|
|
|
|
|
let session = match get_current_session(&state).await {
|
|
|
|
|
Ok(s) => s,
|
|
|
|
|
Err(e) => {
|
|
|
|
|
return (
|
|
|
|
|
StatusCode::UNAUTHORIZED,
|
|
|
|
|
Json(CompileIntentResponse {
|
|
|
|
|
success: false,
|
|
|
|
|
plan_id: None,
|
|
|
|
|
plan_name: None,
|
|
|
|
|
plan_description: None,
|
|
|
|
|
steps: Vec::new(),
|
|
|
|
|
alternatives: Vec::new(),
|
|
|
|
|
confidence: 0.0,
|
|
|
|
|
risk_level: "unknown".to_string(),
|
|
|
|
|
estimated_duration_minutes: 0,
|
|
|
|
|
estimated_cost: 0.0,
|
|
|
|
|
resource_estimate: ResourceEstimateResponse {
|
|
|
|
|
compute_hours: 0.0,
|
|
|
|
|
storage_gb: 0.0,
|
|
|
|
|
api_calls: 0,
|
|
|
|
|
llm_tokens: 0,
|
|
|
|
|
estimated_cost_usd: 0.0,
|
|
|
|
|
},
|
|
|
|
|
basic_program: None,
|
|
|
|
|
requires_approval: false,
|
|
|
|
|
mcp_servers: Vec::new(),
|
|
|
|
|
external_apis: Vec::new(),
|
|
|
|
|
risks: Vec::new(),
|
|
|
|
|
error: Some(format!("Authentication error: {}", e)),
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Create intent compiler
|
|
|
|
|
let compiler = IntentCompiler::new(Arc::clone(&state));
|
|
|
|
|
|
|
|
|
|
// Compile the intent
|
|
|
|
|
match compiler.compile(&request.intent, &session).await {
|
|
|
|
|
Ok(compiled) => {
|
|
|
|
|
let response = CompileIntentResponse {
|
|
|
|
|
success: true,
|
|
|
|
|
plan_id: Some(compiled.plan.id.clone()),
|
|
|
|
|
plan_name: Some(compiled.plan.name.clone()),
|
|
|
|
|
plan_description: Some(compiled.plan.description.clone()),
|
|
|
|
|
steps: compiled
|
|
|
|
|
.plan
|
|
|
|
|
.steps
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|s| PlanStepResponse {
|
|
|
|
|
id: s.id.clone(),
|
|
|
|
|
order: s.order,
|
|
|
|
|
name: s.name.clone(),
|
|
|
|
|
description: s.description.clone(),
|
|
|
|
|
keywords: s.keywords.clone(),
|
|
|
|
|
priority: format!("{:?}", s.priority),
|
|
|
|
|
risk_level: format!("{:?}", s.risk_level),
|
|
|
|
|
estimated_minutes: s.estimated_minutes,
|
|
|
|
|
requires_approval: s.requires_approval,
|
|
|
|
|
})
|
|
|
|
|
.collect(),
|
|
|
|
|
alternatives: compiled
|
|
|
|
|
.alternatives
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|a| AlternativeResponse {
|
|
|
|
|
id: a.id.clone(),
|
|
|
|
|
description: a.description.clone(),
|
|
|
|
|
confidence: a.confidence,
|
|
|
|
|
pros: a.pros.clone(),
|
|
|
|
|
cons: a.cons.clone(),
|
|
|
|
|
estimated_cost: a.estimated_cost,
|
|
|
|
|
estimated_time_hours: a.estimated_time_hours,
|
|
|
|
|
})
|
|
|
|
|
.collect(),
|
|
|
|
|
confidence: compiled.confidence,
|
|
|
|
|
risk_level: format!("{:?}", compiled.risk_assessment.overall_risk),
|
|
|
|
|
estimated_duration_minutes: compiled.plan.estimated_duration_minutes,
|
|
|
|
|
estimated_cost: compiled.resource_estimate.estimated_cost_usd,
|
|
|
|
|
resource_estimate: ResourceEstimateResponse {
|
|
|
|
|
compute_hours: compiled.resource_estimate.compute_hours,
|
|
|
|
|
storage_gb: compiled.resource_estimate.storage_gb,
|
|
|
|
|
api_calls: compiled.resource_estimate.api_calls,
|
Fix truncated files, implement TODOs, remove all compilation errors and warnings
- Complete truncated auto_task.rs, autotask_api.rs, intent_compiler.rs, mcp_client.rs, safety_layer.rs
- Add missing structs: RiskFactor, ResourceUsage, TaskError, RollbackState, TaskSchedule, HealthStatus
- Add missing IntentCompiler methods: call_llm, assess_risks, estimate_resources, check_ambiguity, store_compiled_intent
- Implement SET ATTENDANT STATUS database storage
- Implement queue position calculation in transfer_to_human
- Add llm_tokens to ResourceEstimate
- Fix all unused imports and variables
- Add proper derives (Copy, PartialOrd) where needed
2025-12-12 17:33:11 -03:00
|
|
|
llm_tokens: compiled.resource_estimate.llm_tokens,
|
2025-12-12 12:33:17 -03:00
|
|
|
estimated_cost_usd: compiled.resource_estimate.estimated_cost_usd,
|
|
|
|
|
},
|
|
|
|
|
basic_program: Some(compiled.basic_program.clone()),
|
|
|
|
|
requires_approval: compiled.plan.requires_approval,
|
|
|
|
|
mcp_servers: compiled.resource_estimate.mcp_servers_needed.clone(),
|
|
|
|
|
external_apis: compiled.resource_estimate.external_services.clone(),
|
|
|
|
|
risks: compiled
|
|
|
|
|
.risk_assessment
|
|
|
|
|
.risks
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|r| RiskResponse {
|
|
|
|
|
id: r.id.clone(),
|
|
|
|
|
category: format!("{:?}", r.category),
|
|
|
|
|
description: r.description.clone(),
|
|
|
|
|
probability: r.probability,
|
|
|
|
|
impact: format!("{:?}", r.impact),
|
|
|
|
|
})
|
|
|
|
|
.collect(),
|
|
|
|
|
error: None,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
(StatusCode::OK, Json(response))
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
error!("Failed to compile intent: {}", e);
|
|
|
|
|
(
|
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
|
|
|
Json(CompileIntentResponse {
|
|
|
|
|
success: false,
|
|
|
|
|
plan_id: None,
|
|
|
|
|
plan_name: None,
|
|
|
|
|
plan_description: None,
|
|
|
|
|
steps: Vec::new(),
|
|
|
|
|
alternatives: Vec::new(),
|
|
|
|
|
confidence: 0.0,
|
|
|
|
|
risk_level: "unknown".to_string(),
|
|
|
|
|
estimated_duration_minutes: 0,
|
|
|
|
|
estimated_cost: 0.0,
|
|
|
|
|
resource_estimate: ResourceEstimateResponse {
|
|
|
|
|
compute_hours: 0.0,
|
|
|
|
|
storage_gb: 0.0,
|
|
|
|
|
api_calls: 0,
|
|
|
|
|
llm_tokens: 0,
|
|
|
|
|
estimated_cost_usd: 0.0,
|
|
|
|
|
},
|
|
|
|
|
basic_program: None,
|
|
|
|
|
requires_approval: false,
|
|
|
|
|
mcp_servers: Vec::new(),
|
|
|
|
|
external_apis: Vec::new(),
|
|
|
|
|
risks: Vec::new(),
|
|
|
|
|
error: Some(e.to_string()),
|
|
|
|
|
}),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// POST /api/autotask/execute - Execute a compiled plan
|
|
|
|
|
pub async fn execute_plan_handler(
|
|
|
|
|
State(state): State<Arc<AppState>>,
|
|
|
|
|
Json(request): Json<ExecutePlanRequest>,
|
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
|
info!("Executing plan: {}", request.plan_id);
|
|
|
|
|
|
|
|
|
|
let session = match get_current_session(&state).await {
|
|
|
|
|
Ok(s) => s,
|
|
|
|
|
Err(e) => {
|
|
|
|
|
return (
|
|
|
|
|
StatusCode::UNAUTHORIZED,
|
|
|
|
|
Json(ExecutePlanResponse {
|
|
|
|
|
success: false,
|
|
|
|
|
task_id: None,
|
|
|
|
|
status: None,
|
|
|
|
|
error: Some(format!("Authentication error: {}", e)),
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Parse execution mode
|
|
|
|
|
let execution_mode = match request.execution_mode.as_deref() {
|
|
|
|
|
Some("fully-automatic") => ExecutionMode::FullyAutomatic,
|
|
|
|
|
Some("supervised") => ExecutionMode::Supervised,
|
|
|
|
|
Some("manual") => ExecutionMode::Manual,
|
|
|
|
|
Some("dry-run") => ExecutionMode::DryRun,
|
|
|
|
|
_ => ExecutionMode::SemiAutomatic,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Parse priority
|
|
|
|
|
let priority = match request.priority.as_deref() {
|
|
|
|
|
Some("critical") => TaskPriority::Critical,
|
|
|
|
|
Some("high") => TaskPriority::High,
|
|
|
|
|
Some("low") => TaskPriority::Low,
|
|
|
|
|
Some("background") => TaskPriority::Background,
|
|
|
|
|
_ => TaskPriority::Medium,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Create the auto task from the compiled plan
|
Fix truncated files, implement TODOs, remove all compilation errors and warnings
- Complete truncated auto_task.rs, autotask_api.rs, intent_compiler.rs, mcp_client.rs, safety_layer.rs
- Add missing structs: RiskFactor, ResourceUsage, TaskError, RollbackState, TaskSchedule, HealthStatus
- Add missing IntentCompiler methods: call_llm, assess_risks, estimate_resources, check_ambiguity, store_compiled_intent
- Implement SET ATTENDANT STATUS database storage
- Implement queue position calculation in transfer_to_human
- Add llm_tokens to ResourceEstimate
- Fix all unused imports and variables
- Add proper derives (Copy, PartialOrd) where needed
2025-12-12 17:33:11 -03:00
|
|
|
match create_auto_task_from_plan(&state, &session, &request.plan_id, execution_mode, priority)
|
|
|
|
|
.await
|
|
|
|
|
{
|
2025-12-12 12:33:17 -03:00
|
|
|
Ok(task) => {
|
|
|
|
|
// Start execution
|
|
|
|
|
match start_task_execution(&state, &task.id).await {
|
|
|
|
|
Ok(_) => (
|
|
|
|
|
StatusCode::OK,
|
|
|
|
|
Json(ExecutePlanResponse {
|
|
|
|
|
success: true,
|
|
|
|
|
task_id: Some(task.id),
|
|
|
|
|
status: Some(task.status.to_string()),
|
|
|
|
|
error: None,
|
|
|
|
|
}),
|
|
|
|
|
),
|
|
|
|
|
Err(e) => (
|
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
|
|
|
Json(ExecutePlanResponse {
|
|
|
|
|
success: false,
|
|
|
|
|
task_id: Some(task.id),
|
|
|
|
|
status: Some("failed".to_string()),
|
|
|
|
|
error: Some(e.to_string()),
|
|
|
|
|
}),
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
error!("Failed to create task: {}", e);
|
|
|
|
|
(
|
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
|
|
|
Json(ExecutePlanResponse {
|
|
|
|
|
success: false,
|
|
|
|
|
task_id: None,
|
|
|
|
|
status: None,
|
|
|
|
|
error: Some(e.to_string()),
|
|
|
|
|
}),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// GET /api/autotask/list - List auto tasks
|
|
|
|
|
pub async fn list_tasks_handler(
|
|
|
|
|
State(state): State<Arc<AppState>>,
|
|
|
|
|
Query(query): Query<ListTasksQuery>,
|
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
|
let filter = query.filter.as_deref().unwrap_or("all");
|
|
|
|
|
let limit = query.limit.unwrap_or(50);
|
|
|
|
|
let offset = query.offset.unwrap_or(0);
|
|
|
|
|
|
|
|
|
|
match list_auto_tasks(&state, filter, limit, offset).await {
|
|
|
|
|
Ok(tasks) => {
|
|
|
|
|
// Render as HTML for HTMX
|
|
|
|
|
let html = render_task_list_html(&tasks);
|
|
|
|
|
(StatusCode::OK, axum::response::Html(html))
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
error!("Failed to list tasks: {}", e);
|
|
|
|
|
let html = format!(
|
|
|
|
|
r#"<div class="error-message">
|
|
|
|
|
<span class="error-icon">❌</span>
|
|
|
|
|
<p>Failed to load tasks: {}</p>
|
|
|
|
|
</div>"#,
|
|
|
|
|
html_escape(&e.to_string())
|
|
|
|
|
);
|
Fix truncated files, implement TODOs, remove all compilation errors and warnings
- Complete truncated auto_task.rs, autotask_api.rs, intent_compiler.rs, mcp_client.rs, safety_layer.rs
- Add missing structs: RiskFactor, ResourceUsage, TaskError, RollbackState, TaskSchedule, HealthStatus
- Add missing IntentCompiler methods: call_llm, assess_risks, estimate_resources, check_ambiguity, store_compiled_intent
- Implement SET ATTENDANT STATUS database storage
- Implement queue position calculation in transfer_to_human
- Add llm_tokens to ResourceEstimate
- Fix all unused imports and variables
- Add proper derives (Copy, PartialOrd) where needed
2025-12-12 17:33:11 -03:00
|
|
|
(
|
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
|
|
|
axum::response::Html(html),
|
|
|
|
|
)
|
2025-12-12 12:33:17 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// GET /api/autotask/stats - Get auto task statistics
|
|
|
|
|
pub async fn get_stats_handler(State(state): State<Arc<AppState>>) -> impl IntoResponse {
|
|
|
|
|
match get_auto_task_stats(&state).await {
|
|
|
|
|
Ok(stats) => (StatusCode::OK, Json(stats)),
|
|
|
|
|
Err(e) => {
|
|
|
|
|
error!("Failed to get stats: {}", e);
|
|
|
|
|
(
|
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
|
|
|
Json(AutoTaskStatsResponse {
|
|
|
|
|
total: 0,
|
|
|
|
|
running: 0,
|
|
|
|
|
pending: 0,
|
|
|
|
|
completed: 0,
|
|
|
|
|
failed: 0,
|
|
|
|
|
pending_approval: 0,
|
|
|
|
|
pending_decision: 0,
|
|
|
|
|
}),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// POST /api/autotask/:task_id/pause - Pause a task
|
|
|
|
|
pub async fn pause_task_handler(
|
|
|
|
|
State(state): State<Arc<AppState>>,
|
|
|
|
|
Path(task_id): Path<String>,
|
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
|
match update_task_status(&state, &task_id, AutoTaskStatus::Paused).await {
|
|
|
|
|
Ok(_) => (
|
|
|
|
|
StatusCode::OK,
|
|
|
|
|
Json(TaskActionResponse {
|
|
|
|
|
success: true,
|
|
|
|
|
message: Some("Task paused".to_string()),
|
|
|
|
|
error: None,
|
|
|
|
|
}),
|
|
|
|
|
),
|
|
|
|
|
Err(e) => (
|
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
|
|
|
Json(TaskActionResponse {
|
|
|
|
|
success: false,
|
|
|
|
|
message: None,
|
|
|
|
|
error: Some(e.to_string()),
|
|
|
|
|
}),
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// POST /api/autotask/:task_id/resume - Resume a paused task
|
|
|
|
|
pub async fn resume_task_handler(
|
|
|
|
|
State(state): State<Arc<AppState>>,
|
|
|
|
|
Path(task_id): Path<String>,
|
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
|
match update_task_status(&state, &task_id, AutoTaskStatus::Running).await {
|
|
|
|
|
Ok(_) => {
|
|
|
|
|
// Restart execution
|
|
|
|
|
let _ = start_task_execution(&state, &task_id).await;
|
|
|
|
|
(
|
|
|
|
|
StatusCode::OK,
|
|
|
|
|
Json(TaskActionResponse {
|
|
|
|
|
success: true,
|
|
|
|
|
message: Some("Task resumed".to_string()),
|
|
|
|
|
error: None,
|
|
|
|
|
}),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
Err(e) => (
|
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
|
|
|
Json(TaskActionResponse {
|
|
|
|
|
success: false,
|
|
|
|
|
message: None,
|
|
|
|
|
error: Some(e.to_string()),
|
|
|
|
|
}),
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// POST /api/autotask/:task_id/cancel - Cancel a task
|
|
|
|
|
pub async fn cancel_task_handler(
|
|
|
|
|
State(state): State<Arc<AppState>>,
|
|
|
|
|
Path(task_id): Path<String>,
|
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
|
match update_task_status(&state, &task_id, AutoTaskStatus::Cancelled).await {
|
|
|
|
|
Ok(_) => (
|
|
|
|
|
StatusCode::OK,
|
|
|
|
|
Json(TaskActionResponse {
|
|
|
|
|
success: true,
|
|
|
|
|
message: Some("Task cancelled".to_string()),
|
|
|
|
|
error: None,
|
|
|
|
|
}),
|
|
|
|
|
),
|
|
|
|
|
Err(e) => (
|
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
|
|
|
Json(TaskActionResponse {
|
|
|
|
|
success: false,
|
|
|
|
|
message: None,
|
|
|
|
|
error: Some(e.to_string()),
|
|
|
|
|
}),
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// POST /api/autotask/:task_id/simulate - Simulate task execution
|
|
|
|
|
pub async fn simulate_task_handler(
|
|
|
|
|
State(state): State<Arc<AppState>>,
|
|
|
|
|
Path(task_id): Path<String>,
|
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
|
let session = match get_current_session(&state).await {
|
|
|
|
|
Ok(s) => s,
|
|
|
|
|
Err(e) => {
|
|
|
|
|
return (
|
|
|
|
|
StatusCode::UNAUTHORIZED,
|
|
|
|
|
Json(SimulationResponse {
|
|
|
|
|
success: false,
|
|
|
|
|
confidence: 0.0,
|
|
|
|
|
risk_score: 0.0,
|
|
|
|
|
risk_level: "unknown".to_string(),
|
|
|
|
|
step_outcomes: Vec::new(),
|
|
|
|
|
impact: ImpactResponse {
|
|
|
|
|
risk_score: 0.0,
|
|
|
|
|
risk_level: "unknown".to_string(),
|
|
|
|
|
data_impact: DataImpactResponse {
|
|
|
|
|
records_created: 0,
|
|
|
|
|
records_modified: 0,
|
|
|
|
|
records_deleted: 0,
|
|
|
|
|
tables_affected: Vec::new(),
|
|
|
|
|
reversible: true,
|
|
|
|
|
},
|
|
|
|
|
cost_impact: CostImpactResponse {
|
|
|
|
|
api_costs: 0.0,
|
|
|
|
|
compute_costs: 0.0,
|
|
|
|
|
storage_costs: 0.0,
|
|
|
|
|
total_estimated_cost: 0.0,
|
|
|
|
|
},
|
|
|
|
|
time_impact: TimeImpactResponse {
|
|
|
|
|
estimated_duration_seconds: 0,
|
|
|
|
|
blocking: false,
|
|
|
|
|
},
|
|
|
|
|
security_impact: SecurityImpactResponse {
|
|
|
|
|
risk_level: "unknown".to_string(),
|
|
|
|
|
credentials_accessed: Vec::new(),
|
|
|
|
|
external_systems: Vec::new(),
|
|
|
|
|
concerns: Vec::new(),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
side_effects: Vec::new(),
|
|
|
|
|
recommendations: Vec::new(),
|
|
|
|
|
error: Some(format!("Authentication error: {}", e)),
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let safety_layer = SafetyLayer::new(Arc::clone(&state));
|
|
|
|
|
|
|
|
|
|
match simulate_task_execution(&state, &safety_layer, &task_id, &session).await {
|
|
|
|
|
Ok(result) => {
|
|
|
|
|
let response = SimulationResponse {
|
|
|
|
|
success: result.success,
|
|
|
|
|
confidence: result.confidence,
|
|
|
|
|
risk_score: result.impact.risk_score,
|
|
|
|
|
risk_level: format!("{}", result.impact.risk_level),
|
|
|
|
|
step_outcomes: result
|
|
|
|
|
.step_outcomes
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|s| StepOutcomeResponse {
|
|
|
|
|
step_id: s.step_id.clone(),
|
|
|
|
|
step_name: s.step_name.clone(),
|
|
|
|
|
would_succeed: s.would_succeed,
|
|
|
|
|
success_probability: s.success_probability,
|
Fix truncated files, implement TODOs, remove all compilation errors and warnings
- Complete truncated auto_task.rs, autotask_api.rs, intent_compiler.rs, mcp_client.rs, safety_layer.rs
- Add missing structs: RiskFactor, ResourceUsage, TaskError, RollbackState, TaskSchedule, HealthStatus
- Add missing IntentCompiler methods: call_llm, assess_risks, estimate_resources, check_ambiguity, store_compiled_intent
- Implement SET ATTENDANT STATUS database storage
- Implement queue position calculation in transfer_to_human
- Add llm_tokens to ResourceEstimate
- Fix all unused imports and variables
- Add proper derives (Copy, PartialOrd) where needed
2025-12-12 17:33:11 -03:00
|
|
|
failure_modes: s
|
|
|
|
|
.failure_modes
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|f| f.failure_type.clone())
|
|
|
|
|
.collect(),
|
2025-12-12 12:33:17 -03:00
|
|
|
})
|
|
|
|
|
.collect(),
|
|
|
|
|
impact: ImpactResponse {
|
|
|
|
|
risk_score: result.impact.risk_score,
|
|
|
|
|
risk_level: format!("{}", result.impact.risk_level),
|
|
|
|
|
data_impact: DataImpactResponse {
|
|
|
|
|
records_created: result.impact.data_impact.records_created,
|
|
|
|
|
records_modified: result.impact.data_impact.records_modified,
|
|
|
|
|
records_deleted: result.impact.data_impact.records_deleted,
|
|
|
|
|
tables_affected: result.impact.data_impact.tables_affected.clone(),
|
|
|
|
|
reversible: result.impact.data_impact.reversible,
|
|
|
|
|
},
|
|
|
|
|
cost_impact: CostImpactResponse {
|
|
|
|
|
api_costs: result.impact.cost_impact.api_costs,
|
|
|
|
|
compute_costs: result.impact.cost_impact.compute_costs,
|
|
|
|
|
storage_costs: result.impact.cost_impact.storage_costs,
|
|
|
|
|
total_estimated_cost: result.impact.cost_impact.total_estimated_cost,
|
|
|
|
|
},
|
|
|
|
|
time_impact: TimeImpactResponse {
|
Fix truncated files, implement TODOs, remove all compilation errors and warnings
- Complete truncated auto_task.rs, autotask_api.rs, intent_compiler.rs, mcp_client.rs, safety_layer.rs
- Add missing structs: RiskFactor, ResourceUsage, TaskError, RollbackState, TaskSchedule, HealthStatus
- Add missing IntentCompiler methods: call_llm, assess_risks, estimate_resources, check_ambiguity, store_compiled_intent
- Implement SET ATTENDANT STATUS database storage
- Implement queue position calculation in transfer_to_human
- Add llm_tokens to ResourceEstimate
- Fix all unused imports and variables
- Add proper derives (Copy, PartialOrd) where needed
2025-12-12 17:33:11 -03:00
|
|
|
estimated_duration_seconds: result
|
|
|
|
|
.impact
|
|
|
|
|
.time_impact
|
|
|
|
|
.estimated_duration_seconds,
|
2025-12-12 12:33:17 -03:00
|
|
|
blocking: result.impact.time_impact.blocking,
|
|
|
|
|
},
|
|
|
|
|
security_impact: SecurityImpactResponse {
|
|
|
|
|
risk_level: format!("{}", result.impact.security_impact.risk_level),
|
Fix truncated files, implement TODOs, remove all compilation errors and warnings
- Complete truncated auto_task.rs, autotask_api.rs, intent_compiler.rs, mcp_client.rs, safety_layer.rs
- Add missing structs: RiskFactor, ResourceUsage, TaskError, RollbackState, TaskSchedule, HealthStatus
- Add missing IntentCompiler methods: call_llm, assess_risks, estimate_resources, check_ambiguity, store_compiled_intent
- Implement SET ATTENDANT STATUS database storage
- Implement queue position calculation in transfer_to_human
- Add llm_tokens to ResourceEstimate
- Fix all unused imports and variables
- Add proper derives (Copy, PartialOrd) where needed
2025-12-12 17:33:11 -03:00
|
|
|
credentials_accessed: result
|
|
|
|
|
.impact
|
|
|
|
|
.security_impact
|
|
|
|
|
.credentials_accessed
|
|
|
|
|
.clone(),
|
2025-12-12 12:33:17 -03:00
|
|
|
external_systems: result.impact.security_impact.external_systems.clone(),
|
|
|
|
|
concerns: result.impact.security_impact.concerns.clone(),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
side_effects: result
|
|
|
|
|
.side_effects
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|s| SideEffectResponse {
|
|
|
|
|
effect_type: s.effect_type.clone(),
|
|
|
|
|
description: s.description.clone(),
|
|
|
|
|
severity: format!("{:?}", s.severity),
|
|
|
|
|
mitigation: s.mitigation.clone(),
|
|
|
|
|
})
|
|
|
|
|
.collect(),
|
|
|
|
|
recommendations: result
|
|
|
|
|
.recommendations
|
|
|
|
|
.iter()
|
|
|
|
|
.enumerate()
|
|
|
|
|
.map(|(i, r)| RecommendationResponse {
|
|
|
|
|
id: format!("rec-{}", i),
|
|
|
|
|
recommendation_type: format!("{:?}", r.recommendation_type),
|
|
|
|
|
description: r.description.clone(),
|
|
|
|
|
action: r.action.clone(),
|
|
|
|
|
})
|
|
|
|
|
.collect(),
|
|
|
|
|
error: None,
|
|
|
|
|
};
|
|
|
|
|
(StatusCode::OK, Json(response))
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
error!("Simulation failed: {}", e);
|
|
|
|
|
(
|
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
|
|
|
Json(SimulationResponse {
|
|
|
|
|
success: false,
|
|
|
|
|
confidence: 0.0,
|
|
|
|
|
risk_score: 1.0,
|
|
|
|
|
risk_level: "unknown".to_string(),
|
|
|
|
|
step_outcomes: Vec::new(),
|
|
|
|
|
impact: ImpactResponse {
|
|
|
|
|
risk_score: 1.0,
|
|
|
|
|
risk_level: "unknown".to_string(),
|
|
|
|
|
data_impact: DataImpactResponse {
|
|
|
|
|
records_created: 0,
|
|
|
|
|
records_modified: 0,
|
|
|
|
|
records_deleted: 0,
|
|
|
|
|
tables_affected: Vec::new(),
|
|
|
|
|
reversible: true,
|
|
|
|
|
},
|
|
|
|
|
cost_impact: CostImpactResponse {
|
|
|
|
|
api_costs: 0.0,
|
|
|
|
|
compute_costs: 0.0,
|
|
|
|
|
storage_costs: 0.0,
|
|
|
|
|
total_estimated_cost: 0.0,
|
|
|
|
|
},
|
|
|
|
|
time_impact: TimeImpactResponse {
|
|
|
|
|
estimated_duration_seconds: 0,
|
|
|
|
|
blocking: false,
|
|
|
|
|
},
|
|
|
|
|
security_impact: SecurityImpactResponse {
|
|
|
|
|
risk_level: "unknown".to_string(),
|
|
|
|
|
credentials_accessed: Vec::new(),
|
|
|
|
|
external_systems: Vec::new(),
|
|
|
|
|
concerns: Vec::new(),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
side_effects: Vec::new(),
|
|
|
|
|
recommendations: Vec::new(),
|
|
|
|
|
error: Some(e.to_string()),
|
|
|
|
|
}),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// GET /api/autotask/:task_id/decisions - Get pending decisions for a task
|
|
|
|
|
pub async fn get_decisions_handler(
|
|
|
|
|
State(state): State<Arc<AppState>>,
|
|
|
|
|
Path(task_id): Path<String>,
|
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
|
match get_pending_decisions(&state, &task_id).await {
|
|
|
|
|
Ok(decisions) => (StatusCode::OK, Json(decisions)),
|
|
|
|
|
Err(e) => {
|
|
|
|
|
error!("Failed to get decisions: {}", e);
|
Fix truncated files, implement TODOs, remove all compilation errors and warnings
- Complete truncated auto_task.rs, autotask_api.rs, intent_compiler.rs, mcp_client.rs, safety_layer.rs
- Add missing structs: RiskFactor, ResourceUsage, TaskError, RollbackState, TaskSchedule, HealthStatus
- Add missing IntentCompiler methods: call_llm, assess_risks, estimate_resources, check_ambiguity, store_compiled_intent
- Implement SET ATTENDANT STATUS database storage
- Implement queue position calculation in transfer_to_human
- Add llm_tokens to ResourceEstimate
- Fix all unused imports and variables
- Add proper derives (Copy, PartialOrd) where needed
2025-12-12 17:33:11 -03:00
|
|
|
(
|
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
|
|
|
Json(Vec::<PendingDecision>::new()),
|
|
|
|
|
)
|
2025-12-12 12:33:17 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// POST /api/autotask/:task_id/decide - Submit a decision
|
|
|
|
|
pub async fn submit_decision_handler(
|
|
|
|
|
State(state): State<Arc<AppState>>,
|
|
|
|
|
Path(task_id): Path<String>,
|
|
|
|
|
Json(request): Json<DecisionRequest>,
|
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
|
match submit_decision(&state, &task_id, &request).await {
|
|
|
|
|
Ok(_) => (
|
|
|
|
|
StatusCode::OK,
|
|
|
|
|
Json(TaskActionResponse {
|
|
|
|
|
success: true,
|
|
|
|
|
message: Some("Decision submitted".to_string()),
|
|
|
|
|
error: None,
|
|
|
|
|
}),
|
|
|
|
|
),
|
|
|
|
|
Err(e) => (
|
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
|
|
|
Json(TaskActionResponse {
|
|
|
|
|
success: false,
|
|
|
|
|
message: None,
|
|
|
|
|
error: Some(e.to_string()),
|
|
|
|
|
}),
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// GET /api/autotask/:task_id/approvals - Get pending approvals for a task
|
|
|
|
|
pub async fn get_approvals_handler(
|
|
|
|
|
State(state): State<Arc<AppState>>,
|
|
|
|
|
Path(task_id): Path<String>,
|
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
|
match get_pending_approvals(&state, &task_id).await {
|
|
|
|
|
Ok(approvals) => (StatusCode::OK, Json(approvals)),
|
|
|
|
|
Err(e) => {
|
|
|
|
|
error!("Failed to get approvals: {}", e);
|
Fix truncated files, implement TODOs, remove all compilation errors and warnings
- Complete truncated auto_task.rs, autotask_api.rs, intent_compiler.rs, mcp_client.rs, safety_layer.rs
- Add missing structs: RiskFactor, ResourceUsage, TaskError, RollbackState, TaskSchedule, HealthStatus
- Add missing IntentCompiler methods: call_llm, assess_risks, estimate_resources, check_ambiguity, store_compiled_intent
- Implement SET ATTENDANT STATUS database storage
- Implement queue position calculation in transfer_to_human
- Add llm_tokens to ResourceEstimate
- Fix all unused imports and variables
- Add proper derives (Copy, PartialOrd) where needed
2025-12-12 17:33:11 -03:00
|
|
|
(
|
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
|
|
|
Json(Vec::<PendingApproval>::new()),
|
|
|
|
|
)
|
2025-12-12 12:33:17 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// POST /api/autotask/:task_id/approve - Submit an approval decision
|
|
|
|
|
pub async fn submit_approval_handler(
|
|
|
|
|
State(state): State<Arc<AppState>>,
|
|
|
|
|
Path(task_id): Path<String>,
|
|
|
|
|
Json(request): Json<ApprovalRequest>,
|
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
|
match submit_approval(&state, &task_id, &request).await {
|
|
|
|
|
Ok(_) => (
|
|
|
|
|
StatusCode::OK,
|
|
|
|
|
Json(TaskActionResponse {
|
|
|
|
|
success: true,
|
|
|
|
|
message: Some(format!("Approval {}", request.action)),
|
|
|
|
|
error: None,
|
|
|
|
|
}),
|
|
|
|
|
),
|
|
|
|
|
Err(e) => (
|
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
|
|
|
Json(TaskActionResponse {
|
|
|
|
|
success: false,
|
|
|
|
|
message: None,
|
|
|
|
|
error: Some(e.to_string()),
|
|
|
|
|
}),
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// POST /api/autotask/simulate/:plan_id - Simulate a plan before execution
|
|
|
|
|
pub async fn simulate_plan_handler(
|
|
|
|
|
State(state): State<Arc<AppState>>,
|
|
|
|
|
Path(plan_id): Path<String>,
|
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
|
let session = match get_current_session(&state).await {
|
|
|
|
|
Ok(s) => s,
|
|
|
|
|
Err(e) => {
|
|
|
|
|
return (
|
|
|
|
|
StatusCode::UNAUTHORIZED,
|
|
|
|
|
Json(SimulationResponse {
|
|
|
|
|
success: false,
|
|
|
|
|
confidence: 0.0,
|
|
|
|
|
risk_score: 0.0,
|
|
|
|
|
risk_level: "unknown".to_string(),
|
|
|
|
|
step_outcomes: Vec::new(),
|
|
|
|
|
impact: ImpactResponse {
|
|
|
|
|
risk_score: 0.0,
|
|
|
|
|
risk_level: "unknown".to_string(),
|
|
|
|
|
data_impact: DataImpactResponse {
|
|
|
|
|
records_created: 0,
|
|
|
|
|
records_modified: 0,
|
|
|
|
|
records_deleted: 0,
|
|
|
|
|
tables_affected: Vec::new(),
|
|
|
|
|
reversible: true,
|
Fix truncated files, implement TODOs, remove all compilation errors and warnings
- Complete truncated auto_task.rs, autotask_api.rs, intent_compiler.rs, mcp_client.rs, safety_layer.rs
- Add missing structs: RiskFactor, ResourceUsage, TaskError, RollbackState, TaskSchedule, HealthStatus
- Add missing IntentCompiler methods: call_llm, assess_risks, estimate_resources, check_ambiguity, store_compiled_intent
- Implement SET ATTENDANT STATUS database storage
- Implement queue position calculation in transfer_to_human
- Add llm_tokens to ResourceEstimate
- Fix all unused imports and variables
- Add proper derives (Copy, PartialOrd) where needed
2025-12-12 17:33:11 -03:00
|
|
|
},
|
|
|
|
|
cost_impact: CostImpactResponse {
|
|
|
|
|
api_costs: 0.0,
|
|
|
|
|
compute_costs: 0.0,
|
|
|
|
|
storage_costs: 0.0,
|
|
|
|
|
total_estimated_cost: 0.0,
|
|
|
|
|
},
|
|
|
|
|
time_impact: TimeImpactResponse {
|
|
|
|
|
estimated_duration_seconds: 0,
|
|
|
|
|
blocking: false,
|
|
|
|
|
},
|
|
|
|
|
security_impact: SecurityImpactResponse {
|
|
|
|
|
risk_level: "unknown".to_string(),
|
|
|
|
|
credentials_accessed: Vec::new(),
|
|
|
|
|
external_systems: Vec::new(),
|
|
|
|
|
concerns: Vec::new(),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
side_effects: Vec::new(),
|
|
|
|
|
recommendations: Vec::new(),
|
|
|
|
|
error: Some(format!("Authentication error: {}", e)),
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let safety_layer = SafetyLayer::new(Arc::clone(&state));
|
|
|
|
|
|
|
|
|
|
match simulate_plan_execution(&state, &safety_layer, &plan_id, &session).await {
|
|
|
|
|
Ok(result) => {
|
|
|
|
|
let response = SimulationResponse {
|
|
|
|
|
success: result.success,
|
|
|
|
|
confidence: result.confidence,
|
|
|
|
|
risk_score: result.impact.risk_score,
|
|
|
|
|
risk_level: format!("{}", result.impact.risk_level),
|
|
|
|
|
step_outcomes: result
|
|
|
|
|
.step_outcomes
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|s| StepOutcomeResponse {
|
|
|
|
|
step_id: s.step_id.clone(),
|
|
|
|
|
step_name: s.step_name.clone(),
|
|
|
|
|
would_succeed: s.would_succeed,
|
|
|
|
|
success_probability: s.success_probability,
|
|
|
|
|
failure_modes: s
|
|
|
|
|
.failure_modes
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|f| f.failure_type.clone())
|
|
|
|
|
.collect(),
|
|
|
|
|
})
|
|
|
|
|
.collect(),
|
|
|
|
|
impact: ImpactResponse {
|
|
|
|
|
risk_score: result.impact.risk_score,
|
|
|
|
|
risk_level: format!("{}", result.impact.risk_level),
|
|
|
|
|
data_impact: DataImpactResponse {
|
|
|
|
|
records_created: result.impact.data_impact.records_created,
|
|
|
|
|
records_modified: result.impact.data_impact.records_modified,
|
|
|
|
|
records_deleted: result.impact.data_impact.records_deleted,
|
|
|
|
|
tables_affected: result.impact.data_impact.tables_affected.clone(),
|
|
|
|
|
reversible: result.impact.data_impact.reversible,
|
|
|
|
|
},
|
|
|
|
|
cost_impact: CostImpactResponse {
|
|
|
|
|
api_costs: result.impact.cost_impact.api_costs,
|
|
|
|
|
compute_costs: result.impact.cost_impact.compute_costs,
|
|
|
|
|
storage_costs: result.impact.cost_impact.storage_costs,
|
|
|
|
|
total_estimated_cost: result.impact.cost_impact.total_estimated_cost,
|
|
|
|
|
},
|
|
|
|
|
time_impact: TimeImpactResponse {
|
|
|
|
|
estimated_duration_seconds: result
|
|
|
|
|
.impact
|
|
|
|
|
.time_impact
|
|
|
|
|
.estimated_duration_seconds,
|
|
|
|
|
blocking: result.impact.time_impact.blocking,
|
|
|
|
|
},
|
|
|
|
|
security_impact: SecurityImpactResponse {
|
|
|
|
|
risk_level: format!("{}", result.impact.security_impact.risk_level),
|
|
|
|
|
credentials_accessed: result
|
|
|
|
|
.impact
|
|
|
|
|
.security_impact
|
|
|
|
|
.credentials_accessed
|
|
|
|
|
.clone(),
|
|
|
|
|
external_systems: result.impact.security_impact.external_systems.clone(),
|
|
|
|
|
concerns: result.impact.security_impact.concerns.clone(),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
side_effects: result
|
|
|
|
|
.side_effects
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|s| SideEffectResponse {
|
|
|
|
|
effect_type: s.effect_type.clone(),
|
|
|
|
|
description: s.description.clone(),
|
|
|
|
|
severity: format!("{:?}", s.severity),
|
|
|
|
|
mitigation: s.mitigation.clone(),
|
|
|
|
|
})
|
|
|
|
|
.collect(),
|
|
|
|
|
recommendations: result
|
|
|
|
|
.recommendations
|
|
|
|
|
.iter()
|
|
|
|
|
.enumerate()
|
|
|
|
|
.map(|(i, r)| RecommendationResponse {
|
|
|
|
|
id: format!("rec-{}", i),
|
|
|
|
|
recommendation_type: format!("{:?}", r.recommendation_type),
|
|
|
|
|
description: r.description.clone(),
|
|
|
|
|
action: r.action.clone(),
|
|
|
|
|
})
|
|
|
|
|
.collect(),
|
|
|
|
|
error: None,
|
|
|
|
|
};
|
|
|
|
|
(StatusCode::OK, Json(response))
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
error!("Plan simulation failed: {}", e);
|
|
|
|
|
(
|
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
|
|
|
Json(SimulationResponse {
|
|
|
|
|
success: false,
|
|
|
|
|
confidence: 0.0,
|
|
|
|
|
risk_score: 1.0,
|
|
|
|
|
risk_level: "unknown".to_string(),
|
|
|
|
|
step_outcomes: Vec::new(),
|
|
|
|
|
impact: ImpactResponse {
|
|
|
|
|
risk_score: 1.0,
|
|
|
|
|
risk_level: "unknown".to_string(),
|
|
|
|
|
data_impact: DataImpactResponse {
|
|
|
|
|
records_created: 0,
|
|
|
|
|
records_modified: 0,
|
|
|
|
|
records_deleted: 0,
|
|
|
|
|
tables_affected: Vec::new(),
|
|
|
|
|
reversible: true,
|
|
|
|
|
},
|
|
|
|
|
cost_impact: CostImpactResponse {
|
|
|
|
|
api_costs: 0.0,
|
|
|
|
|
compute_costs: 0.0,
|
|
|
|
|
storage_costs: 0.0,
|
|
|
|
|
total_estimated_cost: 0.0,
|
|
|
|
|
},
|
|
|
|
|
time_impact: TimeImpactResponse {
|
|
|
|
|
estimated_duration_seconds: 0,
|
|
|
|
|
blocking: false,
|
|
|
|
|
},
|
|
|
|
|
security_impact: SecurityImpactResponse {
|
|
|
|
|
risk_level: "unknown".to_string(),
|
|
|
|
|
credentials_accessed: Vec::new(),
|
|
|
|
|
external_systems: Vec::new(),
|
|
|
|
|
concerns: Vec::new(),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
side_effects: Vec::new(),
|
|
|
|
|
recommendations: Vec::new(),
|
|
|
|
|
error: Some(e.to_string()),
|
|
|
|
|
}),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn get_current_session(
|
|
|
|
|
state: &Arc<AppState>,
|
|
|
|
|
) -> Result<crate::shared::models::UserSession, Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
use crate::shared::models::user_sessions::dsl::*;
|
|
|
|
|
use diesel::prelude::*;
|
|
|
|
|
|
|
|
|
|
let mut conn = state
|
|
|
|
|
.conn
|
|
|
|
|
.get()
|
|
|
|
|
.map_err(|e| format!("DB connection error: {}", e))?;
|
|
|
|
|
|
|
|
|
|
let session = user_sessions
|
|
|
|
|
.order(created_at.desc())
|
|
|
|
|
.first::<crate::shared::models::UserSession>(&mut conn)
|
|
|
|
|
.optional()
|
|
|
|
|
.map_err(|e| format!("DB query error: {}", e))?
|
|
|
|
|
.ok_or_else(|| "No active session found")?;
|
|
|
|
|
|
|
|
|
|
Ok(session)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn create_auto_task_from_plan(
|
|
|
|
|
_state: &Arc<AppState>,
|
|
|
|
|
session: &crate::shared::models::UserSession,
|
|
|
|
|
plan_id: &str,
|
|
|
|
|
execution_mode: ExecutionMode,
|
|
|
|
|
priority: TaskPriority,
|
|
|
|
|
) -> Result<AutoTask, Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
let task = AutoTask {
|
|
|
|
|
id: Uuid::new_v4().to_string(),
|
|
|
|
|
title: format!("Task from plan {}", plan_id),
|
|
|
|
|
intent: String::new(),
|
|
|
|
|
status: AutoTaskStatus::Ready,
|
|
|
|
|
mode: execution_mode,
|
|
|
|
|
priority,
|
|
|
|
|
plan_id: Some(plan_id.to_string()),
|
|
|
|
|
basic_program: None,
|
|
|
|
|
current_step: 0,
|
|
|
|
|
total_steps: 0,
|
|
|
|
|
progress: 0.0,
|
|
|
|
|
step_results: Vec::new(),
|
|
|
|
|
pending_decisions: Vec::new(),
|
|
|
|
|
pending_approvals: Vec::new(),
|
|
|
|
|
risk_summary: None,
|
|
|
|
|
resource_usage: crate::basic::keywords::auto_task::ResourceUsage::default(),
|
|
|
|
|
error: None,
|
|
|
|
|
rollback_state: None,
|
|
|
|
|
session_id: session.id.to_string(),
|
|
|
|
|
bot_id: session.bot_id.to_string(),
|
|
|
|
|
created_by: session.user_id.to_string(),
|
|
|
|
|
assigned_to: "auto".to_string(),
|
|
|
|
|
schedule: None,
|
|
|
|
|
tags: Vec::new(),
|
|
|
|
|
parent_task_id: None,
|
|
|
|
|
subtask_ids: Vec::new(),
|
|
|
|
|
depends_on: Vec::new(),
|
|
|
|
|
dependents: Vec::new(),
|
|
|
|
|
mcp_servers: Vec::new(),
|
|
|
|
|
external_apis: Vec::new(),
|
|
|
|
|
created_at: Utc::now(),
|
|
|
|
|
updated_at: Utc::now(),
|
|
|
|
|
started_at: None,
|
|
|
|
|
completed_at: None,
|
|
|
|
|
estimated_completion: None,
|
|
|
|
|
};
|
|
|
|
|
Ok(task)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn start_task_execution(
|
|
|
|
|
_state: &Arc<AppState>,
|
|
|
|
|
task_id: &str,
|
|
|
|
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
info!("Starting task execution task_id={}", task_id);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn list_auto_tasks(
|
|
|
|
|
_state: &Arc<AppState>,
|
|
|
|
|
_filter: &str,
|
|
|
|
|
_limit: i32,
|
|
|
|
|
_offset: i32,
|
|
|
|
|
) -> Result<Vec<AutoTask>, Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
Ok(Vec::new())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn get_auto_task_stats(
|
|
|
|
|
_state: &Arc<AppState>,
|
|
|
|
|
) -> Result<AutoTaskStatsResponse, Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
Ok(AutoTaskStatsResponse {
|
|
|
|
|
total: 0,
|
|
|
|
|
running: 0,
|
|
|
|
|
pending: 0,
|
|
|
|
|
completed: 0,
|
|
|
|
|
failed: 0,
|
|
|
|
|
pending_approval: 0,
|
|
|
|
|
pending_decision: 0,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn update_task_status(
|
|
|
|
|
_state: &Arc<AppState>,
|
|
|
|
|
task_id: &str,
|
|
|
|
|
status: AutoTaskStatus,
|
|
|
|
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
info!(
|
|
|
|
|
"Updating task status task_id={} status={:?}",
|
|
|
|
|
task_id, status
|
|
|
|
|
);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn simulate_task_execution(
|
|
|
|
|
_state: &Arc<AppState>,
|
|
|
|
|
safety_layer: &SafetyLayer,
|
|
|
|
|
task_id: &str,
|
|
|
|
|
session: &crate::shared::models::UserSession,
|
|
|
|
|
) -> Result<SimulationResult, Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
info!("Simulating task execution task_id={}", task_id);
|
|
|
|
|
safety_layer.simulate_execution(task_id, session).await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn simulate_plan_execution(
|
|
|
|
|
_state: &Arc<AppState>,
|
|
|
|
|
safety_layer: &SafetyLayer,
|
|
|
|
|
plan_id: &str,
|
|
|
|
|
session: &crate::shared::models::UserSession,
|
|
|
|
|
) -> Result<SimulationResult, Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
info!("Simulating plan execution plan_id={}", plan_id);
|
|
|
|
|
safety_layer.simulate_execution(plan_id, session).await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn get_pending_decisions(
|
|
|
|
|
_state: &Arc<AppState>,
|
|
|
|
|
task_id: &str,
|
|
|
|
|
) -> Result<Vec<PendingDecision>, Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
trace!("Getting pending decisions for task_id={}", task_id);
|
|
|
|
|
Ok(Vec::new())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn submit_decision(
|
|
|
|
|
_state: &Arc<AppState>,
|
|
|
|
|
task_id: &str,
|
|
|
|
|
request: &DecisionRequest,
|
|
|
|
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
info!(
|
|
|
|
|
"Submitting decision task_id={} decision_id={}",
|
|
|
|
|
task_id, request.decision_id
|
|
|
|
|
);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn get_pending_approvals(
|
|
|
|
|
_state: &Arc<AppState>,
|
|
|
|
|
task_id: &str,
|
|
|
|
|
) -> Result<Vec<PendingApproval>, Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
trace!("Getting pending approvals for task_id={}", task_id);
|
|
|
|
|
Ok(Vec::new())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn submit_approval(
|
|
|
|
|
_state: &Arc<AppState>,
|
|
|
|
|
task_id: &str,
|
|
|
|
|
request: &ApprovalRequest,
|
|
|
|
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
|
|
|
info!(
|
|
|
|
|
"Submitting approval task_id={} approval_id={} action={}",
|
|
|
|
|
task_id, request.approval_id, request.action
|
|
|
|
|
);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn render_task_list_html(tasks: &[AutoTask]) -> String {
|
|
|
|
|
if tasks.is_empty() {
|
|
|
|
|
return r#"<div class="empty-state"><p>No tasks found</p></div>"#.to_string();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut html = String::from(r#"<div class="task-list">"#);
|
|
|
|
|
for task in tasks {
|
|
|
|
|
html.push_str(&format!(
|
|
|
|
|
r#"<div class="task-item" data-task-id="{}">
|
|
|
|
|
<div class="task-title">{}</div>
|
|
|
|
|
<div class="task-status">{}</div>
|
|
|
|
|
<div class="task-progress">{}%</div>
|
|
|
|
|
</div>"#,
|
|
|
|
|
html_escape(&task.id),
|
|
|
|
|
html_escape(&task.title),
|
|
|
|
|
html_escape(&task.status.to_string()),
|
|
|
|
|
(task.progress * 100.0) as i32
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
html.push_str("</div>");
|
|
|
|
|
html
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn html_escape(s: &str) -> String {
|
|
|
|
|
s.replace('&', "&")
|
|
|
|
|
.replace('<', "<")
|
|
|
|
|
.replace('>', ">")
|
|
|
|
|
.replace('"', """)
|
|
|
|
|
.replace('\'', "'")
|
|
|
|
|
}
|