Skip to main content

agent_types/
guard.rs

1//! Guard-related pure types: GuardDecision, GuardCtx.
2
3use serde::{Deserialize, Serialize};
4
5use crate::execution::FinishReason;
6use crate::session::SessionId;
7
8/// Guard decision — returned by guard, executed by base loop.
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub enum GuardDecision {
11    /// Continue loop, optionally inject nudge message
12    Continue { nudge: Option<String> },
13    /// Normal completion (fire_turn_end + RunOutcome::Completed)
14    Complete,
15    /// Abnormal termination (fire_guard_fail + RunOutcome::Failed)
16    Fail { error: String },
17
18    // ─── Thinking control ─────────────────────────────────────
19    /// Temporarily disable thinking functionality
20    ///
21    /// Used for reasoning-only loop scenarios: model keeps thinking but produces no output.
22    /// After calling, runtime will:
23    /// 1. Set thinking_disabled_for_rest_of_run = true
24    /// 2. Inject nudge message
25    /// 3. Continue loop
26    DisableThinking { nudge: String },
27
28    /// Restore thinking functionality to previous state
29    ///
30    /// Used for thinking recovery scenarios: model starts working normally (has text or tool call).
31    /// After calling, runtime will:
32    /// 1. Restore thinking_disabled_for_rest_of_run to original state
33    /// 2. Reset related counters
34    RestoreThinking,
35}
36
37/// Guard context information — built by runtime, passed to guard.
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct GuardCtx {
40    pub session_id: SessionId,
41    pub turn_count: u32,
42    pub user_input: String,
43    pub model_response: String,
44    pub finish_reason: FinishReason,
45    pub available_tools: Vec<String>,
46    // RunState information
47    pub reasoning_only_strikes: usize,
48    pub empty_response_strikes: usize,
49    pub run_has_tool_calls: bool,
50    /// The most recent assistant tool calls were ALL rejected before
51    /// execution (truncated/invalid arguments) and the model was told to
52    /// re-issue them (`RunState.truncation_strikes > 0`). While this is
53    /// true, a text-only response cannot be task completion — the work the
54    /// text describes was never executed (session 20260904_efad759c: a
55    /// fabricated success narrative passed the completion judge).
56    pub last_tool_calls_invalid: bool,
57    /// All user messages in the current session, ordered oldest-first.
58    /// Guards can use this to reconstruct full conversation context
59    /// (e.g. "继续" after a multi-turn discussion).
60    pub all_user_inputs: Vec<String>,
61    // Scene hints (runtime detected, guard can trust or ignore)
62    pub is_reasoning_only: bool,
63    pub is_empty_response: bool,
64    pub is_text_only: bool,
65    // Environment state
66    pub thinking_disabled: bool,
67    /// Original thinking configuration (for restoration)
68    ///
69    /// From RunState.original_thinking_enabled
70    pub original_thinking_enabled: bool,
71    /// Remaining turns before hitting max_turns limit
72    ///
73    /// Guards can use this to nudge the model to wrap up when running low on turns.
74    /// When remaining_turns == 0, the run will be terminated with MaxTurnsExceeded.
75    pub remaining_turns: u32,
76}