nix fmt + rustfmt sweep
This commit is contained in:
parent
0cf120e9e9
commit
411cf86632
16 changed files with 171 additions and 133 deletions
|
|
@ -204,10 +204,12 @@ async fn serve(
|
||||||
// responsiveness if recv() times out.
|
// responsiveness if recv() times out.
|
||||||
tokio::time::sleep(interval).await;
|
tokio::time::sleep(interval).await;
|
||||||
}
|
}
|
||||||
Ok(AgentResponse::Ok
|
Ok(
|
||||||
|
AgentResponse::Ok
|
||||||
| AgentResponse::Status { .. }
|
| AgentResponse::Status { .. }
|
||||||
| AgentResponse::Recent { .. }
|
| AgentResponse::Recent { .. }
|
||||||
| AgentResponse::QuestionQueued { .. }) => {
|
| AgentResponse::QuestionQueued { .. },
|
||||||
|
) => {
|
||||||
tracing::warn!("recv produced unexpected response kind");
|
tracing::warn!("recv produced unexpected response kind");
|
||||||
}
|
}
|
||||||
Ok(AgentResponse::Err { message }) => {
|
Ok(AgentResponse::Err { message }) => {
|
||||||
|
|
|
||||||
|
|
@ -76,11 +76,25 @@ async fn main() -> Result<()> {
|
||||||
));
|
));
|
||||||
match initial {
|
match initial {
|
||||||
LoginState::Online => {
|
LoginState::Online => {
|
||||||
serve(&cli.socket, Duration::from_millis(poll_ms), bus, &files, turn_lock).await
|
serve(
|
||||||
|
&cli.socket,
|
||||||
|
Duration::from_millis(poll_ms),
|
||||||
|
bus,
|
||||||
|
&files,
|
||||||
|
turn_lock,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
LoginState::NeedsLogin => {
|
LoginState::NeedsLogin => {
|
||||||
turn::wait_for_login(&claude_dir, login_state, poll_ms).await;
|
turn::wait_for_login(&claude_dir, login_state, poll_ms).await;
|
||||||
serve(&cli.socket, Duration::from_millis(poll_ms), bus, &files, turn_lock).await
|
serve(
|
||||||
|
&cli.socket,
|
||||||
|
Duration::from_millis(poll_ms),
|
||||||
|
bus,
|
||||||
|
&files,
|
||||||
|
turn_lock,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,15 +23,19 @@ const HISTORY_CAPACITY: usize = 2000;
|
||||||
/// Path to the persisted event db. Overridable via `HYPERHIVE_EVENTS_DB`
|
/// Path to the persisted event db. Overridable via `HYPERHIVE_EVENTS_DB`
|
||||||
/// for dev / tests; otherwise derived from the agent's state dir.
|
/// for dev / tests; otherwise derived from the agent's state dir.
|
||||||
fn events_db_path() -> PathBuf {
|
fn events_db_path() -> PathBuf {
|
||||||
std::env::var_os("HYPERHIVE_EVENTS_DB")
|
std::env::var_os("HYPERHIVE_EVENTS_DB").map_or_else(
|
||||||
.map_or_else(|| crate::paths::state_dir().join("hyperhive-events.sqlite"), PathBuf::from)
|
|| crate::paths::state_dir().join("hyperhive-events.sqlite"),
|
||||||
|
PathBuf::from,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Path to the persisted model file. Overridable via `HYPERHIVE_MODEL_FILE`
|
/// Path to the persisted model file. Overridable via `HYPERHIVE_MODEL_FILE`
|
||||||
/// for dev / tests; otherwise derived from the agent's state dir.
|
/// for dev / tests; otherwise derived from the agent's state dir.
|
||||||
fn model_file_path() -> PathBuf {
|
fn model_file_path() -> PathBuf {
|
||||||
std::env::var_os("HYPERHIVE_MODEL_FILE")
|
std::env::var_os("HYPERHIVE_MODEL_FILE").map_or_else(
|
||||||
.map_or_else(|| crate::paths::state_dir().join("hyperhive-model"), PathBuf::from)
|
|| crate::paths::state_dir().join("hyperhive-model"),
|
||||||
|
PathBuf::from,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_model() -> Option<String> {
|
fn load_model() -> Option<String> {
|
||||||
|
|
|
||||||
|
|
@ -482,7 +482,10 @@ impl ManagerServer {
|
||||||
let (resp, retries) = self
|
let (resp, retries) = self
|
||||||
.dispatch(hive_sh4re::ManagerRequest::Start { name: args.name })
|
.dispatch(hive_sh4re::ManagerRequest::Start { name: args.name })
|
||||||
.await;
|
.await;
|
||||||
annotate_retries(format_ack(resp, "start", format!("started {name}")), retries)
|
annotate_retries(
|
||||||
|
format_ack(resp, "start", format!("started {name}")),
|
||||||
|
retries,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
@ -651,8 +654,7 @@ pub const SERVER_NAME: &str = "hyperhive";
|
||||||
/// state and silently evaporates on /compact or session reset — agents
|
/// state and silently evaporates on /compact or session reset — agents
|
||||||
/// should plan in /state notes instead. Edit later as our trust model
|
/// should plan in /state notes instead. Edit later as our trust model
|
||||||
/// evolves.
|
/// evolves.
|
||||||
pub const ALLOWED_BUILTIN_TOOLS: &[&str] =
|
pub const ALLOWED_BUILTIN_TOOLS: &[&str] = &["Bash", "Edit", "Glob", "Grep", "Read", "Write"];
|
||||||
&["Bash", "Edit", "Glob", "Grep", "Read", "Write"];
|
|
||||||
|
|
||||||
/// Which MCP tool surface to advertise via `--allowedTools`. The agent
|
/// Which MCP tool surface to advertise via `--allowedTools`. The agent
|
||||||
/// list is the strict subset of the manager list, so we just thread the
|
/// list is the strict subset of the manager list, so we just thread the
|
||||||
|
|
|
||||||
|
|
@ -108,8 +108,7 @@ pub async fn write_system_prompt(
|
||||||
mcp::Flavor::Agent => include_str!("../prompts/agent.md"),
|
mcp::Flavor::Agent => include_str!("../prompts/agent.md"),
|
||||||
mcp::Flavor::Manager => include_str!("../prompts/manager.md"),
|
mcp::Flavor::Manager => include_str!("../prompts/manager.md"),
|
||||||
};
|
};
|
||||||
let pronouns =
|
let pronouns = std::env::var("HIVE_OPERATOR_PRONOUNS").unwrap_or_else(|_| "she/her".to_owned());
|
||||||
std::env::var("HIVE_OPERATOR_PRONOUNS").unwrap_or_else(|_| "she/her".to_owned());
|
|
||||||
let body = template
|
let body = template
|
||||||
.replace("{label}", label)
|
.replace("{label}", label)
|
||||||
.replace("{operator_pronouns}", &pronouns);
|
.replace("{operator_pronouns}", &pronouns);
|
||||||
|
|
|
||||||
|
|
@ -192,9 +192,7 @@ async fn run_apply_commit(
|
||||||
// re-lock to the proposal commit on the prepare_deploy step
|
// re-lock to the proposal commit on the prepare_deploy step
|
||||||
// below. On build failure we roll main back to prev_main_sha so
|
// below. On build failure we roll main back to prev_main_sha so
|
||||||
// a crash leaves the agent on its last-good tree.
|
// a crash leaves the agent on its last-good tree.
|
||||||
if let Err(e) =
|
if let Err(e) = lifecycle::git_update_ref(applied_dir, "refs/heads/main", &proposal_ref).await {
|
||||||
lifecycle::git_update_ref(applied_dir, "refs/heads/main", &proposal_ref).await
|
|
||||||
{
|
|
||||||
return (
|
return (
|
||||||
Err(anyhow::anyhow!("ff main to {proposal_ref}: {e:#}")),
|
Err(anyhow::anyhow!("ff main to {proposal_ref}: {e:#}")),
|
||||||
None,
|
None,
|
||||||
|
|
@ -204,20 +202,14 @@ async fn run_apply_commit(
|
||||||
// main is ahead; working tree didn't sync. Roll main back to
|
// main is ahead; working tree didn't sync. Roll main back to
|
||||||
// keep the two consistent before bailing.
|
// keep the two consistent before bailing.
|
||||||
let _ = lifecycle::git_update_ref(applied_dir, "refs/heads/main", &prev_main_sha).await;
|
let _ = lifecycle::git_update_ref(applied_dir, "refs/heads/main", &prev_main_sha).await;
|
||||||
return (
|
return (Err(anyhow::anyhow!("read-tree to main: {e:#}")), None);
|
||||||
Err(anyhow::anyhow!("read-tree to main: {e:#}")),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 1 of the meta two-phase deploy: relock without committing.
|
// Phase 1 of the meta two-phase deploy: relock without committing.
|
||||||
if let Err(e) = crate::meta::prepare_deploy(&approval.agent).await {
|
if let Err(e) = crate::meta::prepare_deploy(&approval.agent).await {
|
||||||
let _ = lifecycle::git_update_ref(applied_dir, "refs/heads/main", &prev_main_sha).await;
|
let _ = lifecycle::git_update_ref(applied_dir, "refs/heads/main", &prev_main_sha).await;
|
||||||
let _ = lifecycle::git_read_tree_reset(applied_dir, "refs/heads/main").await;
|
let _ = lifecycle::git_read_tree_reset(applied_dir, "refs/heads/main").await;
|
||||||
return (
|
return (Err(anyhow::anyhow!("meta prepare_deploy: {e:#}")), None);
|
||||||
Err(anyhow::anyhow!("meta prepare_deploy: {e:#}")),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Container-level rebuild against meta#<name>.
|
// Container-level rebuild against meta#<name>.
|
||||||
|
|
@ -379,12 +371,8 @@ pub async fn deny(coord: &Coordinator, id: i64, note: Option<&str>) -> Result<()
|
||||||
{
|
{
|
||||||
let tag_name = format!("denied/{id}");
|
let tag_name = format!("denied/{id}");
|
||||||
let body = note.unwrap_or("").to_owned();
|
let body = note.unwrap_or("").to_owned();
|
||||||
if let Err(e) = lifecycle::git_tag_annotated(
|
if let Err(e) =
|
||||||
&applied_dir,
|
lifecycle::git_tag_annotated(&applied_dir, &tag_name, &proposal_ref, &body)
|
||||||
&tag_name,
|
|
||||||
&proposal_ref,
|
|
||||||
&body,
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
tracing::warn!(%id, error = ?e, "plant denied tag failed");
|
tracing::warn!(%id, error = ?e, "plant denied tag failed");
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,7 @@ pub struct AgentSocket {
|
||||||
pub handle: JoinHandle<()>,
|
pub handle: JoinHandle<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(
|
pub fn start(agent: &str, socket_path: &Path, coord: Arc<Coordinator>) -> Result<AgentSocket> {
|
||||||
agent: &str,
|
|
||||||
socket_path: &Path,
|
|
||||||
coord: Arc<Coordinator>,
|
|
||||||
) -> Result<AgentSocket> {
|
|
||||||
let agent = agent.to_owned();
|
let agent = agent.to_owned();
|
||||||
if let Some(parent) = socket_path.parent() {
|
if let Some(parent) = socket_path.parent() {
|
||||||
std::fs::create_dir_all(parent)
|
std::fs::create_dir_all(parent)
|
||||||
|
|
@ -215,21 +211,29 @@ async fn dispatch(req: &AgentRequest, agent: &str, coord: &Arc<Coordinator>) ->
|
||||||
let now = std::time::SystemTime::now();
|
let now = std::time::SystemTime::now();
|
||||||
let future = match now.checked_add(std::time::Duration::from_secs(*seconds)) {
|
let future = match now.checked_add(std::time::Duration::from_secs(*seconds)) {
|
||||||
Some(t) => t,
|
Some(t) => t,
|
||||||
None => return AgentResponse::Err {
|
None => {
|
||||||
message: format!("InSeconds overflow: {seconds}s exceeds system time range"),
|
return AgentResponse::Err {
|
||||||
},
|
message: format!(
|
||||||
|
"InSeconds overflow: {seconds}s exceeds system time range"
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let duration = match future.duration_since(std::time::UNIX_EPOCH) {
|
let duration = match future.duration_since(std::time::UNIX_EPOCH) {
|
||||||
Ok(d) => d,
|
Ok(d) => d,
|
||||||
Err(e) => return AgentResponse::Err {
|
Err(e) => {
|
||||||
|
return AgentResponse::Err {
|
||||||
message: format!("system time before UNIX_EPOCH: {e}"),
|
message: format!("system time before UNIX_EPOCH: {e}"),
|
||||||
},
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
match i64::try_from(duration.as_secs()) {
|
match i64::try_from(duration.as_secs()) {
|
||||||
Ok(ts) => Ok(ts),
|
Ok(ts) => Ok(ts),
|
||||||
Err(e) => return AgentResponse::Err {
|
Err(e) => {
|
||||||
|
return AgentResponse::Err {
|
||||||
message: format!("unix timestamp exceeds i64 range: {e}"),
|
message: format!("unix timestamp exceeds i64 range: {e}"),
|
||||||
},
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ReminderTiming::At { unix_timestamp } => Ok(*unix_timestamp),
|
ReminderTiming::At { unix_timestamp } => Ok(*unix_timestamp),
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,13 @@ impl Approvals {
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"INSERT INTO approvals (agent, kind, commit_ref, requested_at, status, description)
|
"INSERT INTO approvals (agent, kind, commit_ref, requested_at, status, description)
|
||||||
VALUES (?1, ?2, ?3, ?4, 'pending', ?5)",
|
VALUES (?1, ?2, ?3, ?4, 'pending', ?5)",
|
||||||
params![agent, kind_to_str(kind), commit_ref, now_unix(), description],
|
params![
|
||||||
|
agent,
|
||||||
|
kind_to_str(kind),
|
||||||
|
commit_ref,
|
||||||
|
now_unix(),
|
||||||
|
description
|
||||||
|
],
|
||||||
)?;
|
)?;
|
||||||
Ok(conn.last_insert_rowid())
|
Ok(conn.last_insert_rowid())
|
||||||
}
|
}
|
||||||
|
|
@ -164,8 +170,16 @@ impl Approvals {
|
||||||
/// approval so the caller can run the action and pass the agent name.
|
/// approval so the caller can run the action and pass the agent name.
|
||||||
pub fn mark_approved(&self, id: i64) -> Result<Approval> {
|
pub fn mark_approved(&self, id: i64) -> Result<Approval> {
|
||||||
let conn = self.conn.lock().unwrap();
|
let conn = self.conn.lock().unwrap();
|
||||||
let current: Option<(String, String, String, i64, String, Option<String>, Option<String>)> =
|
let current: Option<(
|
||||||
conn.query_row(
|
String,
|
||||||
|
String,
|
||||||
|
String,
|
||||||
|
i64,
|
||||||
|
String,
|
||||||
|
Option<String>,
|
||||||
|
Option<String>,
|
||||||
|
)> = conn
|
||||||
|
.query_row(
|
||||||
"SELECT agent, kind, commit_ref, requested_at, status, fetched_sha, description
|
"SELECT agent, kind, commit_ref, requested_at, status, fetched_sha, description
|
||||||
FROM approvals WHERE id = ?1",
|
FROM approvals WHERE id = ?1",
|
||||||
params![id],
|
params![id],
|
||||||
|
|
|
||||||
|
|
@ -1023,8 +1023,8 @@ async fn run_meta_update(coord: &Arc<crate::coordinator::Coordinator>, inputs: &
|
||||||
touched_agents
|
touched_agents
|
||||||
};
|
};
|
||||||
|
|
||||||
let current_rev = crate::auto_update::current_flake_rev(&coord.hyperhive_flake)
|
let current_rev =
|
||||||
.unwrap_or_default();
|
crate::auto_update::current_flake_rev(&coord.hyperhive_flake).unwrap_or_default();
|
||||||
// Sequential rebuild loop — the META_LOCK guards meta-side
|
// Sequential rebuild loop — the META_LOCK guards meta-side
|
||||||
// races but parallel nix builds also serialise via nix-daemon,
|
// races but parallel nix builds also serialise via nix-daemon,
|
||||||
// so sequential is just as fast in practice and keeps logs
|
// so sequential is just as fast in practice and keeps logs
|
||||||
|
|
|
||||||
|
|
@ -43,11 +43,7 @@ fn token_path(name: &str) -> PathBuf {
|
||||||
/// Probe whether `hive-forge` exists as a nixos-container. Cheap —
|
/// Probe whether `hive-forge` exists as a nixos-container. Cheap —
|
||||||
/// `nixos-container list` is just a directory scan in /etc.
|
/// `nixos-container list` is just a directory scan in /etc.
|
||||||
pub async fn is_present() -> bool {
|
pub async fn is_present() -> bool {
|
||||||
let Ok(out) = Command::new("nixos-container")
|
let Ok(out) = Command::new("nixos-container").arg("list").output().await else {
|
||||||
.arg("list")
|
|
||||||
.output()
|
|
||||||
.await
|
|
||||||
else {
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
if !out.status.success() {
|
if !out.status.success() {
|
||||||
|
|
|
||||||
|
|
@ -873,7 +873,9 @@ async fn run(args: &[&str]) -> Result<()> {
|
||||||
// in the last few lines.
|
// in the last few lines.
|
||||||
let stderr_cmdline = cmdline.clone();
|
let stderr_cmdline = cmdline.clone();
|
||||||
let stderr_tail: std::sync::Arc<std::sync::Mutex<std::collections::VecDeque<String>>> =
|
let stderr_tail: std::sync::Arc<std::sync::Mutex<std::collections::VecDeque<String>>> =
|
||||||
std::sync::Arc::new(std::sync::Mutex::new(std::collections::VecDeque::with_capacity(32)));
|
std::sync::Arc::new(std::sync::Mutex::new(
|
||||||
|
std::collections::VecDeque::with_capacity(32),
|
||||||
|
));
|
||||||
let stderr_tail_pump = stderr_tail.clone();
|
let stderr_tail_pump = stderr_tail.clone();
|
||||||
let pump_stderr = tokio::spawn(async move {
|
let pump_stderr = tokio::spawn(async move {
|
||||||
let mut lines = BufReader::new(stderr).lines();
|
let mut lines = BufReader::new(stderr).lines();
|
||||||
|
|
|
||||||
|
|
@ -228,8 +228,7 @@ async fn dispatch(req: &ManagerRequest, coord: &Arc<Coordinator>) -> ManagerResp
|
||||||
message: "update: hyperhive_flake has no canonical path".into(),
|
message: "update: hyperhive_flake has no canonical path".into(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
let _guard =
|
let _guard = coord.transient_guard(name, crate::coordinator::TransientKind::Rebuilding);
|
||||||
coord.transient_guard(name, crate::coordinator::TransientKind::Rebuilding);
|
|
||||||
let result = crate::auto_update::rebuild_agent(coord, name, ¤t_rev).await;
|
let result = crate::auto_update::rebuild_agent(coord, name, ¤t_rev).await;
|
||||||
drop(_guard);
|
drop(_guard);
|
||||||
match result {
|
match result {
|
||||||
|
|
@ -362,12 +361,8 @@ async fn submit_apply_commit(
|
||||||
)
|
)
|
||||||
.map_err(|e| anyhow::anyhow!("queue approval row: {e:#}"))?;
|
.map_err(|e| anyhow::anyhow!("queue approval row: {e:#}"))?;
|
||||||
let tag = format!("proposal/{id}");
|
let tag = format!("proposal/{id}");
|
||||||
let sha = match crate::lifecycle::git_fetch_to_tag(
|
let sha =
|
||||||
&applied_dir,
|
match crate::lifecycle::git_fetch_to_tag(&applied_dir, &proposed_dir, commit_ref, &tag)
|
||||||
&proposed_dir,
|
|
||||||
commit_ref,
|
|
||||||
&tag,
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
|
|
|
||||||
|
|
@ -252,9 +252,7 @@ fn render_flake(
|
||||||
// Free-text operator string — escape backslash + double-quote so a
|
// Free-text operator string — escape backslash + double-quote so a
|
||||||
// pronouns value like `he/him \ "rare"` round-trips into a valid
|
// pronouns value like `he/him \ "rare"` round-trips into a valid
|
||||||
// nix string literal without breaking the flake.
|
// nix string literal without breaking the flake.
|
||||||
let pronouns_escaped = operator_pronouns
|
let pronouns_escaped = operator_pronouns.replace('\\', "\\\\").replace('"', "\\\"");
|
||||||
.replace('\\', "\\\\")
|
|
||||||
.replace('"', "\\\"");
|
|
||||||
let _ = writeln!(
|
let _ = writeln!(
|
||||||
out,
|
out,
|
||||||
" dashboardPort = {dashboard_port};\n operatorPronouns = \"{pronouns_escaped}\";\n mkAgent = {{ name, isManager, port }}:"
|
" dashboardPort = {dashboard_port};\n operatorPronouns = \"{pronouns_escaped}\";\n mkAgent = {{ name, isManager, port }}:"
|
||||||
|
|
|
||||||
|
|
@ -68,17 +68,18 @@ pub async fn run(coord: &Arc<Coordinator>) -> Result<()> {
|
||||||
if let Err(e) = migrate_applied_repo(name).await {
|
if let Err(e) = migrate_applied_repo(name).await {
|
||||||
tracing::warn!(%name, error = ?e, "migration: applied repo rewrite failed");
|
tracing::warn!(%name, error = ?e, "migration: applied repo rewrite failed");
|
||||||
}
|
}
|
||||||
if let Err(e) = lifecycle::setup_proposed(&Coordinator::agent_proposed_dir(name), name)
|
if let Err(e) =
|
||||||
.await
|
lifecycle::setup_proposed(&Coordinator::agent_proposed_dir(name), name).await
|
||||||
{
|
{
|
||||||
tracing::warn!(%name, error = ?e, "migration: setup_proposed failed");
|
tracing::warn!(%name, error = ?e, "migration: setup_proposed failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 3: meta repo.
|
// Phase 3: meta repo.
|
||||||
let agents = lifecycle::agents_for_meta_listing().await.unwrap_or_default();
|
let agents = lifecycle::agents_for_meta_listing()
|
||||||
if let Err(e) =
|
.await
|
||||||
meta::sync_agents(
|
.unwrap_or_default();
|
||||||
|
if let Err(e) = meta::sync_agents(
|
||||||
&coord.hyperhive_flake,
|
&coord.hyperhive_flake,
|
||||||
coord.dashboard_port,
|
coord.dashboard_port,
|
||||||
&coord.operator_pronouns,
|
&coord.operator_pronouns,
|
||||||
|
|
@ -109,7 +110,8 @@ pub async fn run(coord: &Arc<Coordinator>) -> Result<()> {
|
||||||
all_ok = false;
|
all_ok = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if all_ok && !names.is_empty()
|
if all_ok
|
||||||
|
&& !names.is_empty()
|
||||||
&& let Err(e) = std::fs::write(repoint_marker(), b"done\n")
|
&& let Err(e) = std::fs::write(repoint_marker(), b"done\n")
|
||||||
{
|
{
|
||||||
tracing::warn!(error = ?e, "migration: write repoint marker failed");
|
tracing::warn!(error = ?e, "migration: write repoint marker failed");
|
||||||
|
|
@ -142,8 +144,7 @@ async fn migrate_applied_repo(name: &str) -> Result<()> {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let want = lifecycle::initial_flake_nix();
|
let want = lifecycle::initial_flake_nix();
|
||||||
std::fs::write(&flake_path, want)
|
std::fs::write(&flake_path, want).with_context(|| format!("write {}", flake_path.display()))?;
|
||||||
.with_context(|| format!("write {}", flake_path.display()))?;
|
|
||||||
raw_git(
|
raw_git(
|
||||||
&dir,
|
&dir,
|
||||||
&[
|
&[
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,8 @@ async fn dispatch(req: &HostRequest, coord: Arc<Coordinator>) -> HostResponse {
|
||||||
}
|
}
|
||||||
HostRequest::RequestSpawn { name } => {
|
HostRequest::RequestSpawn { name } => {
|
||||||
tracing::info!(%name, "request_spawn");
|
tracing::info!(%name, "request_spawn");
|
||||||
let id = coord
|
let id =
|
||||||
|
coord
|
||||||
.approvals
|
.approvals
|
||||||
.submit_kind(name, hive_sh4re::ApprovalKind::Spawn, "", None)?;
|
.submit_kind(name, hive_sh4re::ApprovalKind::Spawn, "", None)?;
|
||||||
tracing::info!(%id, %name, "spawn approval queued");
|
tracing::info!(%id, %name, "spawn approval queued");
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,9 @@
|
||||||
{ pkgs, lib, config, ... }:
|
{
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}:
|
||||||
{
|
{
|
||||||
# Shared scaffolding for any hyperhive harness container — both
|
# Shared scaffolding for any hyperhive harness container — both
|
||||||
# sub-agents (`agent-base.nix`) and the manager (`manager.nix`) extend
|
# sub-agents (`agent-base.nix`) and the manager (`manager.nix`) extend
|
||||||
|
|
@ -8,7 +13,10 @@
|
||||||
options.hyperhive.allowedRecipients = lib.mkOption {
|
options.hyperhive.allowedRecipients = lib.mkOption {
|
||||||
type = lib.types.listOf lib.types.str;
|
type = lib.types.listOf lib.types.str;
|
||||||
default = [ ];
|
default = [ ];
|
||||||
example = [ "alice" "manager" ];
|
example = [
|
||||||
|
"alice"
|
||||||
|
"manager"
|
||||||
|
];
|
||||||
description = ''
|
description = ''
|
||||||
Names this agent is allowed to `send` to via
|
Names this agent is allowed to `send` to via
|
||||||
`mcp__hyperhive__send`. Empty list (the default) means
|
`mcp__hyperhive__send`. Empty list (the default) means
|
||||||
|
|
@ -29,7 +37,8 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
options.hyperhive.extraMcpServers = lib.mkOption {
|
options.hyperhive.extraMcpServers = lib.mkOption {
|
||||||
type = lib.types.attrsOf (lib.types.submodule {
|
type = lib.types.attrsOf (
|
||||||
|
lib.types.submodule {
|
||||||
options = {
|
options = {
|
||||||
command = lib.mkOption {
|
command = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
|
|
@ -48,7 +57,10 @@
|
||||||
allowedTools = lib.mkOption {
|
allowedTools = lib.mkOption {
|
||||||
type = lib.types.listOf lib.types.str;
|
type = lib.types.listOf lib.types.str;
|
||||||
default = [ "*" ];
|
default = [ "*" ];
|
||||||
example = [ "send_message" "join_room" ];
|
example = [
|
||||||
|
"send_message"
|
||||||
|
"join_room"
|
||||||
|
];
|
||||||
description = ''
|
description = ''
|
||||||
Tool names this MCP server is auto-approved to call via
|
Tool names this MCP server is auto-approved to call via
|
||||||
`--allowedTools`. Single entry `"*"` (the default) means
|
`--allowedTools`. Single entry `"*"` (the default) means
|
||||||
|
|
@ -59,7 +71,8 @@
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
);
|
||||||
default = { };
|
default = { };
|
||||||
example = lib.literalExpression ''
|
example = lib.literalExpression ''
|
||||||
{
|
{
|
||||||
|
|
@ -120,7 +133,10 @@
|
||||||
options.hyperhive.claudePlugins = lib.mkOption {
|
options.hyperhive.claudePlugins = lib.mkOption {
|
||||||
type = lib.types.listOf lib.types.str;
|
type = lib.types.listOf lib.types.str;
|
||||||
default = [ ];
|
default = [ ];
|
||||||
example = [ "formatter@my-marketplace" "thinking-tools@anthropics" ];
|
example = [
|
||||||
|
"formatter@my-marketplace"
|
||||||
|
"thinking-tools@anthropics"
|
||||||
|
];
|
||||||
description = ''
|
description = ''
|
||||||
Claude Code plugins to install at harness boot. Each entry is
|
Claude Code plugins to install at harness boot. Each entry is
|
||||||
passed verbatim to `claude plugin install <spec>` once per
|
passed verbatim to `claude plugin install <spec>` once per
|
||||||
|
|
@ -134,8 +150,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
environment.etc."hyperhive/extra-mcp.json".text =
|
environment.etc."hyperhive/extra-mcp.json".text = builtins.toJSON config.hyperhive.extraMcpServers;
|
||||||
builtins.toJSON config.hyperhive.extraMcpServers;
|
|
||||||
|
|
||||||
environment.etc."hyperhive/send-allow.json".text =
|
environment.etc."hyperhive/send-allow.json".text =
|
||||||
builtins.toJSON config.hyperhive.allowedRecipients;
|
builtins.toJSON config.hyperhive.allowedRecipients;
|
||||||
|
|
@ -181,7 +196,10 @@
|
||||||
Type = "oneshot";
|
Type = "oneshot";
|
||||||
RemainAfterExit = true;
|
RemainAfterExit = true;
|
||||||
};
|
};
|
||||||
path = [ pkgs.tea pkgs.coreutils ];
|
path = [
|
||||||
|
pkgs.tea
|
||||||
|
pkgs.coreutils
|
||||||
|
];
|
||||||
script = ''
|
script = ''
|
||||||
set -eu
|
set -eu
|
||||||
CONFIG=/root/.config/tea/config.yml
|
CONFIG=/root/.config/tea/config.yml
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue