Skip to main content

codewhale_core/engine/thread/
executor.rs

1//! Turn executor — the `monitor_turn` / `handle_deepseek_turn` leg
2//! (issue #5261 / #3313).
3//!
4//! This will own `handle_deepseek_turn`, the steer/subagent drains,
5//! `refresh_system_prompt()`, `should_compact`/`compact_messages_safe`,
6//! `MessageRequest` build, parallel tool exec, `StuckGuard`/
7//! `ReadRepeatGuard`/`ToolCallBudget`, and stream retry budget. The move
8//! is file-by-file from `crates/tui/src/core/engine/turn_loop.rs`
9//! (5,706 lines) so the diff stays reviewable. Until the move lands this
10//! file carries the executor type and the `execpolicy` gate that guarantees
11//! approvals route through the turn context identically in both modes.
12
13use codewhale_execpolicy::ExecPolicyEngine;
14use codewhale_protocol::ids::{SessionId, ThreadId};
15
16/// Per-turn execution context. The `execpolicy` engine is the sole authority
17/// for approvals; both TUI and headless construct it from the same
18/// `permissions.toml` / `ConfigStore` so the gate never diverges.
19#[derive(Debug)]
20pub struct TurnExecutor {
21    pub thread_id: ThreadId,
22    pub session_id: SessionId,
23    pub exec_policy: ExecPolicyEngine,
24    pub max_steps: u32,
25}
26
27impl TurnExecutor {
28    #[must_use]
29    pub fn new(
30        thread_id: ThreadId,
31        session_id: SessionId,
32        exec_policy: ExecPolicyEngine,
33        max_steps: u32,
34    ) -> Self {
35        Self {
36            thread_id,
37            session_id,
38            exec_policy,
39            max_steps,
40        }
41    }
42
43    #[must_use]
44    pub fn can_execute(&self, step: u32) -> bool {
45        step < self.max_steps
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    #[test]
54    fn executor_respects_max_steps() {
55        let ex = TurnExecutor::new(
56            ThreadId::new(),
57            SessionId::new(),
58            ExecPolicyEngine::new(vec![], vec![]),
59            2,
60        );
61        assert!(ex.can_execute(0));
62        assert!(ex.can_execute(1));
63        assert!(!ex.can_execute(2));
64    }
65}