Compare commits
32 changed files with 217 additions and 339 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
|
@ -1675,7 +1675,6 @@ dependencies = [
|
|||
"hive-runtime",
|
||||
"hive-sh4re",
|
||||
"hive-sock-client",
|
||||
"hive-types",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
|
|
@ -2025,6 +2024,7 @@ dependencies = [
|
|||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"strum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4869,7 +4869,6 @@ dependencies = [
|
|||
"backon",
|
||||
"base64",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"forgejo-api",
|
||||
"futures-util",
|
||||
"getrandom 0.3.4",
|
||||
|
|
@ -4877,6 +4876,7 @@ dependencies = [
|
|||
"hive-jobq-metrics",
|
||||
"hive-jobq-wire",
|
||||
"hive-log",
|
||||
"hive-sh4re",
|
||||
"hive-types",
|
||||
"hmac 0.13.0",
|
||||
"http",
|
||||
|
|
@ -5031,7 +5031,7 @@ dependencies = [
|
|||
"async-nats",
|
||||
"axum",
|
||||
"data-encoding",
|
||||
"hive-types",
|
||||
"hive-sh4re",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
|
|||
|
|
@ -272,7 +272,7 @@ JSON-line protocol with their own result types; RFC 9457 is an HTTP format.
|
|||
|
||||
## Permissions
|
||||
|
||||
One set of named `Permission` values (`hive_types::Permission`)
|
||||
One set of named `Permission` values (`hive_sh4re::permissions::Permission`)
|
||||
decides what an agent may do: which MCP and built-in tools it gets, and which
|
||||
host access it has.
|
||||
|
||||
|
|
@ -342,7 +342,7 @@ agent grant itself any tool by submitting a config commit, bypassing the
|
|||
operator gate.
|
||||
|
||||
**Adding to the set** — add a variant to `Permission` in
|
||||
`hive-types/src/permission.rs` and to `Permission::ALL`, and enforce it where
|
||||
`hive-sh4re/src/permissions.rs` and to `Permission::ALL`, and enforce it where
|
||||
it applies. A new `#[tool]` fn on `AgentServer` (`hive-agent-mcp/src/mcp/mod.rs`)
|
||||
goes into the matching `Permission::tools()` slice: that's the single source
|
||||
of truth `mcp_config::allowed_mcp_tools` (`hive-agent/src/mcp_config.rs`)
|
||||
|
|
|
|||
|
|
@ -197,8 +197,7 @@ status hint lives in the wake prompt + UI header, not here.
|
|||
## Tool allowlist (`hive_sh4re::permissions::ALLOWED_BUILTIN_TOOLS`)
|
||||
|
||||
The built-in list and the `--tools` value it resolves to live in
|
||||
`hive-sh4re` (the `Permission` enum itself is in `hive-types`), not in the
|
||||
harness, because the
|
||||
`hive-sh4re` alongside `Permission`, not in the harness, because the
|
||||
subagent daemon spawns its own `claude` and must resolve the same set —
|
||||
see [`docs/tools/subagent.md`](../tools/subagent.md).
|
||||
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ impl AgentServer {
|
|||
}
|
||||
|
||||
// IMPORTANT: when adding a new `#[tool]` fn to this impl, also add
|
||||
// its name to the matching `Permission::tools()` slice in hive-types.
|
||||
// its name to the matching `Permission::tools()` slice in hive-sh4re.
|
||||
// Claude Code's permission gate refuses uninlisted MCP tools in
|
||||
// non-interactive `--print` mode with "permissions not granted yet".
|
||||
#[tool_router]
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ hive-core-agent-sock.workspace = true
|
|||
hive-log.workspace = true
|
||||
hive-sh4re.workspace = true
|
||||
hive-sock-client.workspace = true
|
||||
hive-types.workspace = true
|
||||
libc.workspace = true
|
||||
opentelemetry.workspace = true
|
||||
opentelemetry_sdk.workspace = true
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
//! running MCP server (a separate binary) — the `send` allow-list check that
|
||||
//! server enforces lives alongside it in the `hive-agent-mcp` crate.
|
||||
|
||||
use hive_types::Permission;
|
||||
use hive_sh4re::permissions::Permission;
|
||||
|
||||
/// Name of the hyperhive MCP server inside claude's view. Claude prefixes
|
||||
/// tools as `mcp__<this>__<tool>` (e.g. `mcp__hyperhive__send`).
|
||||
|
|
@ -55,7 +55,7 @@ fn extra_server_enabled(server: &str, perms: &[Permission]) -> bool {
|
|||
#[cfg(test)]
|
||||
mod extra_server_gate_tests {
|
||||
use super::{extra_server_enabled, extra_server_required_permission};
|
||||
use hive_types::Permission;
|
||||
use hive_sh4re::permissions::Permission;
|
||||
|
||||
#[test]
|
||||
fn bash_is_gated_behind_execution() {
|
||||
|
|
@ -84,7 +84,7 @@ mod extra_server_gate_tests {
|
|||
|
||||
/// MCP tools claude is allowed to call without prompting, derived from
|
||||
/// the supplied permissions. Adding a new `#[tool]` fn to a server impl
|
||||
/// requires updating the matching `Permission::tools()` slice in hive-types
|
||||
/// requires updating the matching `Permission::tools()` slice in hive-sh4re
|
||||
/// (single source of truth).
|
||||
#[must_use]
|
||||
pub fn allowed_mcp_tools(perms: &[Permission]) -> Vec<String> {
|
||||
|
|
@ -212,7 +212,7 @@ fn build_mcp_servers() -> serde_json::Map<String, serde_json::Value> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{SERVER_NAME, allowed_mcp_tools};
|
||||
use hive_types::Permission;
|
||||
use hive_sh4re::permissions::Permission;
|
||||
|
||||
fn qualified(tool: &str) -> String {
|
||||
format!("mcp__{SERVER_NAME}__{tool}")
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ use std::path::Path;
|
|||
use std::sync::{PoisonError, RwLock};
|
||||
use std::time::Duration;
|
||||
|
||||
use hive_types::Permission;
|
||||
use hive_sh4re::permissions::Permission;
|
||||
use swarm_queue_client::agent_permissions::{HIVE_COPY_PATH, HiveCopy, Read, read};
|
||||
|
||||
/// How often the key is read. Also the longest a change can take to reach
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
//! both a text block and a `tool_use` block produces two rows.
|
||||
|
||||
pub use hive_sh4re::term_msg::{ClassifyCtx, Level, TermMsg, iso8601_utc};
|
||||
use hive_types::term_msg::{TURN_ICON, TURN_SUMMARY_PREFIX};
|
||||
use hive_sh4re::term_msg::{TURN_ICON, TURN_SUMMARY_PREFIX};
|
||||
|
||||
use crate::events::LiveEvent;
|
||||
|
||||
|
|
|
|||
|
|
@ -351,7 +351,8 @@ pub fn make_session(bus: &Bus) -> Result<AgentSession> {
|
|||
/// against the agent's permissions at the moment it asks.
|
||||
fn acp_permission_policy() -> PermissionPolicy {
|
||||
std::sync::Arc::new(|ask: &PermissionAsk<'_>| {
|
||||
let web = crate::swarm_permissions::current().contains(&hive_types::Permission::WebTools);
|
||||
let web = crate::swarm_permissions::current()
|
||||
.contains(&hive_sh4re::permissions::Permission::WebTools);
|
||||
acp_permits(ask, web)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use hive_types::Permission;
|
||||
use hive_sh4re::permissions::Permission;
|
||||
use swarm_queue_client::agent_permissions::{AgentPermissions, HIVE_COPY_FILE, HiveCopy};
|
||||
|
||||
const FILE: &str = "swarm-agent-permissions.json";
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
//! dashboard's tree view. All of that is gone; what the file is
|
||||
//! *for* now is the one thing that survived the removal — naming every
|
||||
//! agent, which is the set a
|
||||
//! [`hive_types::Permission::ManageRootAgent`] holder gets
|
||||
//! [`hive_sh4re::permissions::Permission::ManageRootAgent`] holder gets
|
||||
//! bind-mounted ([`all_agents`]).
|
||||
//!
|
||||
//! Broader-than-your-own bind-mount grants are **not** stored here: they
|
||||
|
|
@ -65,7 +65,7 @@ fn read_from(path: &Path) -> std::io::Result<BTreeSet<String>> {
|
|||
}
|
||||
|
||||
/// Every agent the roster knows about, in name order. This is the set
|
||||
/// a [`hive_types::Permission::ManageRootAgent`] holder gets
|
||||
/// a [`hive_sh4re::permissions::Permission::ManageRootAgent`] holder gets
|
||||
/// bind-mounted, and it is deliberately unfiltered: that permission means
|
||||
/// "may manage any agent", so the set is all of them.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use std::path::{Path, PathBuf};
|
|||
|
||||
use anyhow::{Context, Result};
|
||||
use hive_priv_sock::{BindMount, CredentialMount};
|
||||
use hive_types::Permission;
|
||||
use hive_sh4re::permissions::Permission;
|
||||
|
||||
use crate::coordinator::{AgentPaths, HiveEnv};
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use std::collections::{BTreeMap, BTreeSet};
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use hive_types::Permission;
|
||||
use hive_sh4re::permissions::Permission;
|
||||
use swarm_queue_client::agent_permissions::{
|
||||
self, AgentPermissions, IMPORT_REPLY_DEADLINE, Import, ImportReply,
|
||||
};
|
||||
|
|
@ -240,7 +240,7 @@ async fn send_at(client: &async_nats::Client, hive: &str, meta: &Path) -> Result
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{FileMap, grants, seed_local_at};
|
||||
use hive_types::Permission::{self, *};
|
||||
use hive_sh4re::permissions::Permission::{self, *};
|
||||
|
||||
fn file(json: &str) -> FileMap {
|
||||
serde_json::from_str(json).expect("decodes")
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use std::sync::Arc;
|
|||
use std::time::Duration;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use hive_types::Permission;
|
||||
use hive_sh4re::permissions::Permission;
|
||||
use swarm_queue_client::agent_permissions::{self, AgentPermissions, Read};
|
||||
|
||||
use crate::coordinator::Coordinator;
|
||||
|
|
@ -216,7 +216,11 @@ mod tests {
|
|||
assert!(changed.is_empty());
|
||||
assert!(crate::permissions::read_at(&path).expect("read").is_empty());
|
||||
assert!(
|
||||
!crate::permissions::holds(&path, "ruth", hive_types::Permission::ManageRootAgent)
|
||||
!crate::permissions::holds(
|
||||
&path,
|
||||
"ruth",
|
||||
hive_sh4re::permissions::Permission::ManageRootAgent
|
||||
)
|
||||
.expect("read")
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,3 +15,4 @@ 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
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
//! server instead); the permission transfers to a subagent, not the
|
||||
//! mechanism.
|
||||
|
||||
use hive_types::Permission;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Built-in claude tools present in every session regardless of permissions.
|
||||
/// Anything not in this list (and not added by a permission's
|
||||
|
|
@ -90,10 +90,131 @@ 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
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
//! `mcp__hyperhive__send`'s row say, which tools get an expandable body)
|
||||
//! didn't change; only the shape it gets packed into did.
|
||||
|
||||
use crate::term_msg::{BodyFormat, ClassifyCtx, Level, TermMsg};
|
||||
use hive_types::term_msg::{SEND_ICON, SEND_SUMMARY_PREFIX};
|
||||
use crate::term_msg::{BodyFormat, ClassifyCtx, Level, SEND_ICON, SEND_SUMMARY_PREFIX, TermMsg};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
/// Classify one raw claude stream-json line into zero or more terminal rows.
|
||||
|
|
@ -1347,16 +1346,13 @@ mod tests {
|
|||
}));
|
||||
assert_eq!(row.icon.as_deref(), Some(SEND_ICON));
|
||||
assert_eq!(row.summary.contains(" · 3L"), lines, "{}", row.summary);
|
||||
assert_eq!(
|
||||
hive_types::term_msg::send_recipient(&row.summary),
|
||||
Some("iris")
|
||||
);
|
||||
assert_eq!(crate::term_msg::send_recipient(&row.summary), Some("iris"));
|
||||
}
|
||||
assert_eq!(hive_types::term_msg::send_recipient("send* →"), None);
|
||||
assert_eq!(hive_types::term_msg::send_recipient("Read(x)"), None);
|
||||
assert_eq!(crate::term_msg::send_recipient("send* →"), None);
|
||||
assert_eq!(crate::term_msg::send_recipient("Read(x)"), None);
|
||||
// A suffix lookalike in the name is not stripped.
|
||||
assert_eq!(
|
||||
hive_types::term_msg::send_recipient("send* → a · xL"),
|
||||
crate::term_msg::send_recipient("send* → a · xL"),
|
||||
Some("a · xL")
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,6 +128,41 @@ pub fn iso8601_utc(unix_seconds: i64) -> String {
|
|||
.to_rfc3339_opts(SecondsFormat::Secs, true)
|
||||
}
|
||||
|
||||
/// Icon of the row an agent's `send` tool call makes.
|
||||
pub const SEND_ICON: &str = "📤";
|
||||
|
||||
/// What a `send` row's summary starts with; the recipient follows. A
|
||||
/// multi-line body appends ` · <N>L`, which [`send_recipient`] drops.
|
||||
pub const SEND_SUMMARY_PREFIX: &str = "send* → ";
|
||||
|
||||
/// Icon of the row a turn started by a message makes.
|
||||
pub const TURN_ICON: &str = "◆";
|
||||
|
||||
/// What a turn-start row's summary starts with; the sender follows.
|
||||
pub const TURN_SUMMARY_PREFIX: &str = "TURN ← ";
|
||||
|
||||
/// The recipient a `send` row's summary names, without the line-count suffix.
|
||||
#[must_use]
|
||||
pub fn send_recipient(summary: &str) -> Option<&str> {
|
||||
let rest = summary.strip_prefix(SEND_SUMMARY_PREFIX)?;
|
||||
Some(match rest.rsplit_once(" · ") {
|
||||
Some((to, lines))
|
||||
if lines
|
||||
.strip_suffix('L')
|
||||
.is_some_and(|n| !n.is_empty() && n.bytes().all(|b| b.is_ascii_digit())) =>
|
||||
{
|
||||
to
|
||||
}
|
||||
_ => rest,
|
||||
})
|
||||
}
|
||||
|
||||
/// The sender a turn-start row's summary names.
|
||||
#[must_use]
|
||||
pub fn turn_sender(summary: &str) -> Option<&str> {
|
||||
summary.strip_prefix(TURN_SUMMARY_PREFIX)
|
||||
}
|
||||
|
||||
/// Per-connection/per-request classification state. A live SSE stream keeps
|
||||
/// one of these alive for the connection's lifetime — `tool_use` id → name
|
||||
/// correlation, so a `tool_result` can tell it's answering a `recv` call and
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ use hive_claude::{Attach, Claude, Config, SessionStore};
|
|||
use hive_runtime::{
|
||||
AcpCommand, AcpRuntime, PermissionAsk, PermissionPolicy, Runtime as _, RuntimeSpec, TurnStop,
|
||||
};
|
||||
use hive_types::Permission;
|
||||
use hive_sh4re::permissions::Permission;
|
||||
use swarm_wire::agent_inference::AgentInference;
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
|||
use std::sync::{Arc, OnceLock};
|
||||
use std::time::Duration;
|
||||
|
||||
use hive_types::Permission;
|
||||
use hive_sh4re::permissions::Permission;
|
||||
use swarm_queue_client::agent_inference::{self, read_own};
|
||||
use swarm_queue_client::agent_permissions::{self, HIVE_COPY_PATH, HiveCopy};
|
||||
use swarm_wire::agent_inference::AgentInference;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue