Watch
0
0
Fork
You've already forked hyperhive
0

move Permission into hive-types, drop swarm-controller's hive-sh4re dep

This commit is contained in:
atlas 2026-10-18 10:14:30 +02:00
commit e4a9005468
27 changed files with 221 additions and 175 deletions

View file

@ -15,4 +15,3 @@ schemars.workspace = true
serde.workspace = true
# `stream_enrich` walks raw claude `stream-json` values into `term_msg` rows.
serde_json.workspace = true
strum.workspace = true

View file

@ -18,7 +18,7 @@
//! server instead); the permission transfers to a subagent, not the
//! mechanism.
use serde::{Deserialize, Serialize};
use hive_types::Permission;
/// Built-in claude tools present in every session regardless of permissions.
/// Anything not in this list (and not added by a permission's
@ -90,131 +90,10 @@ pub fn subagent_builtin_tools_for(perms: &[Permission]) -> Vec<&'static str> {
tools
}
/// One thing an agent may be permitted. Stored by its `snake_case` name.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, strum::IntoStaticStr)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum Permission {
/// `send`, `recv`, `ack_until`
Messaging,
/// `get_agent_meta` (`set_status` is always-on — see `ALWAYS_ON_TOOLS`)
Meta,
/// `get_loose_ends`, `cancel_loose_end`, `remind`
Inbox,
/// Gates whether the `bash` MCP server (`mcp__bash__run`/`status`/
/// `kill`) is rendered into the agent's config at all — see
/// `extra_server_required_permission` in `hive-agent/src/mcp_config.rs`.
/// `tools()` returns `&[]`: this isn't a `mcp__hyperhive__*`
/// allowlist entry, the gate lives at config-render time instead.
Execution,
/// Claude built-in web egress tools: `WebFetch` (retrieve a URL) and
/// `WebSearch` (search the web). Both are omitted from `--tools`
/// without it; holding it enables them in the session and in
/// `--allowedTools` so they run without a confirmation prompt.
WebTools,
/// Agent may manage *any* agent in the hive. Concretely, hive-c0re
/// bind-mounts every agent's state dir (RW) and config dir (RO) into the
/// holder's container, plus `/applied` and `/meta` read-only, so the
/// holder can recover an agent that is down. See
/// `lifecycle::host_config::set_nspawn_flags`.
///
/// The name is historical, from when "the root agent" was the widest
/// thing to manage. Kept under that spelling because every stored grant
/// carries it.
ManageRootAgent,
}
impl Permission {
/// Every permission in a stable order.
pub const ALL: &'static [Self] = &[
Self::Messaging,
Self::Meta,
Self::Inbox,
Self::Execution,
Self::WebTools,
Self::ManageRootAgent,
];
/// What an agent the swarm sets nothing for holds, whatever its name.
pub const DEFAULT: &'static [Self] =
&[Self::Messaging, Self::Meta, Self::Inbox, Self::Execution];
/// The MCP tool names (without the `mcp__hyperhive__` prefix) this
/// permission grants. Empty for the permissions that grant something
/// else; see each variant's doc comment.
#[must_use]
pub fn tools(self) -> &'static [&'static str] {
match self {
Self::Messaging => &["send", "recv", "ack_until"],
Self::Meta => &["get_agent_meta"],
Self::Inbox => &["get_loose_ends", "cancel_loose_end", "remind"],
// `Execution` grants the out-of-process `bash` MCP server,
// gated at config-render time in `hive-agent/src/mcp_config.rs`
// — an out-of-process server has no later enforcement point, so
// that gate is the actual security boundary. `WebTools` grants
// Claude built-ins (`builtin_tools()`); `ManageRootAgent` grants
// bind mounts.
Self::Execution | Self::WebTools | Self::ManageRootAgent => &[],
}
}
/// MCP tools that are always exposed regardless of which permissions an
/// agent holds. `set_status` lives here because the operator
/// dashboard depends on every agent being able to report its status
/// chip — gating it would let a misconfigured agent go dark on the
/// dashboard. The server-side `SetStatus` handler has no permission
/// check either (only length validation), so listing it here keeps the
/// `--allowedTools` list honest with that reality.
///
/// `compact` lives here too: it's pure self-management (no cross-agent
/// effect, no privilege), gated server-side on context usage, and every
/// agent should be able to reach for it — same reasoning as `set_status`.
///
/// `mark_todos_done` too: todos are pushed to an agent whether or not it
/// holds `Inbox` (which only gates `get_loose_ends`/`cancel_loose_end`/
/// `remind`), so an agent without `Inbox` must still be able to clear
/// them.
pub const ALWAYS_ON_TOOLS: &'static [&'static str] =
&["set_status", "compact", "mark_todos_done"];
/// The Claude built-in tool names this permission enables. Only
/// `WebTools` returns a non-empty slice.
#[must_use]
pub fn builtin_tools(self) -> &'static [&'static str] {
match self {
Self::WebTools => &["WebFetch", "WebSearch"],
_ => &[],
}
}
}
#[cfg(test)]
mod tests {
use super::*;
/// The stored names are the `snake_case` spellings the old tool-group
/// and capability files used, so an imported grant decodes.
#[test]
fn stored_names_are_snake_case() {
let names: Vec<&str> = Permission::ALL.iter().map(|p| <&str>::from(*p)).collect();
assert_eq!(
names,
[
"messaging",
"meta",
"inbox",
"execution",
"web_tools",
"manage_root_agent"
]
);
for perm in Permission::ALL {
let json = serde_json::to_string(perm).unwrap();
assert_eq!(json, format!("\"{}\"", <&str>::from(*perm)));
}
assert!(serde_json::from_str::<Permission>("\"lifecycle\"").is_err());
}
/// Adding permissions only ever adds built-ins — the base set is in every
/// resolution, and no permission can remove one. Both `--tools` callers (the
/// harness and the subagent daemon) rely on this to reason about "the