Skip to main content

agent_block_core/bridge/
config.rs

1//! Config resolution for `std.kv` / `std.sql` / `std.ts` storage backends.
2//!
3//! All knobs are ENV-driven (no CLI flags) so `.env` can drive them uniformly.
4//!
5//! | ENV var                            | Default                  | Used by  |
6//! |------------------------------------|--------------------------|----------|
7//! | `AGENT_BLOCK_HOME`                 | `$HOME/.agent-block`     | all      |
8//! | `AGENT_BLOCK_KV_PATH`              | `{HOME}/kv.sqlite`       | std.kv   |
9//! | `AGENT_BLOCK_SQL_PATH`             | `{HOME}/db.sqlite`       | std.sql  |
10//! | `AGENT_BLOCK_TS_PATH`              | `{HOME}/ts.sqlite`       | std.ts   |
11//! | `AGENT_BLOCK_SQL_BUSY_TIMEOUT_MS`  | `5000`                   | all      |
12//! | `AGENT_BLOCK_SQL_QUERY_TIMEOUT_MS` | `5000`                   | all      |
13//! | `AGENT_BLOCK_SQL_JOURNAL_MODE`     | `WAL`                    | all      |
14//! | `AGENT_BLOCK_BUS_CAPACITY`         | `64`                     | EventBus |
15//! | `AGENT_BLOCK_TASK_GRACE_MS`        | `1000`                   | task/bus |
16//!
17//! `std.kv`, `std.sql`, and `std.ts` are backed by separate SQLite database
18//! files so that agent-internal KV state, explicit user SQL data, and
19//! time-series rows don't share WAL, page cache, or backup lifecycle.
20//! Pragma/timeout knobs apply to all three.
21//!
22//! Special: `=:memory:` selects an in-memory database (works for
23//! `AGENT_BLOCK_KV_PATH`, `AGENT_BLOCK_SQL_PATH`, and `AGENT_BLOCK_TS_PATH`).
24//! Journal mode is ignored for `:memory:` (SQLite forces MEMORY).
25//! `AGENT_BLOCK_SQL_QUERY_TIMEOUT_MS=0` disables the query timeout.
26
27#[cfg(feature = "sqlite")]
28use std::path::PathBuf;
29#[cfg(feature = "sqlite")]
30use std::time::Duration;
31
32#[cfg(feature = "sqlite")]
33const DEFAULT_SQL_BUSY_TIMEOUT_MS: u64 = 5000;
34#[cfg(feature = "sqlite")]
35const DEFAULT_SQL_QUERY_TIMEOUT_MS: u64 = 5000;
36#[cfg(feature = "sqlite")]
37const DEFAULT_SQL_JOURNAL_MODE: &str = "WAL";
38const DEFAULT_BUS_CAPACITY: usize = 64;
39const DEFAULT_TASK_GRACE_MS: u64 = 1000;
40
41/// Base dir for agent-block local state.
42/// `AGENT_BLOCK_HOME` → `$HOME/.agent-block`.
43#[cfg(feature = "sqlite")]
44pub fn base_dir() -> Result<PathBuf, String> {
45    if let Some(v) = std::env::var_os("AGENT_BLOCK_HOME") {
46        return Ok(PathBuf::from(v));
47    }
48    let home = std::env::var_os("HOME").ok_or_else(|| "HOME env var not set".to_string())?;
49    Ok(PathBuf::from(home).join(".agent-block"))
50}
51
52/// Path to the std.kv SQLite database file (or `:memory:`).
53/// `AGENT_BLOCK_KV_PATH` → `{base_dir}/kv.sqlite`.
54#[cfg(feature = "sqlite")]
55pub fn kv_path() -> Result<PathBuf, String> {
56    if let Some(v) = std::env::var_os("AGENT_BLOCK_KV_PATH") {
57        return Ok(PathBuf::from(v));
58    }
59    Ok(base_dir()?.join("kv.sqlite"))
60}
61
62/// Path to the std.sql SQLite database file (or `:memory:`).
63/// `AGENT_BLOCK_SQL_PATH` → `{base_dir}/db.sqlite`.
64#[cfg(feature = "sqlite")]
65pub fn sql_path() -> Result<PathBuf, String> {
66    if let Some(v) = std::env::var_os("AGENT_BLOCK_SQL_PATH") {
67        return Ok(PathBuf::from(v));
68    }
69    Ok(base_dir()?.join("db.sqlite"))
70}
71
72/// Path to the std.ts SQLite database file (or `:memory:`).
73///
74/// `AGENT_BLOCK_TS_PATH` → `{base_dir}/ts.sqlite`.
75/// Separate from kv and sql so the TSDB WAL does not share page cache or
76/// backup lifecycle with agent-internal KV or user SQL data.
77#[cfg(feature = "sqlite")]
78pub fn ts_path() -> Result<PathBuf, String> {
79    if let Some(v) = std::env::var_os("AGENT_BLOCK_TS_PATH") {
80        return Ok(PathBuf::from(v));
81    }
82    Ok(base_dir()?.join("ts.sqlite"))
83}
84
85/// True when the resolved path is SQLite's in-memory sentinel.
86#[cfg(feature = "sqlite")]
87pub fn is_memory_sql(path: &std::path::Path) -> bool {
88    path.as_os_str() == ":memory:"
89}
90
91/// SQLite busy_timeout.
92/// `AGENT_BLOCK_SQL_BUSY_TIMEOUT_MS` → 5000ms.
93#[cfg(feature = "sqlite")]
94pub fn sql_busy_timeout() -> Duration {
95    let ms = std::env::var("AGENT_BLOCK_SQL_BUSY_TIMEOUT_MS")
96        .ok()
97        .and_then(|s| s.parse::<u64>().ok())
98        .unwrap_or(DEFAULT_SQL_BUSY_TIMEOUT_MS);
99    Duration::from_millis(ms)
100}
101
102/// SQLite journal_mode pragma value.
103/// `AGENT_BLOCK_SQL_JOURNAL_MODE` → `WAL`.
104#[cfg(feature = "sqlite")]
105pub fn sql_journal_mode() -> String {
106    std::env::var("AGENT_BLOCK_SQL_JOURNAL_MODE")
107        .unwrap_or_else(|_| DEFAULT_SQL_JOURNAL_MODE.to_string())
108}
109
110/// Per-query timeout. `0` disables the timeout.
111/// `AGENT_BLOCK_SQL_QUERY_TIMEOUT_MS` → 5000ms.
112#[cfg(feature = "sqlite")]
113pub fn sql_query_timeout() -> Option<Duration> {
114    let ms = std::env::var("AGENT_BLOCK_SQL_QUERY_TIMEOUT_MS")
115        .ok()
116        .and_then(|s| s.parse::<u64>().ok())
117        .unwrap_or(DEFAULT_SQL_QUERY_TIMEOUT_MS);
118    if ms == 0 {
119        None
120    } else {
121        Some(Duration::from_millis(ms))
122    }
123}
124
125/// EventBus bounded mpsc capacity.
126/// `AGENT_BLOCK_BUS_CAPACITY` → 64. Parse failures warn and fall back.
127pub fn bus_capacity() -> usize {
128    match std::env::var("AGENT_BLOCK_BUS_CAPACITY") {
129        Ok(v) => v.parse::<usize>().unwrap_or_else(|e| {
130            tracing::warn!(
131                value = %v,
132                error = %e,
133                default = DEFAULT_BUS_CAPACITY,
134                "AGENT_BLOCK_BUS_CAPACITY parse failed, using default"
135            );
136            DEFAULT_BUS_CAPACITY
137        }),
138        Err(_) => DEFAULT_BUS_CAPACITY,
139    }
140}
141
142/// SIGTERM/SIGINT grace window (ms) shared by `std.task.with_timeout` and the
143/// EventBus shutdown path.
144/// `AGENT_BLOCK_TASK_GRACE_MS` → 1000. Parse failures warn and fall back.
145pub fn task_grace_ms() -> u64 {
146    match std::env::var("AGENT_BLOCK_TASK_GRACE_MS") {
147        Ok(v) => v.parse::<u64>().unwrap_or_else(|e| {
148            tracing::warn!(
149                value = %v,
150                error = %e,
151                default = DEFAULT_TASK_GRACE_MS,
152                "AGENT_BLOCK_TASK_GRACE_MS parse failed, using default"
153            );
154            DEFAULT_TASK_GRACE_MS
155        }),
156        Err(_) => DEFAULT_TASK_GRACE_MS,
157    }
158}