agent permissions: one set, tool groups included, set at agent creation
mara on #5170: "dont split caps and perms. its all perms. / dont include the now empty tool groups / nothing set -> default / also add perms to agent creation screen". - hive-sh4re: one `Permission` enum (messaging, meta, inbox, execution, web_tools, manage_root_agent) replaces `ToolGroup` and `Capability`; `lifecycle`/`forge` are gone. `Permission::default_for` is the default for an agent the swarm sets nothing for. - The record is `{ "permissions": [...] }`. Missing key, unset field or no successful read yet = default; `[]` = none; a failed read after a good one keeps the last value. - hive-agent reads its own key every 10s and builds each turn's --tools, --allowedTools and MCP config (rewritten before every turn) from it; the subagent daemon reads it on each start/continue. Agents get a read-only grant on their own key. - hive-c0re restarts an agent only when its manage_root_agent changes. The import now merges meta/capabilities.json and meta/tool-groups.json into one list per agent and deletes both; the "import pending, don't read" decision is `ImportOutcome::read_allowed`, table-tested. - swarm-controller: `GET /api/permissions` (names + default) and an optional `permissions` on `POST /api/agents`, stored before the job is queued. swarm-ui's create-agent form gets a permissions field. - Deleted: HIVE_TOOL_GROUPS and its forward to the subagent daemon, tool_groups.rs, ruth's tool-group seed, the permissions rebuild job (PermPayload/WritePermFile/perm_change), the dashboard permissions tab and its stale-entries panel. Part of #5170.
This commit is contained in:
parent
f862354e43
commit
f0844f6da3
73 changed files with 1578 additions and 2742 deletions
|
|
@ -120,7 +120,6 @@ pub(super) async fn run_node(
|
|||
// The payload rides the node and is destructured here, so the executor
|
||||
// takes it directly instead of re-matching the kind behind a `bail!`
|
||||
// that could never fire.
|
||||
NodeKind::WritePermFile { payload, .. } => run_write_perm_file(coord, agent, payload).await,
|
||||
NodeKind::EmitRebuilt { ok, .. } => {
|
||||
run_emit_rebuilt(coord, agent, coord.job_queue.root_of(id), *ok).await;
|
||||
Ok(())
|
||||
|
|
@ -790,30 +789,6 @@ async fn run_write_dropin(coord: &Arc<Coordinator>, name: &str) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Write + commit the perm file(s) (fused under `META_LOCK` so the
|
||||
/// working tree is never left dirty), then emit the P3RM1SS10NS-tab
|
||||
/// snapshots so the dashboard reflects the new assignment.
|
||||
async fn run_write_perm_file(
|
||||
coord: &Arc<Coordinator>,
|
||||
name: &str,
|
||||
payload: &super::model::PermPayload,
|
||||
) -> Result<()> {
|
||||
use super::model::PermPayload;
|
||||
// Runs under the deploy window (it declares `Resource::MetaWindow`): a
|
||||
// perm commit landing inside another node's staged prepare→finalize
|
||||
// window would sweep the staged deploy lock into its commit (the
|
||||
// commits are also path-limited in meta.rs — belt and braces).
|
||||
match payload {
|
||||
PermPayload::ToolGroups { groups } => {
|
||||
crate::meta::commit_tool_groups(name, groups)
|
||||
.await
|
||||
.with_context(|| format!("commit tool-groups for {name}"))?;
|
||||
coord.emit_tool_groups_snapshot();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compute which agents a `nix flake update <inputs>` on the meta
|
||||
/// flake affects — the fan-out set for `MetaUpdate` DAGs. Empty
|
||||
/// `inputs` or any input under `hyperhive` → every container;
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ use hive_jobq::{Graph, NodeId};
|
|||
use hive_jobq_wire::{GraphNode, GraphWire};
|
||||
use tokio::sync::Notify;
|
||||
|
||||
pub use model::{NodeKind, PermPayload, State};
|
||||
pub use model::{NodeKind, State};
|
||||
use resource::Resource;
|
||||
|
||||
/// A job under construction: `hive_jobq`'s builder over this queue's payload
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//! Data model for the generic job-DAG queue: the node kinds — the primitive
|
||||
//! operations — and what each one carries. The `State` / `PermPayload` wire
|
||||
//! enums live in `hive_host_sock::jobs` (they travel on the host admin
|
||||
//! socket) and are re-exported here for the queue's internal use. The graph
|
||||
//! operations — and what each one carries. The `State` wire
|
||||
//! enum lives in `hive_host_sock::jobs` (it travels on the host admin
|
||||
//! socket) and is re-exported here for the queue's internal use. The graph
|
||||
//! itself is served through `hive_jobq_wire`'s generic projection — there is
|
||||
//! no second, typed view of it any more.
|
||||
//!
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
//! job is exactly the nodes it declared. See `docs/scheduler/coordinator.md::Job queue`
|
||||
//! for the full design.
|
||||
|
||||
pub use hive_host_sock::jobs::{PermPayload, State};
|
||||
pub use hive_host_sock::jobs::State;
|
||||
use serde::Serialize;
|
||||
|
||||
/// The primitive operations — each kind maps to one executor fn in
|
||||
|
|
@ -98,12 +98,10 @@ pub enum NodeKind {
|
|||
PauseDrain { agent: String },
|
||||
/// `set_nspawn_flags` + `set_resource_limits` + daemon-reload.
|
||||
WriteDropin { agent: String },
|
||||
/// Commit `tool-groups.json` per its `payload`.
|
||||
WritePermFile { agent: String, payload: PermPayload },
|
||||
/// Group root of a rebuild subtree — the **brace**, scoped to one
|
||||
/// agent. Why braces exist and what they cost: _Braces_.
|
||||
AgentWindow { agent: String },
|
||||
/// Tail node of a rebuild / perm-change: emit this agent's `Rebuilt`
|
||||
/// Tail node of a rebuild: emit this agent's `Rebuilt`
|
||||
/// manager event. One node per agent *and* per outcome; no cancel
|
||||
/// variant (a cancelled DAG never ran, so there is no rebuild to
|
||||
/// report).
|
||||
|
|
@ -185,7 +183,6 @@ impl NodeKind {
|
|||
| NodeKind::PauseSignal { agent }
|
||||
| NodeKind::PauseDrain { agent }
|
||||
| NodeKind::WriteDropin { agent }
|
||||
| NodeKind::WritePermFile { agent, .. }
|
||||
| NodeKind::AgentWindow { agent }
|
||||
| NodeKind::EmitRebuilt { agent, .. }
|
||||
| NodeKind::SetWanted { agent, .. } => agent,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
//! `lifecycle::is_running` read), so [`super::power`] assembles them out of
|
||||
//! the primitives this module exports ([`rebuild_nodes`]).
|
||||
|
||||
use super::model::{NodeKind, PermPayload};
|
||||
use super::model::NodeKind;
|
||||
use super::resource::Resource;
|
||||
use super::{Handle, JobBuilder};
|
||||
|
||||
|
|
@ -403,26 +403,6 @@ pub fn destroy(builder: &JobBuilder, agent: &str, purge: bool) {
|
|||
.after_ok(last);
|
||||
}
|
||||
|
||||
/// Perm change: commit the JSON file, then the rebuild subgraph so
|
||||
/// the updated `HIVE_TOOL_GROUPS` env var takes
|
||||
/// effect in the container. Group-roots are `WritePermFile` plus the rebuild
|
||||
/// subgraph's `MetaSync` / `Prebuild` / `Reconcile`, so the `EmitRebuilt` tail
|
||||
/// edges all four.
|
||||
pub fn perm_change(builder: &JobBuilder, agent: &str, payload: PermPayload) {
|
||||
let write = builder
|
||||
.node(NodeKind::WritePermFile {
|
||||
agent: agent.to_owned(),
|
||||
payload,
|
||||
})
|
||||
.needs(Resource::MetaWindow);
|
||||
let roots = rebuild_nodes(builder, agent, true, Some(write));
|
||||
emit_rebuilt_tails(
|
||||
builder,
|
||||
agent,
|
||||
&[write, roots.meta_sync, roots.agent_window, roots.reconcile],
|
||||
);
|
||||
}
|
||||
|
||||
/// Meta-input lock bump. The `MetaLock` executor grows one rebuild subgraph
|
||||
/// per affected agent into *this same* DAG on completion (declared onto the
|
||||
/// builder it was handed) — appended *after* the bump lands so their prebuilds
|
||||
|
|
|
|||
|
|
@ -1515,38 +1515,6 @@ fn destroy_container_must_not_suppress_crash_watch() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn perm_change_shape_prefixes_rebuild_chain() {
|
||||
let q = JobQueue::new(1);
|
||||
insert(&q, |builder| {
|
||||
templates::perm_change(
|
||||
builder,
|
||||
"agent-a",
|
||||
PermPayload::ToolGroups { groups: vec![] },
|
||||
);
|
||||
});
|
||||
assert_eq!(
|
||||
declared_shape(&q)
|
||||
.iter()
|
||||
.map(|d| d.kind)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
"write_perm_file",
|
||||
"meta_sync",
|
||||
"agent_window",
|
||||
"prebuild",
|
||||
"stop_for_update",
|
||||
"swap",
|
||||
"rebuild_bookkeeping",
|
||||
"reconcile",
|
||||
// the ok / !ok tail pair
|
||||
"emit_rebuilt",
|
||||
"emit_rebuilt",
|
||||
],
|
||||
"the perm write prefixes an otherwise ordinary rebuild chain"
|
||||
);
|
||||
}
|
||||
|
||||
// ---- standalone sweeps ----
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Reference in a new issue