Skip to main content

locode_engine/
config.rs

1//! Static per-session configuration for the loop.
2
3use std::path::PathBuf;
4use std::time::Duration;
5
6use locode_instructions::InstructionsConfig;
7use locode_provider::{CacheHint, SamplingArgs};
8use locode_skills::SkillsConfig;
9
10/// Everything the loop needs that isn't the provider, registry, preamble, or sink.
11#[derive(Debug, Clone)]
12pub struct EngineConfig {
13    /// The session identifier (stamped into the report + `Init`).
14    pub session_id: String,
15    /// The harness pack in use, e.g. `grok` (stamped into the report + `Init`).
16    pub harness: String,
17    /// The wire schema in use, e.g. `anthropic`/`mock` (the provider's `api_schema`).
18    pub api_schema: String,
19    /// The model id (for `Init`).
20    pub model: String,
21    /// The working directory handed to each tool call.
22    pub cwd: PathBuf,
23    /// The path-jail root (ADR-0008), handed to each `ToolCtx`.
24    pub workspace_root: PathBuf,
25    /// The max sample→dispatch turns before terminating with `MaxTurns`
26    /// (ADR-0005 amendment 2026-07-18). **`None` (the default) = unlimited** —
27    /// every studied harness defaults to no cap (Claude Code's `maxTurns?` is
28    /// enforced only when set; grok's `max_turns: Option<u32>` defaults `None`;
29    /// codex has no turn cap at all).
30    pub max_turns: Option<u32>,
31    /// Bounded loop-level resample budget on retryable provider errors (ADR-0007).
32    pub resample_retries: u32,
33    /// Base backoff between resamples; the nth retry waits `base * n`. Set to zero
34    /// in tests to avoid real sleeps.
35    pub resample_backoff: Duration,
36    /// Provider-neutral sampling knobs.
37    pub sampling_args: SamplingArgs,
38    /// Prompt-cache placement hint for the wire.
39    pub cache_hint: CacheHint,
40    /// Use the streaming sample path ([`locode_provider::Provider::stream`]) instead of
41    /// [`locode_provider::Provider::complete`] (ADR-0021). Default **false** — the headless one-shot
42    /// stays non-streaming; the TUI sets this, and headless can opt in via
43    /// `--stream` (Anthropic rejects non-streaming requests past ~10 min, so
44    /// streaming is required for unbounded output). Emits `Event::MessageDelta`s;
45    /// the final `Message` is still appended whole, so the trace can stay
46    /// whole-message by dropping the deltas (Q1).
47    pub streaming: bool,
48    /// Shared project-instruction loading (`AGENTS.md`, ADR-0023): discovered from `cwd`
49    /// and injected once per session as a `User` `<system-reminder>`. Default = enabled.
50    pub instructions: InstructionsConfig,
51    /// Skill discovery + the injected listing (ADR-0025). Like `instructions`, this is
52    /// shared machinery: identical under every `--harness`, and no pack sees a tool.
53    pub skills: SkillsConfig,
54    /// The model's context window in tokens, when known — sizes the listing budget
55    /// (ADR-0025 §3). `None` falls back to a fixed char budget.
56    pub context_window_tokens: Option<u64>,
57}
58
59impl Default for EngineConfig {
60    fn default() -> Self {
61        Self {
62            session_id: String::new(),
63            harness: String::new(),
64            api_schema: String::new(),
65            model: String::new(),
66            cwd: PathBuf::from("."),
67            workspace_root: PathBuf::from("."),
68            max_turns: None,
69            resample_retries: 2,
70            resample_backoff: Duration::from_millis(500),
71            sampling_args: SamplingArgs::default(),
72            cache_hint: CacheHint::default(),
73            streaming: false,
74            instructions: InstructionsConfig::default(),
75            skills: SkillsConfig::default(),
76            context_window_tokens: None,
77        }
78    }
79}