diff options
| author | soryu <soryu@soryu.co> | 2026-01-15 22:33:47 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-01-15 22:33:47 +0000 |
| commit | 6ee2e75834bff187b8c262e0798ef365bc21cd59 (patch) | |
| tree | d4bd61c7740835acfc9a9dff952d1d088ff6d535 /makima/src/db/models.rs | |
| parent | 908973b5c08a8b7b624880843c512e8bddf37896 (diff) | |
| download | soryu-6ee2e75834bff187b8c262e0798ef365bc21cd59.tar.gz soryu-6ee2e75834bff187b8c262e0798ef365bc21cd59.zip | |
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
Diffstat (limited to 'makima/src/db/models.rs')
| -rw-r--r-- | makima/src/db/models.rs | 233 |
1 files changed, 233 insertions, 0 deletions
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<i32>, } + +// ============================================================================= +// 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<Uuid>, + /// 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<serde_json::Value>, + pub created_at: DateTime<Utc>, +} + +/// 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<Uuid>, + pub task_id: Option<Uuid>, + pub event_type: String, + pub event_subtype: Option<String>, + pub phase: Option<String>, + #[sqlx(json)] + pub event_data: serde_json::Value, + pub created_at: DateTime<Utc>, +} + +/// 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<Utc>, + #[serde(skip_serializing_if = "Option::is_none")] + pub tool_calls: Option<Vec<ToolCallInfo>>, + #[serde(skip_serializing_if = "Option::is_none")] + pub tool_name: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + pub tool_input: Option<serde_json::Value>, + #[serde(skip_serializing_if = "Option::is_none")] + pub tool_result: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + pub is_error: Option<bool>, + #[serde(skip_serializing_if = "Option::is_none")] + pub token_count: Option<i32>, + #[serde(skip_serializing_if = "Option::is_none")] + pub cost_usd: Option<f64>, +} + +/// 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<String>, + pub event_types: Option<Vec<String>>, + pub from: Option<DateTime<Utc>>, + pub to: Option<DateTime<Utc>>, + pub limit: Option<i32>, + pub cursor: Option<String>, +} + +/// Request to resume a supervisor +#[derive(Debug, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ResumeSupervisorRequest { + pub target_daemon_id: Option<Uuid>, + /// Resume mode: 'continue', 'restart_phase', 'from_checkpoint' + pub resume_mode: String, + pub checkpoint_id: Option<Uuid>, + pub additional_context: Option<String>, +} + +/// Request to resume from a checkpoint +#[derive(Debug, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ResumeFromCheckpointRequest { + pub task_name: Option<String>, + pub plan: String, + pub include_conversation: Option<bool>, + pub target_daemon_id: Option<Uuid>, +} + +/// Request to rewind a task to a checkpoint +#[derive(Debug, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct RewindTaskRequest { + pub checkpoint_id: Option<Uuid>, + pub checkpoint_sha: Option<String>, + /// Preserve mode: 'discard', 'create_branch', 'stash' + pub preserve_mode: String, + pub branch_name: Option<String>, +} + +/// Request to rewind a conversation +#[derive(Debug, Deserialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct RewindConversationRequest { + pub to_message_id: Option<String>, + pub to_timestamp: Option<DateTime<Utc>>, + pub by_message_count: Option<i32>, + pub rewind_code: Option<bool>, +} + +/// 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<bool>, + pub create_branch: Option<bool>, + pub branch_name: Option<String>, +} + +/// Response for contract history endpoint +#[derive(Debug, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ContractHistoryResponse { + pub contract_id: Uuid, + pub entries: Vec<HistoryEvent>, + pub total_count: i64, + pub cursor: Option<String>, +} + +/// 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<ConversationMessage>, + pub total_tokens: Option<i32>, + pub total_cost: Option<f64>, +} + +/// 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<Utc>, + pub pending_task_ids: Vec<Uuid>, + pub messages: Vec<ConversationMessage>, + pub spawned_tasks: Vec<TaskReference>, +} + +/// 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<Utc>, + pub completed_at: Option<DateTime<Utc>>, +} + +/// 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<PreservedState>, +} + +/// 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<String>, + pub conversation_included: bool, + pub message_count: Option<i32>, +} + +/// Fork point information in fork response +#[derive(Debug, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct ForkPoint { + pub fork_type: String, + pub checkpoint: Option<TaskCheckpoint>, + pub timestamp: DateTime<Utc>, +} |
