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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
use crate;
use crateContextTranslationStrategy;
use crate;
use crate*;
use Future;
use Pin;
use Arc;
// ── Context transformation callbacks ────────────────────────────────────────
/// All hook types use `Arc` (shared ownership) so they can be cloned into closures
/// and stored without lifetime complications. `Box<dyn Fn>` would suffice for single-owner
/// cases but `Arc` makes it trivially cheap to share across async tasks.
/// Converts `AgentMessage[]` → `Message[]` before each LLM call.
pub type ConvertToLlmFn = ;
/// Transforms the full context before `convert_to_llm` (for pruning, reordering, injection).
pub type TransformContextFn = ;
/// Returns pending messages (steering interrupts or follow-up work) when polled.
pub type GetMessagesFn = ;
// ── 0.9.0 async lifecycle hooks ─────────────────────────────────────────────
//
// All lifecycle Fn types below are async-trait-style boxed futures. To
// construct one from a sync closure body:
//
// ```rust,ignore
// let hook: BeforeTurnFn = Arc::new(|messages, turn| {
// Box::pin(async move {
// // sync logic ...
// true
// })
// });
// ```
//
// For async closure bodies, simply `.await` inside the `async move` block.
/// Boxed-future return type used by all 0.9.0 async lifecycle hooks. `T` is the
/// hook's logical return value (often `bool` for veto-returning hooks or `()`
/// for fire-and-forget hooks).
pub type HookFuture<'a, T> = ;
// ── Loop hooks ───────────────────────────────────────────────────────────────
/// Called once before the entire agent loop begins (before `AgentStart` is emitted).
///
/// Arguments: `(messages, loop_index)` — `messages` is the full context at the time of the call;
/// `loop_index` is always `0` (reserved for future multi-loop scenarios).
/// Return `false` to abort: `AgentEnd` is emitted immediately with an empty message list.
///
/// 0.9.0: async hook. Wrap sync bodies in `Box::pin(async move { ... })`.
pub type BeforeLoopFn =
;
/// Called once after the entire agent loop ends (after `AgentEnd` is emitted).
///
/// Arguments: `(new_messages, accumulated_usage)` — `new_messages` are the messages produced
/// by this loop call; `accumulated_usage` sums input/output tokens across all turns.
///
/// 0.9.0: async hook.
pub type AfterLoopFn =
;
// ── Turn hooks ───────────────────────────────────────────────────────────────
/// Called before each LLM turn (before `TurnStart` is emitted).
///
/// Arguments: `(messages, turn_index)` — `messages` is the full context (steering messages
/// queued for *this* turn are not yet visible); `turn_index` is 0-based.
/// Return `false` to abort the turn: no `TurnStart`/`TurnEnd` events are emitted,
/// but `AgentEnd` still fires normally.
///
/// 0.9.0: async hook.
pub type BeforeTurnFn =
;
/// Called after each LLM turn (after `TurnEnd` is emitted).
///
/// Arguments: `(messages, turn_usage)` — `turn_usage` covers only this turn's tokens.
/// Fires on both the normal path and the error/abort path.
///
/// 0.9.0: async hook.
pub type AfterTurnFn =
;
// ── Tool execution hooks ─────────────────────────────────────────────────────
/// Called before each tool call (before `ToolExecutionStart` is emitted).
///
/// Arguments: `(tool_name, tool_call_id, args)`.
/// Return `false` to skip the call: an error `ToolResult` is synthesised so the LLM still
/// receives a response, but `ToolExecutionStart`/`End` are **not** emitted.
///
/// 0.9.0: async hook.
pub type BeforeToolExecutionFn = ;
/// Called after each tool call (after `ToolExecutionEnd` is emitted).
///
/// Arguments: `(tool_name, tool_call_id, is_error)`.
///
/// 0.9.0: async hook.
pub type AfterToolExecutionFn =
;
/// Called before each incremental tool update (before `ToolExecutionUpdate` is emitted).
///
/// Fires every time a tool calls `ctx.on_update(partial)` — potentially many times per call
/// (e.g. each line of bash output). Arguments: `(tool_name, tool_call_id, text_content)`.
/// Return `false` to suppress the streaming event; the tool keeps running and its final
/// `ToolResult` (what the LLM sees) is **unaffected**.
///
/// 0.10.0: async hook (matches the 9 other lifecycle Fns async-migrated at
/// 0.9.0). Wrap sync bodies in `Box::pin(async move { ... })`. The closure
/// fires from inside the synchronous `ToolUpdateFn` callback that tools
/// invoke during their async execute body; the agent loop bridges to the
/// async hook via a `futures::executor::block_on` shim at the call site —
/// see `agent_loop/tools.rs`.
pub type BeforeToolExecutionUpdateFn =
;
/// Called after each incremental tool update (after `ToolExecutionUpdate` is emitted).
///
/// Only fires when the update was *not* suppressed by `BeforeToolExecutionUpdateFn`.
/// Arguments: `(tool_name, tool_call_id, text_content)`.
///
/// 0.10.0: async hook. See [`BeforeToolExecutionUpdateFn`] for the
/// bridging-from-sync-ToolUpdateFn rationale.
pub type AfterToolExecutionUpdateFn =
;
/// Called when the LLM returns `StopReason::Error`. Argument: the error message string.
///
/// 0.9.0: async hook.
pub type OnErrorFn = ;
// ── Compaction hooks (G1) ───────────────────────────────────────────────────
/// Called before compaction starts.
///
/// Arguments: `(estimated_tokens, message_count)`.
/// Return `false` to skip compaction for this cycle.
///
/// 0.9.0: async hook.
pub type BeforeCompactionStartFn =
;
/// Called after compaction completes.
///
/// Arguments: `(messages_before, messages_after, tokens_before, tokens_after)`.
///
/// 0.9.0: async hook.
pub type AfterCompactionEndFn =
;
/// All static settings for a single [`agent_loop`] / [`agent_loop_continue`] call.
///
/// Build with the public fields directly or via [`crate::agent::Agent`]'s builder methods.
/// The config is borrowed (`&AgentLoopConfig`) throughout the loop — it is never mutated.
///
/// ## Lifecycle hooks
///
/// All hook fields are `Option<Arc<dyn Fn(...)>>`. `None` means "no hook" (zero overhead).
/// See the module-level doc for the guaranteed ordering relative to [`AgentEvent`]s.