xz_agent_core/traits/processor.rs
1use std::future::Future;
2
3use crate::error::EngineError;
4use crate::types::signal::Signal;
5
6/// Output processor — handles Thinker output and decides flow control.
7///
8/// After a Thinker produces output, each OutputProcessor in the chain
9/// processes it and returns Continue or Stop.
10///
11/// # Chain semantics
12///
13/// Processors are consulted **in registration order** and share a single
14/// immutable reference to the thinker's output (no per-processor clone).
15/// The first processor that returns [`Signal::Stop`] terminates the loop;
16/// subsequent processors in the chain are **not** invoked for that turn.
17///
18/// Recommended registration order for side-effect + termination processors:
19///
20/// 1. Safety / policy gates (may Stop early)
21/// 2. Side-effect processors (tool execution, message recording)
22/// 3. Loop guards (doom-loop detection)
23/// 4. Round limits (max turns)
24///
25/// # Example
26///
27/// ```rust
28/// use xz_agent_core::traits::processor::OutputProcessor;
29/// use xz_agent_core::error::EngineError;
30/// use xz_agent_core::types::signal::Signal;
31///
32/// struct StopOnHello;
33///
34/// impl OutputProcessor for StopOnHello {
35/// type Context = String;
36/// type Output = String;
37/// async fn process(&self, output: &Self::Output, ctx: &mut Self::Context) -> Result<Signal, EngineError> {
38/// if output == "stop" {
39/// Ok(Signal::Stop)
40/// } else {
41/// ctx.push_str(output);
42/// Ok(Signal::Continue)
43/// }
44/// }
45/// }
46/// ```
47pub trait OutputProcessor: Send + Sync {
48 /// The type of context modified during processing.
49 type Context;
50 /// The type of output produced by the Thinker.
51 type Output;
52 /// Process the Thinker's output.
53 ///
54 /// Receives a shared reference so the full processor chain can inspect
55 /// the same output without requiring [`Clone`].
56 ///
57 /// Returns Continue to proceed or Stop to terminate the engine loop.
58 fn process(
59 &self,
60 output: &Self::Output,
61 ctx: &mut Self::Context,
62 ) -> impl Future<Output = Result<Signal, EngineError>> + Send;
63}