areev 0.2.0

Rust SDK for the Areev knowledge database — gRPC and HTTP transports
Documentation
//! `AgentIdentities` resource — register and manage agent DIDs.
//!
//! Mirrors the Python SDK's `client.agent_identities.*` surface.

use serde_json::{json, Map, Value};

use crate::error::Result;
use crate::http::HttpClient;

/// Agent-identity lifecycle.
///
/// Access via [`crate::Areev::agent_identities`]. Requires: admin scope
/// for [`AgentIdentities::register`] and [`AgentIdentities::deactivate`].
pub struct AgentIdentities<'a> {
    http: &'a HttpClient,
    memory_id: String,
}

impl<'a> AgentIdentities<'a> {
    /// Internal constructor — use [`crate::Areev::agent_identities`].
    pub(crate) fn new(http: &'a HttpClient, memory_id: String) -> Self {
        Self { http, memory_id }
    }

    /// Register a new agent identity. Requires: admin scope. Emits an
    /// audit event.
    ///
    /// `display_name` and `scopes` are **required by the live server**
    /// (it deserializes into its `AgentIdentity` struct, whose fields are
    /// `agent_id`, `display_name`, `delegated_by`, `scopes`, …). Each
    /// entry in `scopes` is an `AgentScope` JSON object:
    /// `{"namespaces": [...], "grain_types": [...], "permission": "Read"
    /// | "ReadWrite" | "Admin"}` (empty `namespaces`/`grain_types` mean
    /// "all"). When `scopes` is empty a single read-only all-access scope
    /// is sent. `delegated_by` is optional (the delegating identity's id).
    pub async fn register(
        &self,
        agent_id: &str,
        display_name: &str,
        scopes: &[Value],
        delegated_by: Option<&str>,
    ) -> Result<Value> {
        let mut body = Map::new();
        body.insert("agent_id".into(), Value::String(agent_id.to_string()));
        body.insert(
            "display_name".into(),
            Value::String(display_name.to_string()),
        );
        let scopes = if scopes.is_empty() {
            vec![json!({ "namespaces": [], "grain_types": [], "permission": "Read" })]
        } else {
            scopes.to_vec()
        };
        body.insert("scopes".into(), Value::Array(scopes));
        if let Some(d) = delegated_by {
            body.insert("delegated_by".into(), Value::String(d.to_string()));
        }
        let path = format!("/memories/{}/agents/identities", self.memory_id);
        self.http._post(&path, Some(&Value::Object(body))).await
    }

    /// List registered agent identities.
    pub async fn list(&self) -> Result<Value> {
        let path = format!("/memories/{}/agents/identities", self.memory_id);
        self.http._get(&path, None).await
    }

    /// Get one agent identity by id.
    pub async fn get(&self, agent_id: &str) -> Result<Value> {
        let path = format!(
            "/memories/{}/agents/identities/{}",
            self.memory_id, agent_id
        );
        self.http._get(&path, None).await
    }

    /// Deactivate an agent identity. Requires: admin scope. Emits an
    /// audit event.
    pub async fn deactivate(&self, agent_id: &str, opts: Option<Value>) -> Result<Value> {
        let body = opts.unwrap_or_else(|| Value::Object(Map::new()));
        let path = format!(
            "/memories/{}/agents/identities/{}/deactivate",
            self.memory_id, agent_id
        );
        self.http._post(&path, Some(&body)).await
    }
}