diff --git a/hive-ag3nt/src/bin/hive-ag3nt.rs b/hive-ag3nt/src/bin/hive-ag3nt.rs index 7fa08b9..4d94fab 100644 --- a/hive-ag3nt/src/bin/hive-ag3nt.rs +++ b/hive-ag3nt/src/bin/hive-ag3nt.rs @@ -127,6 +127,7 @@ async fn serve( tracing::info!(socket = %socket.display(), "hive-ag3nt serve"); let _ = state; // reserved for future state transitions (turn-loop -> needs-login) let mcp_config = turn::write_mcp_config(socket).await?; + let settings = turn::write_settings(socket).await?; let label = std::env::var("HIVE_LABEL").unwrap_or_else(|_| "hive-ag3nt".into()); let system_prompt = turn::write_system_prompt(socket, &label, mcp::Flavor::Agent).await?; loop { @@ -143,6 +144,7 @@ async fn serve( &prompt, &mcp_config, &system_prompt, + &settings, &bus, mcp::Flavor::Agent, ) diff --git a/hive-ag3nt/src/bin/hive-m1nd.rs b/hive-ag3nt/src/bin/hive-m1nd.rs index 9c8eead..e139893 100644 --- a/hive-ag3nt/src/bin/hive-m1nd.rs +++ b/hive-ag3nt/src/bin/hive-m1nd.rs @@ -126,6 +126,7 @@ async fn one_shot(socket: &Path, req: ManagerRequest) -> Result<()> { async fn serve(socket: &Path, interval: Duration, bus: Bus) -> Result<()> { tracing::info!(socket = %socket.display(), "hive-m1nd serve"); let mcp_config = turn::write_mcp_config(socket).await?; + let settings = turn::write_settings(socket).await?; let label = std::env::var("HIVE_LABEL").unwrap_or_else(|_| "hm1nd".into()); let system_prompt = turn::write_system_prompt(socket, &label, mcp::Flavor::Manager).await?; loop { @@ -159,6 +160,7 @@ async fn serve(socket: &Path, interval: Duration, bus: Bus) -> Result<()> { &prompt, &mcp_config, &system_prompt, + &settings, &bus, mcp::Flavor::Manager, ) diff --git a/hive-ag3nt/src/turn.rs b/hive-ag3nt/src/turn.rs index 93f712a..b5ca8cc 100644 --- a/hive-ag3nt/src/turn.rs +++ b/hive-ag3nt/src/turn.rs @@ -51,6 +51,18 @@ pub async fn write_mcp_config(socket: &Path) -> Result { Ok(path) } +/// Drop the static `--settings` JSON next to the MCP config so we can +/// pass a path (`--settings `) instead of an ever-growing inline +/// blob — the CLI argv has a finite length budget. +pub async fn write_settings(socket: &Path) -> Result { + let parent = socket.parent().unwrap_or_else(|| Path::new("/run/hive")); + tokio::fs::create_dir_all(parent).await.ok(); + let path = parent.join("claude-settings.json"); + tokio::fs::write(&path, CLAUDE_SETTINGS).await?; + tracing::info!(path = %path.display(), "wrote claude settings"); + Ok(path) +} + /// Write the agent's / manager's static system prompt to a file next to /// the MCP config and return the path. Passed to claude via /// `--system-prompt-file`, replacing claude's default system prompt with @@ -91,16 +103,17 @@ pub async fn drive_turn( prompt: &str, mcp_config: &Path, system_prompt: &Path, + settings: &Path, bus: &Bus, flavor: mcp::Flavor, ) -> TurnOutcome { - match run_turn(prompt, mcp_config, system_prompt, bus, flavor).await { + match run_turn(prompt, mcp_config, system_prompt, settings, bus, flavor).await { TurnOutcome::PromptTooLong => { - if let Err(e) = compact_session(bus).await { + if let Err(e) = compact_session(settings, bus).await { tracing::warn!(error = %format!("{e:#}"), "compact failed"); return TurnOutcome::Failed(e); } - run_turn(prompt, mcp_config, system_prompt, bus, flavor).await + run_turn(prompt, mcp_config, system_prompt, settings, bus, flavor).await } other => other, } @@ -157,6 +170,7 @@ pub async fn run_turn( prompt: &str, mcp_config: &Path, system_prompt: &Path, + settings: &Path, bus: &Bus, flavor: mcp::Flavor, ) -> TurnOutcome { @@ -164,6 +178,7 @@ pub async fn run_turn( prompt, mcp_config, Some(system_prompt), + settings, bus, flavor, ClaudeMode::Turn, @@ -179,7 +194,7 @@ pub async fn run_turn( /// Run claude's built-in `/compact` slash command on the persistent /// session so the next turn can fit. No MCP tools needed; we just feed /// `/compact` over stdin and let claude rewrite its own history. -pub async fn compact_session(bus: &Bus) -> Result<()> { +pub async fn compact_session(settings: &Path, bus: &Bus) -> Result<()> { bus.emit(LiveEvent::Note( "context overflow — running /compact on the persistent session".into(), )); @@ -187,6 +202,7 @@ pub async fn compact_session(bus: &Bus) -> Result<()> { "/compact", Path::new("/dev/null"), None, + settings, bus, mcp::Flavor::Agent, // tool surface unused for /compact ClaudeMode::Compact, @@ -206,6 +222,7 @@ async fn run_claude( prompt: &str, mcp_config: &Path, system_prompt: Option<&Path>, + settings: &Path, bus: &Bus, flavor: mcp::Flavor, mode: ClaudeMode, @@ -219,7 +236,7 @@ async fn run_claude( .arg("haiku") .arg("--continue") .arg("--settings") - .arg(CLAUDE_SETTINGS); + .arg(settings); if let Some(p) = system_prompt { cmd.arg("--system-prompt-file").arg(p); }