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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
// SPDX-FileCopyrightText: 2026 Andrei G <bug-ops>
// SPDX-License-Identifier: MIT OR Apache-2.0
//! Boilerplate-reduction macros for [`ToolExecutor`](crate::ToolExecutor) and
//! [`ErasedToolExecutor`](crate::ErasedToolExecutor) implementors.
//!
//! Issue #6019: both traits used to give the six risk-bearing methods
//! (`requires_confirmation`, `execute_tool_call_confirmed`, the checkpoint trio, and
//! `is_tool_speculatable` — plus their `_erased` counterparts) permissive default bodies.
//! Wrapper types that forgot to override one silently inherited a default that could
//! disable a security check, a checkpoint capability, or a confirmation gate. This
//! recurred five times across prior PRs. The traits no longer provide those defaults —
//! every implementor must now supply all six, and the compiler enforces it.
//!
//! These four macros exist only to keep that compiler-forced boilerplate short. They do
//! **not** themselves close the defect class — only the removal of the default bodies
//! does that. A macro invoked on the wrong type (e.g. `tool_executor_no_inner_defaults!()`
//! on a wrapper that owns an inner executor) silently reintroduces the exact bug this
//! issue fixes, because `macro_rules!` cannot check "this type has no delegate field."
//! Read each macro's own doc comment before using it.
/// Forwards the four mechanical capability methods of
/// [`ToolExecutor`](crate::ToolExecutor) — the checkpoint trio and `is_tool_speculatable`
/// — to `self.$inner`.
///
/// Use inside `impl ToolExecutor for YourWrapper` where `$inner` is the **field name**
/// (an identifier, not an expression — macro hygiene forbids capturing `self` at item
/// position) of a field whose type implements [`ToolExecutor`](crate::ToolExecutor).
///
/// The two policy methods, `requires_confirmation` and `execute_tool_call_confirmed`, are
/// intentionally **not** emitted by this macro — wrappers that gate on confirmation or
/// checkpoint policy must implement those two explicitly, so the compiler forces every
/// wrapper author to make a deliberate decision about them instead of inheriting one
/// silently.
///
/// # Examples
///
/// ```rust
/// use zeph_tools::{ToolExecutor, ToolCall, ToolOutput, ToolError};
///
/// struct PassThrough<T> {
/// inner: T,
/// }
///
/// impl<T: ToolExecutor> ToolExecutor for PassThrough<T> {
/// async fn execute(&self, response: &str) -> Result<Option<ToolOutput>, ToolError> {
/// self.inner.execute(response).await
/// }
///
/// fn requires_confirmation(&self, call: &ToolCall) -> bool {
/// self.inner.requires_confirmation(call)
/// }
///
/// async fn execute_tool_call_confirmed(
/// &self,
/// call: &ToolCall,
/// ) -> Result<Option<ToolOutput>, ToolError> {
/// self.inner.execute_tool_call_confirmed(call).await
/// }
///
/// zeph_tools::tool_executor_forward!(inner);
/// }
/// ```
/// Emits the six risk-bearing [`ToolExecutor`](crate::ToolExecutor) methods with the same
/// trivial bodies the trait's removed defaults used to provide: no confirmation required,
/// confirmed execution falls back to `execute_tool_call`, checkpoints unsupported, not
/// speculatable.
///
/// # Use ONLY on leaf executors that own no wrapped executor
///
/// Invoking this macro on a type that wraps another [`ToolExecutor`](crate::ToolExecutor)
/// (i.e. has an `inner`/delegate field) silently disables forwarding for all six methods
/// and **reintroduces issue #6019**. Wrappers must use [`tool_executor_forward!`] for the
/// mechanical four and hand-write `requires_confirmation` /
/// `execute_tool_call_confirmed`. `macro_rules!` has no way to enforce this at compile
/// time — review carefully.
///
/// # Examples
///
/// ```rust
/// use zeph_tools::{ToolExecutor, ToolOutput, ToolError};
///
/// struct EchoExecutor;
///
/// impl ToolExecutor for EchoExecutor {
/// async fn execute(&self, _response: &str) -> Result<Option<ToolOutput>, ToolError> {
/// Ok(None)
/// }
///
/// zeph_tools::tool_executor_no_inner_defaults!();
/// }
/// ```
/// Erased-trait counterpart of [`tool_executor_forward!`]: forwards the checkpoint trio
/// and `is_tool_speculatable_erased` to `self.$inner`.
///
/// Use inside `impl ErasedToolExecutor for YourWrapper` where `$inner` is the **field
/// name** (an identifier, not an expression — see [`tool_executor_forward!`] for why) of
/// a field whose type implements [`ErasedToolExecutor`](crate::ErasedToolExecutor). As
/// with the static-side macro, the policy methods `requires_confirmation_erased` and
/// `execute_tool_call_confirmed_erased` are not emitted and must be hand-written.
/// Erased-trait counterpart of [`tool_executor_no_inner_defaults!`]: emits the six
/// risk-bearing [`ErasedToolExecutor`](crate::ErasedToolExecutor) methods with the same
/// trivial bodies the trait's removed defaults used to provide.
///
/// # Use ONLY on leaf executors that own no wrapped executor
///
/// Invoking this macro on a type that wraps another
/// [`ErasedToolExecutor`](crate::ErasedToolExecutor) silently disables forwarding for all
/// six methods and **reintroduces issue #6019**. Wrappers must use
/// [`erased_tool_executor_forward!`] for the mechanical four and hand-write
/// `requires_confirmation_erased` / `execute_tool_call_confirmed_erased`.