Skip to main content

codetether_agent/tool/session_recall/
tool_struct.rs

1//! `SessionRecallTool` struct and `Tool` trait implementation.
2
3use crate::provider::Provider;
4use crate::rlm::RlmConfig;
5use crate::tool::{Tool, ToolResult};
6use anyhow::Result;
7use async_trait::async_trait;
8use std::sync::Arc;
9
10const DEFAULT_SESSION_LIMIT: usize = 3;
11/// Hard cap preventing OOM from unreasonably high `limit`.
12const MAX_SESSION_LIMIT: usize = 5;
13
14const DESCRIPTION: &str = "\
15RECALL FROM YOUR OWN PAST SESSIONS. Call this whenever you see \
16[AUTO CONTEXT COMPRESSION] and need specifics, or the user references \
17something from earlier. Pass a natural-language `query`. Optionally \
18pin `session_id` or widen `limit` (default 3, max 5). Distinct from \
19`memory`: `memory` is curated notes; `session_recall` is the raw archive.";
20
21/// RLM-backed recall tool over persisted session history.
22pub struct SessionRecallTool {
23    provider: Arc<dyn Provider>,
24    model: String,
25    config: RlmConfig,
26}
27
28impl SessionRecallTool {
29    pub fn new(provider: Arc<dyn Provider>, model: String, config: RlmConfig) -> Self {
30        Self {
31            provider,
32            model,
33            config,
34        }
35    }
36}
37
38#[async_trait]
39impl Tool for SessionRecallTool {
40    fn id(&self) -> &str {
41        "session_recall"
42    }
43    fn name(&self) -> &str {
44        "SessionRecall"
45    }
46    fn description(&self) -> &str {
47        DESCRIPTION
48    }
49    fn parameters(&self) -> serde_json::Value {
50        super::schema::parameters()
51    }
52
53    async fn execute(&self, args: serde_json::Value) -> Result<ToolResult> {
54        let query = match args["query"].as_str() {
55            Some(q) if !q.trim().is_empty() => q.to_string(),
56            _ => return Ok(ToolResult::error("query is required")),
57        };
58        let sid = args["session_id"].as_str().map(str::to_string);
59        let limit = args["limit"]
60            .as_u64()
61            .map(|n| n as usize)
62            .unwrap_or(DEFAULT_SESSION_LIMIT)
63            .clamp(1, MAX_SESSION_LIMIT);
64
65        let (ctx, sources) = match super::context::build_recall_context(sid, limit).await {
66            Ok(ok) => ok,
67            Err(e) => {
68                return Ok(super::faults::fault_result(
69                    super::faults::fault_from_error(&e),
70                    format!("recall load failed: {e}"),
71                ));
72            }
73        };
74        if ctx.trim().is_empty() {
75            return Ok(super::faults::fault_result(
76                crate::session::Fault::NoMatch,
77                "No prior session history found.",
78            ));
79        }
80        super::rlm_run::run_recall(
81            &ctx,
82            &sources,
83            &query,
84            Arc::clone(&self.provider),
85            &self.model,
86            &self.config,
87        )
88        .await
89    }
90}