Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/hive-subagent-mcp/src/paths.rs
atlas 8c0a552792 subagent: default cwd to the work dir only when it exists
A spawn into a missing cwd fails with a bare ENOENT; a daemon newer than its
container's bind now inherits its own cwd again, as before the work-dir default.
2026-10-16 00:32:41 +02:00

69 lines
2.8 KiB
Rust

//! The per-agent paths this daemon needs.
use std::path::PathBuf;
/// The harness-served in-agent todo socket (loose-ends v2). This daemon
/// pushes exactly one todo per subagent lifetime — the completion summary,
/// once its background turn finishes. Override via `HIVE_AGENT_SOCKET`;
/// same resolution as `hive-bash-mcp`'s own `paths::agent_socket`.
#[must_use]
pub fn agent_socket() -> PathBuf {
std::env::var_os("HIVE_AGENT_SOCKET").map_or_else(
|| PathBuf::from(hive_agent_sock::DEFAULT_AGENT_SOCKET),
PathBuf::from,
)
}
/// Durable state directory for the current agent. Same resolution as
/// `hive-agent`'s own `paths::state_dir` — reads `HYPERHIVE_STATE_DIR`
/// (always injected via `systemd.globalEnvironment` by the meta flake),
/// falling back to the `HIVE_LABEL`-derived pattern for dev/test
/// environments where the env var may not be set. Copied rather than shared
/// with `hive-agent` (a binary crate with no lib target) — a five-line env
/// read isn't worth a crate dependency to dedupe.
#[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"))
}
/// The agent's persistent work dir, a sibling of [`state_dir`]
/// (`/agents/<name>/work`). It has no `CLAUDE.md` above it, so a subagent
/// started here does not load the agent's own.
#[must_use]
pub fn work_dir() -> PathBuf {
state_dir().with_file_name("work")
}
/// The working directory a subagent gets when `start` is given no `dir`:
/// [`work_dir`] when it exists, else `None` (the daemon's own cwd). The
/// directory only exists once the container has restarted with its bind, and
/// a spawn into a missing cwd fails with a bare ENOENT.
#[must_use]
pub fn default_cwd() -> Option<PathBuf> {
Some(work_dir()).filter(|p| p.is_dir())
}
/// Where this agent's named subagent role files live, one markdown file per
/// role under the agent's own [`state_dir`]. Per-agent rather than
/// hive-wide: the clauses a role carries came out of one agent's own prompt
/// files, and this daemon only ever spawns on behalf of that agent. The
/// directory is a convention, not a registry — it is created by whoever
/// writes a role into it, and its absence simply means this agent ships no
/// roles yet (see `crate::role`).
#[must_use]
pub fn subagent_roles_dir() -> PathBuf {
state_dir().join("subagent_roles")
}
/// Harness-internal scratch dir this daemon writes its own generated
/// subagent `--mcp-config` file into (see `crate::mcp_config`). Delegates to
/// the shared resolver so this and `hive-agent`'s own harness dir always
/// agree on the same path.
#[must_use]
pub fn harness_dir() -> PathBuf {
hive_agent_sock::paths::harness_dir()
}