diff --git a/hive-ag3nt/src/mcp.rs b/hive-ag3nt/src/mcp.rs index 2df48a2..bced8cc 100644 --- a/hive-ag3nt/src/mcp.rs +++ b/hive-ag3nt/src/mcp.rs @@ -1383,13 +1383,20 @@ pub fn render_claude_config(agent_binary: &str, socket: &std::path::Path) -> Str "env": {} }), ); - for (name, spec) in load_extra_mcp() { + // Auto-inject HYPERHIVE_STATE_DIR so extra MCP servers can resolve the + // agent's durable state dir without the agent author hard-coding it. + // User-supplied env takes precedence — we only fill in the missing key. + let state_dir = crate::paths::state_dir(); + for (name, mut spec) in load_extra_mcp() { if name == SERVER_NAME { tracing::warn!( "extra MCP server name `{SERVER_NAME}` collides with the built-in surface; ignoring", ); continue; } + spec.env + .entry("HYPERHIVE_STATE_DIR".to_owned()) + .or_insert_with(|| state_dir.display().to_string()); servers.insert( name, serde_json::json!({ diff --git a/hive-c0re/src/meta.rs b/hive-c0re/src/meta.rs index 6774e66..c2d67fa 100644 --- a/hive-c0re/src/meta.rs +++ b/hive-c0re/src/meta.rs @@ -273,11 +273,22 @@ fn render_flake( name = name; email = "${name}@hyperhive"; }; + # Container-wide env: every service + co-process daemon can + # resolve the agent's durable state dir without hard-coding it. + environment.variables = { + HIVE_LABEL = name; + HYPERHIVE_STATE_DIR = + if isManager then "/state" + else "/agents/${name}/state"; + }; systemd.services.${service}.environment = { HIVE_PORT = toString port; HIVE_LABEL = name; HIVE_DASHBOARD_PORT = toString dashboardPort; HIVE_OPERATOR_PRONOUNS = operatorPronouns; + HYPERHIVE_STATE_DIR = + if isManager then "/state" + else "/agents/${name}/state"; }; } ];