codetether_agent/tool/context_pin/
execute.rs1use 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
10pub 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}