feat(#1969): ship claude settings via /etc/claude-code/managed-settings.json
This commit is contained in:
parent
70d1cdc859
commit
b231ed2392
6 changed files with 49 additions and 65 deletions
|
|
@ -9,7 +9,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
|||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{Context, Result, bail};
|
||||
use anyhow::{Result, bail};
|
||||
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||
use tokio::process::Command;
|
||||
|
||||
|
|
@ -17,12 +17,13 @@ use crate::events::{Bus, LiveEvent};
|
|||
use crate::login::LoginState;
|
||||
use crate::mcp;
|
||||
|
||||
// `--settings` JSON is read at runtime from
|
||||
// `$HIVE_ASSETS_DIR/prompts/claude-settings.json` via
|
||||
// `hive_sh4re::assets::claude_settings()`. We turn off claude's
|
||||
// in-session auto-compaction and its cross-session auto-memory because
|
||||
// hyperhive owns those concerns (`/compact` on overflow, notes
|
||||
// persistence under `/state`). Unknown keys are silently ignored by
|
||||
// Hive-enforced claude settings ship at `/etc/claude-code/managed-settings.json`
|
||||
// (wired in `nix/templates/harness-base.nix` from the `prompts/claude-settings.json`
|
||||
// asset). claude-code auto-discovers that managed path — precedence #1,
|
||||
// read-only, un-overridable — so the harness no longer passes `--settings`.
|
||||
// We turn off claude's in-session auto-compaction and its cross-session
|
||||
// auto-memory because hyperhive owns those concerns (`/compact` on overflow,
|
||||
// notes persistence under `/state`). Unknown keys are silently ignored by
|
||||
// claude-code; if a key gets renamed we'll spot it because the
|
||||
// corresponding behavior will start firing mid-turn again.
|
||||
|
||||
|
|
@ -95,8 +96,10 @@ conversation to go on. Do not start new work or reply to anyone — just write y
|
|||
and end the turn.";
|
||||
|
||||
/// The set of files claude reads on every invocation: the MCP server
|
||||
/// config (`--mcp-config`), static settings (`--settings`), and the
|
||||
/// pre-rendered role/tools system prompt (`--system-prompt-file`).
|
||||
/// config (`--mcp-config`) and the pre-rendered role/tools system
|
||||
/// prompt (`--system-prompt-file`). Static settings are no longer
|
||||
/// passed here — they live at `/etc/claude-code/managed-settings.json`
|
||||
/// and claude auto-discovers them.
|
||||
/// Materialised once at harness startup; shared between the turn loop
|
||||
/// and the operator-driven `/compact` path so both invocations look
|
||||
/// identical to claude (same MCP surface, same allowed tools, same
|
||||
|
|
@ -104,7 +107,6 @@ and end the turn.";
|
|||
#[derive(Clone)]
|
||||
pub struct TurnFiles {
|
||||
pub mcp_config: PathBuf,
|
||||
pub settings: PathBuf,
|
||||
pub system_prompt: PathBuf,
|
||||
}
|
||||
|
||||
|
|
@ -118,7 +120,6 @@ impl TurnFiles {
|
|||
pub async fn prepare(socket: &Path, label: &str) -> Result<Self> {
|
||||
Ok(Self {
|
||||
mcp_config: write_mcp_config(socket).await?,
|
||||
settings: write_settings(socket).await?,
|
||||
system_prompt: write_system_prompt(socket, label).await?,
|
||||
})
|
||||
}
|
||||
|
|
@ -145,33 +146,6 @@ pub async fn write_mcp_config(socket: &Path) -> Result<PathBuf> {
|
|||
Ok(path)
|
||||
}
|
||||
|
||||
/// Drop the static `--settings` JSON next to the MCP config so we can
|
||||
/// pass a path (`--settings <file>`) instead of an ever-growing inline
|
||||
/// blob — the CLI argv has a finite length budget.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the settings file cannot be written.
|
||||
pub async fn write_settings(_socket: &Path) -> Result<PathBuf> {
|
||||
let parent = crate::paths::config_dir();
|
||||
tokio::fs::create_dir_all(&parent).await.ok();
|
||||
let path = parent.join("claude-settings.json");
|
||||
// Source-of-truth is `$HIVE_ASSETS_DIR/prompts/claude-settings.json`;
|
||||
// copy through the per-agent runtime dir so claude reads it from the
|
||||
// same socket-adjacent location every time and so a future override
|
||||
// (per-agent settings JSON layer) drops in cleanly.
|
||||
let src = hive_sh4re::assets::claude_settings();
|
||||
tokio::fs::copy(&src, &path).await.with_context(|| {
|
||||
format!(
|
||||
"copy claude settings from {} to {}",
|
||||
src.display(),
|
||||
path.display()
|
||||
)
|
||||
})?;
|
||||
tracing::info!(path = %path.display(), "wrote claude settings");
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
/// Thin re-export of [`crate::prompt::write_system_prompt`] for
|
||||
/// callers that already import this module. The actual rendering +
|
||||
/// marker-block logic lives in `prompt.rs`; this is just the public
|
||||
|
|
@ -579,8 +553,9 @@ fn session_refreshed(prev: DirSnapshot, now: DirSnapshot) -> bool {
|
|||
/// live event bus. Prompt goes over stdin (variadic
|
||||
/// `--allowedTools`/`--tools` would otherwise eat a trailing positional
|
||||
/// prompt). The session is persistent across turns via `--continue` and
|
||||
/// claude's in-session auto-compact is disabled via `--settings` so it
|
||||
/// doesn't stall mid-turn — hyperhive owns compaction.
|
||||
/// claude's in-session auto-compact is disabled via the managed
|
||||
/// settings at `/etc/claude-code/managed-settings.json` so it doesn't
|
||||
/// stall mid-turn — hyperhive owns compaction.
|
||||
pub async fn run_turn(prompt: &str, files: &TurnFiles, bus: &Bus) -> TurnOutcome {
|
||||
match run_claude(prompt, files, bus).await {
|
||||
Ok((true, _, _)) => TurnOutcome::PromptTooLong,
|
||||
|
|
@ -675,9 +650,7 @@ async fn run_claude(prompt: &str, files: &TurnFiles, bus: &Bus) -> Result<(bool,
|
|||
.arg("--model")
|
||||
.arg(&model)
|
||||
.arg("--effort")
|
||||
.arg(&effort)
|
||||
.arg("--settings")
|
||||
.arg(&files.settings);
|
||||
.arg(&effort);
|
||||
if resume {
|
||||
cmd.arg("--continue");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,9 +51,9 @@ struct AppState {
|
|||
bus: Bus,
|
||||
socket: PathBuf,
|
||||
/// Same `TurnFiles` the harness's turn loop uses. Shared so
|
||||
/// `/api/compact` re-uses the exact MCP config / system prompt /
|
||||
/// settings claude saw on the last regular turn — keeps the
|
||||
/// session shape identical across compact + normal turns.
|
||||
/// `/api/compact` re-uses the exact MCP config / system prompt
|
||||
/// claude saw on the last regular turn — keeps the session shape
|
||||
/// identical across compact + normal turns.
|
||||
files: TurnFiles,
|
||||
/// Prevents `/api/compact` from racing with an in-flight normal turn.
|
||||
turn_lock: TurnLock,
|
||||
|
|
|
|||
Loading…
Reference in a new issue