From 6ee2e75834bff187b8c262e0798ef365bc21cd59 Mon Sep 17 00:00:00 2001 From: soryu Date: Thu, 15 Jan 2026 22:33:47 +0000 Subject: Add resume and history system for makima (#1) This PR implements a comprehensive resume and history system that enables: 1. **History Viewing** - View complete conversation history for contracts across all phases - View conversation history for individual tasks - View task output/tool call history with timestamps - View checkpoint history - Timeline view showing all activities 2. **Resume System** - Resume interrupted supervisor conversations with full context - Resume interrupted task conversations - Resume from specific checkpoints - Continue tasks from previous task state (worktree inheritance) 3. **Rewind/Restore Features** - Rewind code to any checkpoint (git restore) - Rewind conversation to any point - Create new branches from historical points - Fork tasks from any point in history - New migration: 20250117000000_history_tables.sql - conversation_snapshots table for storing conversation state - history_events table for unified timeline - Added forking fields to tasks table - Added conversation_snapshot_id to task_checkpoints - ConversationSnapshot, HistoryEvent, ConversationMessage - Request/response types for resume and rewind operations - Query filter types for history endpoints - CRUD functions for conversation_snapshots - CRUD functions for history_events - Task conversation retrieval from task_events - GET /api/v1/contracts/{id}/history - GET /api/v1/contracts/{id}/supervisor/conversation - GET /api/v1/mesh/tasks/{id}/conversation - GET /api/v1/timeline - POST /api/v1/contracts/{id}/supervisor/resume - POST /api/v1/mesh/tasks/{id}/rewind - POST /api/v1/mesh/tasks/{id}/fork - POST /api/v1/mesh/tasks/{id}/checkpoints/{cid}/resume - POST /api/v1/mesh/tasks/{id}/checkpoints/{cid}/branch - POST /api/v1/contracts/{id}/supervisor/conversation/rewind - task-history: View task conversation history - task-checkpoints: List task checkpoints - resume: Resume supervisor after interruption - task-resume-from: Resume task from checkpoint - task-rewind: Rewind task code to checkpoint - task-fork: Fork task from historical point - rewind-conversation: Rewind supervisor conversation --- makima/src/db/models.rs | 233 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) (limited to 'makima/src/db/models.rs') diff --git a/makima/src/db/models.rs b/makima/src/db/models.rs index 40d4109..4419580 100644 --- a/makima/src/db/models.rs +++ b/makima/src/db/models.rs @@ -1559,3 +1559,236 @@ pub struct RepositorySuggestionsQuery { /// Limit results (default: 10) pub limit: Option, } + +// ============================================================================= +// Resume and History System Types +// ============================================================================= + +/// Conversation snapshot for task resumption +#[derive(Debug, Clone, FromRow, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ConversationSnapshot { + pub id: Uuid, + pub task_id: Uuid, + pub checkpoint_id: Option, + /// Snapshot type: 'auto', 'manual', 'checkpoint' + pub snapshot_type: String, + pub message_count: i32, + #[sqlx(json)] + pub conversation_state: serde_json::Value, + #[sqlx(json)] + pub metadata: Option, + pub created_at: DateTime, +} + +/// History event for contract/task history tracking +#[derive(Debug, Clone, FromRow, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct HistoryEvent { + pub id: Uuid, + pub owner_id: Uuid, + pub contract_id: Option, + pub task_id: Option, + pub event_type: String, + pub event_subtype: Option, + pub phase: Option, + #[sqlx(json)] + pub event_data: serde_json::Value, + pub created_at: DateTime, +} + +/// Unified conversation message for API responses +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ConversationMessage { + pub id: String, + /// Message role: 'user', 'assistant', 'system', 'tool' + pub role: String, + pub content: String, + pub timestamp: DateTime, + #[serde(skip_serializing_if = "Option::is_none")] + pub tool_calls: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub tool_name: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub tool_input: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub tool_result: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub is_error: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub token_count: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub cost_usd: Option, +} + +/// Tool call information within a conversation message +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ToolCallInfo { + pub id: String, + pub name: String, + pub input: serde_json::Value, +} + +/// Query filters for history endpoints +#[derive(Debug, Deserialize, ToSchema, Default)] +#[serde(rename_all = "camelCase")] +pub struct HistoryQueryFilters { + pub phase: Option, + pub event_types: Option>, + pub from: Option>, + pub to: Option>, + pub limit: Option, + pub cursor: Option, +} + +/// Request to resume a supervisor +#[derive(Debug, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ResumeSupervisorRequest { + pub target_daemon_id: Option, + /// Resume mode: 'continue', 'restart_phase', 'from_checkpoint' + pub resume_mode: String, + pub checkpoint_id: Option, + pub additional_context: Option, +} + +/// Request to resume from a checkpoint +#[derive(Debug, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ResumeFromCheckpointRequest { + pub task_name: Option, + pub plan: String, + pub include_conversation: Option, + pub target_daemon_id: Option, +} + +/// Request to rewind a task to a checkpoint +#[derive(Debug, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct RewindTaskRequest { + pub checkpoint_id: Option, + pub checkpoint_sha: Option, + /// Preserve mode: 'discard', 'create_branch', 'stash' + pub preserve_mode: String, + pub branch_name: Option, +} + +/// Request to rewind a conversation +#[derive(Debug, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct RewindConversationRequest { + pub to_message_id: Option, + pub to_timestamp: Option>, + pub by_message_count: Option, + pub rewind_code: Option, +} + +/// Request to fork a task +#[derive(Debug, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ForkTaskRequest { + /// Fork from type: 'checkpoint', 'timestamp', 'message_id' + pub fork_from_type: String, + pub fork_from_value: String, + pub new_task_name: String, + pub new_task_plan: String, + pub include_conversation: Option, + pub create_branch: Option, + pub branch_name: Option, +} + +/// Response for contract history endpoint +#[derive(Debug, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ContractHistoryResponse { + pub contract_id: Uuid, + pub entries: Vec, + pub total_count: i64, + pub cursor: Option, +} + +/// Response for task conversation endpoint +#[derive(Debug, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct TaskConversationResponse { + pub task_id: Uuid, + pub task_name: String, + pub status: String, + pub messages: Vec, + pub total_tokens: Option, + pub total_cost: Option, +} + +/// Response for supervisor conversation endpoint +#[derive(Debug, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct SupervisorConversationResponse { + pub contract_id: Uuid, + pub supervisor_task_id: Uuid, + pub phase: String, + pub last_activity: DateTime, + pub pending_task_ids: Vec, + pub messages: Vec, + pub spawned_tasks: Vec, +} + +/// Reference to a task for history/conversation responses +#[derive(Debug, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct TaskReference { + pub task_id: Uuid, + pub task_name: String, + pub status: String, + pub created_at: DateTime, + pub completed_at: Option>, +} + +/// Response for task rewind operation +#[derive(Debug, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct RewindTaskResponse { + pub task_id: Uuid, + pub rewinded_to: CheckpointInfo, + pub preserved_as: Option, +} + +/// Checkpoint information in rewind response +#[derive(Debug, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct CheckpointInfo { + pub checkpoint_number: i32, + pub sha: String, + pub message: String, +} + +/// Preserved state information in rewind response +#[derive(Debug, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct PreservedState { + /// State type: 'branch' or 'stash' + pub state_type: String, + pub reference: String, +} + +/// Response for task fork operation +#[derive(Debug, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ForkTaskResponse { + pub new_task_id: Uuid, + pub source_task_id: Uuid, + pub fork_point: ForkPoint, + pub branch_name: Option, + pub conversation_included: bool, + pub message_count: Option, +} + +/// Fork point information in fork response +#[derive(Debug, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ForkPoint { + pub fork_type: String, + pub checkpoint: Option, + pub timestamp: DateTime, +} -- cgit v1.2.3