summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-02-10 23:27:26 +0000
committersoryu <soryu@soryu.co>2026-02-10 23:27:26 +0000
commit22de1ec23196431ee536ada639a595f96a2caf25 (patch)
tree851f748089d1ad26ffb9872f948eca9bb0864ace
parent339c1769379a851c4126021132573bd4b7994cf2 (diff)
downloadsoryu-makima/makima--add-an-optional-memory-system-for-directiv-97239c63.tar.gz
soryu-makima/makima--add-an-optional-memory-system-for-directiv-97239c63.zip
feat: makima: Add an optional memory system for directives: Add repository functions for directive memory CRUDmakima/makima--add-an-optional-memory-system-for-directiv-97239c63
-rw-r--r--makima/src/db/models.rs46
-rw-r--r--makima/src/db/repository.rs126
2 files changed, 166 insertions, 6 deletions
diff --git a/makima/src/db/models.rs b/makima/src/db/models.rs
index 9159fd5..726c166 100644
--- a/makima/src/db/models.rs
+++ b/makima/src/db/models.rs
@@ -2711,6 +2711,7 @@ pub struct Directive {
pub local_path: Option<String>,
pub base_branch: Option<String>,
pub orchestrator_task_id: Option<Uuid>,
+ pub memory_enabled: bool,
pub goal_updated_at: DateTime<Utc>,
pub started_at: Option<DateTime<Utc>>,
pub version: i32,
@@ -2758,6 +2759,7 @@ pub struct DirectiveSummary {
pub status: String,
pub repository_url: Option<String>,
pub orchestrator_task_id: Option<Uuid>,
+ pub memory_enabled: bool,
pub version: i32,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
@@ -2784,6 +2786,8 @@ pub struct CreateDirectiveRequest {
pub repository_url: Option<String>,
pub local_path: Option<String>,
pub base_branch: Option<String>,
+ #[serde(default)]
+ pub memory_enabled: bool,
}
/// Request to update a directive.
@@ -2797,6 +2801,7 @@ pub struct UpdateDirectiveRequest {
pub local_path: Option<String>,
pub base_branch: Option<String>,
pub orchestrator_task_id: Option<Uuid>,
+ pub memory_enabled: Option<bool>,
pub version: Option<i32>,
}
@@ -2833,3 +2838,44 @@ pub struct UpdateDirectiveStepRequest {
pub task_id: Option<Uuid>,
pub order_index: Option<i32>,
}
+
+// =============================================================================
+// Directive Memory Types
+// =============================================================================
+
+/// A memory entry associated with a directive.
+#[derive(Debug, Clone, FromRow, Serialize, Deserialize, ToSchema)]
+#[serde(rename_all = "camelCase")]
+pub struct DirectiveMemory {
+ pub id: Uuid,
+ pub directive_id: Uuid,
+ pub key: String,
+ pub value: String,
+ pub category: Option<String>,
+ pub created_at: DateTime<Utc>,
+ pub updated_at: DateTime<Utc>,
+}
+
+/// Request to set (upsert) a directive memory entry.
+#[derive(Debug, Deserialize, ToSchema)]
+#[serde(rename_all = "camelCase")]
+pub struct SetDirectiveMemoryRequest {
+ pub key: String,
+ pub value: String,
+ pub category: Option<String>,
+}
+
+/// Request to batch set multiple directive memory entries.
+#[derive(Debug, Deserialize, ToSchema)]
+#[serde(rename_all = "camelCase")]
+pub struct BatchSetDirectiveMemoryRequest {
+ pub entries: Vec<SetDirectiveMemoryRequest>,
+}
+
+/// List response for directive memories.
+#[derive(Debug, Serialize, ToSchema)]
+#[serde(rename_all = "camelCase")]
+pub struct DirectiveMemoryListResponse {
+ pub memories: Vec<DirectiveMemory>,
+ pub total: i64,
+}
diff --git a/makima/src/db/repository.rs b/makima/src/db/repository.rs
index 930a73e..11c63fb 100644
--- a/makima/src/db/repository.rs
+++ b/makima/src/db/repository.rs
@@ -11,9 +11,9 @@ use super::models::{
ContractTypeTemplateRecord, ConversationMessage, ConversationSnapshot,
CreateContractRequest, CreateFileRequest, CreateTaskRequest,
CreateTemplateRequest, Daemon, DaemonTaskAssignment, DaemonWithCapacity,
- DeliverableDefinition, Directive, DirectiveStep, DirectiveSummary,
+ DeliverableDefinition, Directive, DirectiveMemory, DirectiveStep, DirectiveSummary,
CreateDirectiveRequest, CreateDirectiveStepRequest, UpdateDirectiveRequest,
- UpdateDirectiveStepRequest,
+ UpdateDirectiveStepRequest, SetDirectiveMemoryRequest,
File, FileSummary, FileVersion, HistoryEvent, HistoryQueryFilters,
MeshChatConversation, MeshChatMessageRecord, PhaseChangeResult, PhaseConfig,
PhaseDefinition, SupervisorHeartbeatRecord, SupervisorState,
@@ -4926,8 +4926,8 @@ pub async fn create_directive_for_owner(
) -> Result<Directive, sqlx::Error> {
sqlx::query_as::<_, Directive>(
r#"
- INSERT INTO directives (owner_id, title, goal, repository_url, local_path, base_branch)
- VALUES ($1, $2, $3, $4, $5, $6)
+ INSERT INTO directives (owner_id, title, goal, repository_url, local_path, base_branch, memory_enabled)
+ VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING *
"#,
)
@@ -4937,6 +4937,7 @@ pub async fn create_directive_for_owner(
.bind(&req.repository_url)
.bind(&req.local_path)
.bind(&req.base_branch)
+ .bind(req.memory_enabled)
.fetch_one(pool)
.await
}
@@ -4988,7 +4989,7 @@ pub async fn list_directives_for_owner(
r#"
SELECT
d.id, d.owner_id, d.title, d.goal, d.status, d.repository_url,
- d.orchestrator_task_id, d.version, d.created_at, d.updated_at,
+ d.orchestrator_task_id, d.memory_enabled, d.version, d.created_at, d.updated_at,
COALESCE((SELECT COUNT(*) FROM directive_steps WHERE directive_id = d.id), 0) as total_steps,
COALESCE((SELECT COUNT(*) FROM directive_steps WHERE directive_id = d.id AND status = 'completed'), 0) as completed_steps,
COALESCE((SELECT COUNT(*) FROM directive_steps WHERE directive_id = d.id AND status = 'running'), 0) as running_steps,
@@ -5040,12 +5041,14 @@ pub async fn update_directive_for_owner(
let local_path = req.local_path.as_deref().or(current.local_path.as_deref());
let base_branch = req.base_branch.as_deref().or(current.base_branch.as_deref());
let orchestrator_task_id = req.orchestrator_task_id.or(current.orchestrator_task_id);
+ let memory_enabled = req.memory_enabled.unwrap_or(current.memory_enabled);
let result = sqlx::query_as::<_, Directive>(
r#"
UPDATE directives
SET title = $3, goal = $4, status = $5, repository_url = $6, local_path = $7,
- base_branch = $8, orchestrator_task_id = $9, version = version + 1, updated_at = NOW()
+ base_branch = $8, orchestrator_task_id = $9, memory_enabled = $10,
+ version = version + 1, updated_at = NOW()
WHERE id = $1 AND owner_id = $2
RETURNING *
"#,
@@ -5059,6 +5062,7 @@ pub async fn update_directive_for_owner(
.bind(local_path)
.bind(base_branch)
.bind(orchestrator_task_id)
+ .bind(memory_enabled)
.fetch_optional(pool)
.await
.map_err(RepositoryError::Database)?;
@@ -5612,3 +5616,113 @@ pub async fn get_directive_max_generation(
.await?;
Ok(row.0.unwrap_or(0))
}
+
+// =============================================================================
+// Directive Memory
+// =============================================================================
+
+/// Set (upsert) a memory entry for a directive.
+pub async fn set_directive_memory(
+ pool: &PgPool,
+ directive_id: Uuid,
+ req: SetDirectiveMemoryRequest,
+) -> Result<DirectiveMemory, sqlx::Error> {
+ sqlx::query_as::<_, DirectiveMemory>(
+ r#"
+ INSERT INTO directive_memories (directive_id, key, value, category)
+ VALUES ($1, $2, $3, $4)
+ ON CONFLICT (directive_id, key) DO UPDATE
+ SET value = EXCLUDED.value, category = EXCLUDED.category, updated_at = NOW()
+ RETURNING *
+ "#,
+ )
+ .bind(directive_id)
+ .bind(&req.key)
+ .bind(&req.value)
+ .bind(&req.category)
+ .fetch_one(pool)
+ .await
+}
+
+/// Batch set memory entries for a directive.
+pub async fn batch_set_directive_memories(
+ pool: &PgPool,
+ directive_id: Uuid,
+ entries: Vec<SetDirectiveMemoryRequest>,
+) -> Result<Vec<DirectiveMemory>, sqlx::Error> {
+ let mut results = Vec::with_capacity(entries.len());
+ for entry in entries {
+ let mem = set_directive_memory(pool, directive_id, entry).await?;
+ results.push(mem);
+ }
+ Ok(results)
+}
+
+/// Get a single memory entry by key.
+pub async fn get_directive_memory(
+ pool: &PgPool,
+ directive_id: Uuid,
+ key: &str,
+) -> Result<Option<DirectiveMemory>, sqlx::Error> {
+ sqlx::query_as::<_, DirectiveMemory>(
+ r#"SELECT * FROM directive_memories WHERE directive_id = $1 AND key = $2"#,
+ )
+ .bind(directive_id)
+ .bind(key)
+ .fetch_optional(pool)
+ .await
+}
+
+/// List all memory entries for a directive, optionally filtered by category.
+pub async fn list_directive_memories(
+ pool: &PgPool,
+ directive_id: Uuid,
+ category: Option<&str>,
+) -> Result<Vec<DirectiveMemory>, sqlx::Error> {
+ if let Some(cat) = category {
+ sqlx::query_as::<_, DirectiveMemory>(
+ r#"SELECT * FROM directive_memories WHERE directive_id = $1 AND category = $2 ORDER BY key"#,
+ )
+ .bind(directive_id)
+ .bind(cat)
+ .fetch_all(pool)
+ .await
+ } else {
+ sqlx::query_as::<_, DirectiveMemory>(
+ r#"SELECT * FROM directive_memories WHERE directive_id = $1 ORDER BY key"#,
+ )
+ .bind(directive_id)
+ .fetch_all(pool)
+ .await
+ }
+}
+
+/// Delete a single memory entry by key.
+pub async fn delete_directive_memory(
+ pool: &PgPool,
+ directive_id: Uuid,
+ key: &str,
+) -> Result<bool, sqlx::Error> {
+ let result = sqlx::query(
+ r#"DELETE FROM directive_memories WHERE directive_id = $1 AND key = $2"#,
+ )
+ .bind(directive_id)
+ .bind(key)
+ .execute(pool)
+ .await?;
+ Ok(result.rows_affected() > 0)
+}
+
+/// Delete all memory entries for a directive.
+pub async fn clear_directive_memories(
+ pool: &PgPool,
+ directive_id: Uuid,
+) -> Result<u64, sqlx::Error> {
+ let result = sqlx::query(
+ r#"DELETE FROM directive_memories WHERE directive_id = $1"#,
+ )
+ .bind(directive_id)
+ .execute(pool)
+ .await?;
+ Ok(result.rows_affected())
+}