1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
//! Placeholder tool for session history compatibility.
//!
//! When a session references a tool that is no longer registered,
//! [`NoopTool`] is auto-injected to prevent deserialization failures.
//! It returns an error result explaining the tool is no longer available.
//!
//! # Example
//!
//! ```
//! use swink_agent::{AgentTool, NoopTool};
//!
//! let tool = NoopTool::new("old_tool");
//! assert_eq!(tool.name(), "old_tool");
//! assert!(!tool.requires_approval());
//! ```
use std::sync::Arc;
use serde_json::Value;
use tokio_util::sync::CancellationToken;
use crate::tool::{AgentTool, AgentToolResult, ToolFuture, permissive_object_schema};
// ─── NoopTool ──────────────────────────────────────────────────────────────
/// A placeholder tool that returns an error message when invoked.
///
/// Used for session history compatibility when a tool referenced in a saved
/// session no longer exists in the agent's registry.
#[derive(Debug, Clone)]
pub struct NoopTool {
name: String,
}
impl NoopTool {
/// Create a new `NoopTool` with the given name.
#[must_use]
pub fn new(name: impl Into<String>) -> Self {
Self { name: name.into() }
}
}
impl AgentTool for NoopTool {
fn name(&self) -> &str {
&self.name
}
fn label(&self) -> &str {
&self.name
}
fn description(&self) -> &'static str {
"This tool is no longer available."
}
fn parameters_schema(&self) -> &Value {
// Accept any arguments (the tool won't execute them anyway).
static SCHEMA: std::sync::LazyLock<Value> =
std::sync::LazyLock::new(permissive_object_schema);
&SCHEMA
}
fn requires_approval(&self) -> bool {
false
}
fn execute(
&self,
_tool_call_id: &str,
_params: Value,
_cancellation_token: CancellationToken,
_on_update: Option<Box<dyn Fn(AgentToolResult) + Send + Sync>>,
_state: Arc<std::sync::RwLock<crate::SessionState>>,
_credential: Option<crate::credential::ResolvedCredential>,
) -> ToolFuture<'_> {
let name = self.name.clone();
Box::pin(async move {
AgentToolResult::error(format!(
"Tool '{name}' is no longer available. It may have been removed or renamed."
))
})
}
}
// ─── Compile-time Send + Sync assertion ─────────────────────────────────────
const _: () = {
const fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<NoopTool>();
};
#[cfg(test)]
#[path = "noop_tool_tests.rs"]
mod tests;