//! Per-agent path resolution for state, harness, and credential directories. //! //! All agents (including the manager `root`) use `/agents/{label}/state` //! for agent-owned durable notes, and `/agents/{label}/harness` for //! harness-internal files (`hyperhive-events.sqlite`, `hyperhive-model`, etc.) //! that should not clutter what claude sees as "my notes dir". //! Claude credentials live at `$HOME/.claude` (resolves to //! `/home//.claude` because the harness service runs as a //! non-root unix user matching the agent label — see //! `docs/persistence.md::First-boot agent-user migration`). //! //! All three paths can be overridden via env vars (`HYPERHIVE_STATE_DIR`, //! `HYPERHIVE_HARNESS_DIR`, `HYPERHIVE_CLAUDE_DIR`) for dev / test scenarios. use std::path::PathBuf; /// Durable state directory for the current agent. Reads `HYPERHIVE_STATE_DIR` /// first (always set by the meta flake to `/agents/{label}/state`); falls back /// to the same pattern derived from `HIVE_LABEL` for dev/test environments /// where the env var may not be set. #[must_use] pub fn state_dir() -> PathBuf { if let Some(p) = std::env::var_os("HYPERHIVE_STATE_DIR") { return PathBuf::from(p); } let label = std::env::var("HIVE_LABEL").unwrap_or_default(); PathBuf::from(format!("/agents/{label}/state")) } /// Harness-internal state directory. Holds files the harness owns /// (`hyperhive-events.sqlite`, `hyperhive-turn-stats.sqlite`, /// `hyperhive-model`) so they do not appear inside the agent-visible /// `/agents/{label}/state` tree. Delegates to the shared canonical /// resolver in `hive_sh4re::paths` so the harness + every out-of-process /// MCP daemon resolve this identically (reads `HYPERHIVE_HARNESS_DIR`, /// then a `harness/` sibling of `HYPERHIVE_STATE_DIR`, then /// `/agents/{HIVE_LABEL}/harness`). #[must_use] pub fn harness_dir() -> PathBuf { hive_sh4re::paths::harness_dir() } /// Consolidated harness-local state db — todos + reminders + the questions /// mirror, one table each — mutable per-agent state the harness owns, kept /// out of the append-only `hyperhive-events.sqlite` sink. Per mara's call /// ("not yet another sqlite! todos, reminders, questions should be like /// three tiny tables in one 500kb sqlite"), this file is the shared home /// for all loose-ends-v2 stores; each store's `open()` only applies its own /// `CREATE TABLE IF NOT EXISTS`, so opening multiple stores against the /// same path is safe (distinct table names, no schema collision). /// All three stores (todos, reminders, questions) open this same path /// directly (see their `open()` call sites) — distinct table names mean no /// schema collision, so there's no need for per-store path wrapper fns here. /// /// Before this consolidation, todos and reminders lived in their own /// `hyperhive-todos.sqlite` / `hyperhive-reminders.sqlite` files; a /// one-time boot migration (`db_migrate::run`) folds those into this path /// the first time a harness boots after the upgrade. #[must_use] pub fn state_db() -> PathBuf { harness_dir().join("hyperhive-state.sqlite") } /// Legacy pre-consolidation todos db path, consulted only by /// [`crate::db_migrate`] on the first boot after the upgrade. #[must_use] pub fn legacy_todos_db() -> PathBuf { harness_dir().join("hyperhive-todos.sqlite") } /// Legacy pre-consolidation reminders db path, consulted only by /// [`crate::db_migrate`] on the first boot after the upgrade. #[must_use] pub fn legacy_reminders_db() -> PathBuf { harness_dir().join("hyperhive-reminders.sqlite") } /// Per-turn config dir for the regenerated claude-{mcp-config,settings, /// system-prompt} files the harness drops before each turn. Set by /// systemd via `RuntimeDirectory = "hive-config"`: a per-service runtime /// dir owned by the agent unix user, auto-cleared on stop. Kept separate /// from `/run/hive` (the host-owned mcp.sock bind) so the harness owns /// its own write surface and we don't have to chown a bind-mounted dir. /// Overridable via `HYPERHIVE_CONFIG_DIR` for dev / test scenarios. #[must_use] pub fn config_dir() -> PathBuf { if let Some(p) = std::env::var_os("HYPERHIVE_CONFIG_DIR") { return PathBuf::from(p); } PathBuf::from("/run/hive-config") } /// Claude credentials directory for the current agent. `$HOME/.claude` /// matches what the `claude` CLI reads at runtime — the harness sees /// the same `$HOME` set by the per-service systemd `environment` /// declaration (`/home/`). Falls back to `/root/.claude` for /// dev / test environments where `HOME` isn't set so the previous /// root-by-default shape keeps working without env wiring. /// Overridable via `HYPERHIVE_CLAUDE_DIR` for dev / test scenarios. #[must_use] pub fn claude_dir() -> PathBuf { if let Some(p) = std::env::var_os("HYPERHIVE_CLAUDE_DIR") { return PathBuf::from(p); } if let Some(home) = std::env::var_os("HOME") { let mut path = PathBuf::from(home); path.push(".claude"); return path; } PathBuf::from("/root/.claude") }