Skip to main content

zeph_core/agent/
memory_commands.rs

1// SPDX-FileCopyrightText: 2026 Andrei G <bug-ops>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use std::fmt::Write as _;
5
6use zeph_memory::MessageId;
7
8use super::{Agent, error::AgentError};
9use crate::channel::Channel;
10
11impl<C: Channel> Agent<C> {
12    /// Dispatch `/memory [subcommand]` slash command.
13    ///
14    /// # Errors
15    ///
16    /// Returns an error if the channel send fails or database query fails.
17    pub async fn handle_memory_command(&mut self, input: &str) -> Result<(), AgentError> {
18        let args = input.strip_prefix("/memory").unwrap_or("").trim();
19
20        if args.is_empty() || args == "tiers" {
21            return self.handle_memory_tiers().await;
22        }
23
24        if args.starts_with("promote") {
25            let rest = args.strip_prefix("promote").unwrap_or("").trim();
26            return self.handle_memory_promote(rest).await;
27        }
28
29        self.channel
30            .send("Unknown /memory subcommand. Available: /memory tiers, /memory promote <id>...")
31            .await?;
32        Ok(())
33    }
34
35    async fn handle_memory_tiers(&mut self) -> Result<(), AgentError> {
36        let Some(memory) = self.memory_state.memory.clone() else {
37            self.channel.send("Memory not configured.").await?;
38            return Ok(());
39        };
40
41        match memory.sqlite().count_messages_by_tier().await {
42            Ok((episodic, semantic)) => {
43                let mut out = String::new();
44                let _ = writeln!(out, "Memory tiers:");
45                let _ = writeln!(out, "  Working:  (current context window — virtual)");
46                let _ = writeln!(out, "  Episodic: {episodic} messages");
47                let _ = writeln!(out, "  Semantic: {semantic} facts");
48                self.channel.send(out.trim_end()).await?;
49            }
50            Err(e) => {
51                let msg = format!("Failed to query tier stats: {e}");
52                self.channel.send(&msg).await?;
53            }
54        }
55
56        Ok(())
57    }
58
59    async fn handle_memory_promote(&mut self, args: &str) -> Result<(), AgentError> {
60        let Some(memory) = self.memory_state.memory.clone() else {
61            self.channel.send("Memory not configured.").await?;
62            return Ok(());
63        };
64
65        let ids: Vec<MessageId> = args
66            .split_whitespace()
67            .filter_map(|s| s.parse::<i64>().ok().map(MessageId))
68            .collect();
69
70        if ids.is_empty() {
71            self.channel
72                .send("Usage: /memory promote <id> [id...]\nExample: /memory promote 42 43 44")
73                .await?;
74            return Ok(());
75        }
76
77        match memory.sqlite().manual_promote(&ids).await {
78            Ok(count) => {
79                let msg = format!("Promoted {count} message(s) to semantic tier.");
80                self.channel.send(&msg).await?;
81            }
82            Err(e) => {
83                let msg = format!("Promotion failed: {e}");
84                self.channel.send(&msg).await?;
85            }
86        }
87
88        Ok(())
89    }
90}