summaryrefslogtreecommitdiff
path: root/makima/src/db/models.rs
diff options
context:
space:
mode:
Diffstat (limited to 'makima/src/db/models.rs')
-rw-r--r--makima/src/db/models.rs212
1 files changed, 211 insertions, 1 deletions
diff --git a/makima/src/db/models.rs b/makima/src/db/models.rs
index 636d81a..a7d2cda 100644
--- a/makima/src/db/models.rs
+++ b/makima/src/db/models.rs
@@ -1949,6 +1949,64 @@ pub struct CheckpointListResponse {
// Supervisor State (for supervisor resumability)
// ============================================================================
+/// Supervisor activity state enum
+/// Tracks the current operational state of the supervisor
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum SupervisorActivityState {
+ /// Supervisor is initializing
+ Initializing,
+ /// Supervisor is in planning phase
+ Planning,
+ /// Supervisor is actively executing work
+ Executing,
+ /// Supervisor is waiting for spawned tasks to complete
+ WaitingForTask,
+ /// Supervisor is waiting for user input
+ WaitingForUser,
+ /// Supervisor encountered an error
+ Error,
+ /// Supervisor has completed its work
+ Completed,
+}
+
+impl Default for SupervisorActivityState {
+ fn default() -> Self {
+ Self::Initializing
+ }
+}
+
+impl std::fmt::Display for SupervisorActivityState {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Self::Initializing => write!(f, "initializing"),
+ Self::Planning => write!(f, "planning"),
+ Self::Executing => write!(f, "executing"),
+ Self::WaitingForTask => write!(f, "waiting_for_task"),
+ Self::WaitingForUser => write!(f, "waiting_for_user"),
+ Self::Error => write!(f, "error"),
+ Self::Completed => write!(f, "completed"),
+ }
+ }
+}
+
+impl std::str::FromStr for SupervisorActivityState {
+ type Err = String;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match s {
+ "initializing" => Ok(Self::Initializing),
+ "planning" => Ok(Self::Planning),
+ "executing" => Ok(Self::Executing),
+ "waiting_for_task" => Ok(Self::WaitingForTask),
+ "waiting_for_user" => Ok(Self::WaitingForUser),
+ "error" => Ok(Self::Error),
+ "completed" => Ok(Self::Completed),
+ _ => Err(format!("Unknown supervisor state: {}", s)),
+ }
+ }
+}
+
/// Supervisor state for contract supervisor tasks
/// Enables resumption after interruption
#[derive(Debug, Clone, FromRow, Serialize, ToSchema)]
@@ -1971,10 +2029,59 @@ pub struct SupervisorState {
pub last_activity: DateTime<Utc>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
+
+ // Enhanced fields for Phase 3 crash recovery
+ /// Current supervisor activity state
+ pub state: String,
+ /// Human-readable current activity description
+ pub current_activity: Option<String>,
+ /// Progress percentage (0-100)
+ pub progress: Option<i32>,
+ /// Error message if in error state
+ pub error_message: Option<String>,
+ /// All task UUIDs spawned by this supervisor
+ #[sqlx(try_from = "Vec<Uuid>")]
+ pub spawned_task_ids: Vec<Uuid>,
+ /// Pending question UUID if waiting for user input
+ pub pending_question_id: Option<Uuid>,
+ /// Last LLM response for context restoration
+ pub last_llm_response: Option<String>,
+ /// Number of times this supervisor has been restored
+ pub restoration_count: Option<i32>,
+ /// Timestamp of last restoration
+ pub last_restored_at: Option<DateTime<Utc>>,
+}
+
+impl SupervisorState {
+ /// Get the parsed supervisor activity state
+ pub fn activity_state(&self) -> SupervisorActivityState {
+ self.state.parse().unwrap_or(SupervisorActivityState::Initializing)
+ }
+
+ /// Check if this supervisor state is restorable
+ pub fn is_restorable(&self) -> bool {
+ let state = self.activity_state();
+ // Can restore if not in a terminal or error state
+ !matches!(state, SupervisorActivityState::Completed | SupervisorActivityState::Error)
+ }
+
+ /// Check if supervisor is waiting for something
+ pub fn is_waiting(&self) -> bool {
+ let state = self.activity_state();
+ matches!(
+ state,
+ SupervisorActivityState::WaitingForTask | SupervisorActivityState::WaitingForUser
+ )
+ }
+
+ /// Check if supervisor has pending questions
+ pub fn has_pending_question(&self) -> bool {
+ self.pending_question_id.is_some()
+ }
}
/// Request to update supervisor state
-#[derive(Debug, Deserialize, ToSchema)]
+#[derive(Debug, Default, Deserialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct UpdateSupervisorStateRequest {
/// Updated conversation history
@@ -1983,6 +2090,109 @@ pub struct UpdateSupervisorStateRequest {
pub pending_task_ids: Option<Vec<Uuid>>,
/// Current contract phase
pub phase: Option<String>,
+ /// Current supervisor activity state
+ pub state: Option<String>,
+ /// Human-readable current activity description
+ pub current_activity: Option<String>,
+ /// Progress percentage (0-100)
+ pub progress: Option<i32>,
+ /// Error message if in error state
+ pub error_message: Option<String>,
+ /// All spawned task IDs
+ pub spawned_task_ids: Option<Vec<Uuid>>,
+ /// Pending question UUID
+ pub pending_question_id: Option<Uuid>,
+ /// Clear the pending question (set to None)
+ #[serde(default)]
+ pub clear_pending_question: bool,
+ /// Last LLM response
+ pub last_llm_response: Option<String>,
+}
+
+/// Request to save supervisor state at specific save points
+#[derive(Debug, Deserialize, Serialize, ToSchema)]
+#[serde(rename_all = "camelCase")]
+pub struct SaveSupervisorStateRequest {
+ /// The save point type that triggered this save
+ pub save_point: SupervisorSavePoint,
+ /// Updated conversation history (if available)
+ pub conversation_history: Option<serde_json::Value>,
+ /// Current state
+ pub state: Option<String>,
+ /// Current activity description
+ pub current_activity: Option<String>,
+ /// Progress percentage
+ pub progress: Option<i32>,
+ /// Last LLM response
+ pub last_llm_response: Option<String>,
+ /// Task that was spawned (for task_spawn save point)
+ pub spawned_task_id: Option<Uuid>,
+ /// Question that was asked (for question_asked save point)
+ pub question_id: Option<Uuid>,
+ /// Error message (for error save point)
+ pub error_message: Option<String>,
+}
+
+/// Types of save points for supervisor state persistence
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum SupervisorSavePoint {
+ /// Save on every LLM response
+ LlmResponse,
+ /// Save when spawning a new task
+ TaskSpawn,
+ /// Save when asking a question to user
+ QuestionAsked,
+ /// Save when phase changes
+ PhaseChange,
+ /// Lightweight heartbeat update
+ Heartbeat,
+ /// Save when an error occurs
+ Error,
+ /// Save when task completes
+ TaskComplete,
+}
+
+/// Supervisor restoration context
+/// Contains all information needed to restore a supervisor after crash
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+#[serde(rename_all = "camelCase")]
+pub struct SupervisorRestorationContext {
+ /// The restored supervisor state
+ pub state: SupervisorState,
+ /// Tasks that were pending when the crash occurred
+ pub pending_tasks: Vec<TaskSummary>,
+ /// Pending question that needs re-delivery (if any)
+ pub pending_question: Option<PendingQuestionInfo>,
+ /// Whether state was fully valid or partially recovered
+ pub restoration_type: RestorationResult,
+ /// Human-readable restoration message
+ pub message: String,
+}
+
+/// Information about a pending question for restoration
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+#[serde(rename_all = "camelCase")]
+pub struct PendingQuestionInfo {
+ pub question_id: Uuid,
+ pub question: String,
+ pub choices: Vec<String>,
+ pub context: Option<String>,
+ pub multi_select: bool,
+}
+
+/// Result of restoration attempt
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum RestorationResult {
+ /// State was fully valid and restored
+ FullRestore,
+ /// State was valid but restored from last checkpoint
+ CheckpointRestore,
+ /// Started fresh due to invalid/missing state
+ FreshStart,
+ /// Partial restoration with some context lost
+ PartialRestore,
}
// ============================================================================