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
27use std::path::PathBuf;
28use std::time::Duration;
29
30const DEFAULT_SQL_BUSY_TIMEOUT_MS: u64 = 5000;
31const DEFAULT_SQL_QUERY_TIMEOUT_MS: u64 = 5000;
32const DEFAULT_SQL_JOURNAL_MODE: &str = "WAL";
33const DEFAULT_BUS_CAPACITY: usize = 64;
34const DEFAULT_TASK_GRACE_MS: u64 = 1000;
35
36/// Base dir for agent-block local state.
37/// `AGENT_BLOCK_HOME` → `$HOME/.agent-block`.
38pub fn base_dir() -> Result<PathBuf, String> {
39    if let Some(v) = std::env::var_os("AGENT_BLOCK_HOME") {
40        return Ok(PathBuf::from(v));
41    }
42    let home = std::env::var_os("HOME").ok_or_else(|| "HOME env var not set".to_string())?;
43    Ok(PathBuf::from(home).join(".agent-block"))
44}
45
46/// Path to the std.kv SQLite database file (or `:memory:`).
47/// `AGENT_BLOCK_KV_PATH` → `{base_dir}/kv.sqlite`.
48pub fn kv_path() -> Result<PathBuf, String> {
49    if let Some(v) = std::env::var_os("AGENT_BLOCK_KV_PATH") {
50        return Ok(PathBuf::from(v));
51    }
52    Ok(base_dir()?.join("kv.sqlite"))
53}
54
55/// Path to the std.sql SQLite database file (or `:memory:`).
56/// `AGENT_BLOCK_SQL_PATH` → `{base_dir}/db.sqlite`.
57pub fn sql_path() -> Result<PathBuf, String> {
58    if let Some(v) = std::env::var_os("AGENT_BLOCK_SQL_PATH") {
59        return Ok(PathBuf::from(v));
60    }
61    Ok(base_dir()?.join("db.sqlite"))
62}
63
64/// Path to the std.ts SQLite database file (or `:memory:`).
65///
66/// `AGENT_BLOCK_TS_PATH` → `{base_dir}/ts.sqlite`.
67/// Separate from kv and sql so the TSDB WAL does not share page cache or
68/// backup lifecycle with agent-internal KV or user SQL data.
69pub fn ts_path() -> Result<PathBuf, String> {
70    if let Some(v) = std::env::var_os("AGENT_BLOCK_TS_PATH") {
71        return Ok(PathBuf::from(v));
72    }
73    Ok(base_dir()?.join("ts.sqlite"))
74}
75
76/// True when the resolved path is SQLite's in-memory sentinel.
77pub fn is_memory_sql(path: &std::path::Path) -> bool {
78    path.as_os_str() == ":memory:"
79}
80
81/// SQLite busy_timeout.
82/// `AGENT_BLOCK_SQL_BUSY_TIMEOUT_MS` → 5000ms.
83pub fn sql_busy_timeout() -> Duration {
84    let ms = std::env::var("AGENT_BLOCK_SQL_BUSY_TIMEOUT_MS")
85        .ok()
86        .and_then(|s| s.parse::<u64>().ok())
87        .unwrap_or(DEFAULT_SQL_BUSY_TIMEOUT_MS);
88    Duration::from_millis(ms)
89}
90
91/// SQLite journal_mode pragma value.
92/// `AGENT_BLOCK_SQL_JOURNAL_MODE` → `WAL`.
93pub fn sql_journal_mode() -> String {
94    std::env::var("AGENT_BLOCK_SQL_JOURNAL_MODE")
95        .unwrap_or_else(|_| DEFAULT_SQL_JOURNAL_MODE.to_string())
96}
97
98/// Per-query timeout. `0` disables the timeout.
99/// `AGENT_BLOCK_SQL_QUERY_TIMEOUT_MS` → 5000ms.
100pub fn sql_query_timeout() -> Option<Duration> {
101    let ms = std::env::var("AGENT_BLOCK_SQL_QUERY_TIMEOUT_MS")
102        .ok()
103        .and_then(|s| s.parse::<u64>().ok())
104        .unwrap_or(DEFAULT_SQL_QUERY_TIMEOUT_MS);
105    if ms == 0 {
106        None
107    } else {
108        Some(Duration::from_millis(ms))
109    }
110}
111
112/// EventBus bounded mpsc capacity.
113/// `AGENT_BLOCK_BUS_CAPACITY` → 64. Parse failures warn and fall back.
114pub fn bus_capacity() -> usize {
115    match std::env::var("AGENT_BLOCK_BUS_CAPACITY") {
116        Ok(v) => v.parse::<usize>().unwrap_or_else(|e| {
117            tracing::warn!(
118                value = %v,
119                error = %e,
120                default = DEFAULT_BUS_CAPACITY,
121                "AGENT_BLOCK_BUS_CAPACITY parse failed, using default"
122            );
123            DEFAULT_BUS_CAPACITY
124        }),
125        Err(_) => DEFAULT_BUS_CAPACITY,
126    }
127}
128
129/// SIGTERM/SIGINT grace window (ms) shared by `std.task.with_timeout` and the
130/// EventBus shutdown path.
131/// `AGENT_BLOCK_TASK_GRACE_MS` → 1000. Parse failures warn and fall back.
132pub fn task_grace_ms() -> u64 {
133    match std::env::var("AGENT_BLOCK_TASK_GRACE_MS") {
134        Ok(v) => v.parse::<u64>().unwrap_or_else(|e| {
135            tracing::warn!(
136                value = %v,
137                error = %e,
138                default = DEFAULT_TASK_GRACE_MS,
139                "AGENT_BLOCK_TASK_GRACE_MS parse failed, using default"
140            );
141            DEFAULT_TASK_GRACE_MS
142        }),
143        Err(_) => DEFAULT_TASK_GRACE_MS,
144    }
145}