Skip to main content

codetether_agent/tool/context_pin/
execute.rs

1//! Tool trait implementation for `context_pin`.
2
3use super::super::context_helpers::load_latest_session;
4use super::super::{Tool, ToolResult};
5use super::logic::apply_pin;
6use anyhow::Result;
7use async_trait::async_trait;
8use serde_json::{Value, json};
9
10/// Pin or unpin a turn by message index.
11pub struct ContextPinTool;
12
13#[async_trait]
14impl Tool for ContextPinTool {
15    fn id(&self) -> &str {
16        "context_pin"
17    }
18    fn name(&self) -> &str {
19        "ContextPin"
20    }
21
22    fn description(&self) -> &str {
23        "Pin or unpin a conversation turn so it is never dropped during \
24         context compression. Pinned turns are treated as hard constraints. \
25         Pass `action: \"pin\"` or `action: \"unpin\"` along with the \
26         0-based `turn_index`. Pinned turns survive across resets."
27    }
28
29    fn parameters(&self) -> Value {
30        json!({
31            "type": "object",
32            "properties": {
33                "action": { "type": "string", "enum": ["pin", "unpin"],
34                    "description": "Whether to pin or unpin the turn." },
35                "turn_index": { "type": "integer", "minimum": 0,
36                    "description": "0-based index into the session transcript." }
37            },
38            "required": ["action", "turn_index"]
39        })
40    }
41
42    async fn execute(&self, args: Value) -> Result<ToolResult> {
43        let action = args["action"].as_str().unwrap_or("").to_lowercase();
44        let idx = match args["turn_index"].as_u64() {
45            Some(i) => i as usize,
46            None => {
47                return Ok(ToolResult::error(
48                    "`turn_index` must be a non-negative integer.",
49                ));
50            }
51        };
52        let mut session = match load_latest_session().await {
53            Ok(Some(s)) => s,
54            Ok(None) => return Ok(ToolResult::error("No active session.")),
55            Err(e) => return Ok(ToolResult::error(&format!("Load failed: {e}"))),
56        };
57        match apply_pin(&mut session, idx, &action).await {
58            Ok(msg) => Ok(ToolResult::success(msg)),
59            Err(e) => Ok(ToolResult::error(&e.to_string())),
60        }
61    }
62}