car-ffi-common 0.28.0

Shared logic for FFI bindings (NAPI, PyO3) — JSON wrappers for verify, multi-agent, scheduler
//! Proxy for the daemon's `discovery.*` namespace (AgentDNS-style service
//! discovery). Thin [`DaemonClient`] calls.

use serde_json::{json, Value};

use crate::proxy::DaemonClient;

fn to_string(v: Value) -> Result<String, String> {
    serde_json::to_string(&v).map_err(|e| e.to_string())
}

/// `discovery.resolve` — resolve a natural-language need into ranked CAR-local
/// services, each named under the `agentdns://organization/category/name`
/// scheme. Returns `{ services: [{ identifier, name, kind, protocol, score,
/// similarity }], count }`. `limit` caps results (clamped to [1, 50]; None =
/// default 5).
pub async fn resolve(
    client: &DaemonClient,
    need: &str,
    limit: Option<u32>,
) -> Result<String, String> {
    let mut params = json!({ "need": need });
    if let Some(l) = limit {
        params["limit"] = json!(l);
    }
    client
        .call("discovery.resolve", params)
        .await
        .and_then(to_string)
}

/// `discovery.route_compose` — plan a decompose/retrieve/compose route over
/// all discoverable services. Returns `{ plan, decomposition, candidates,
/// metadata }`. This plans only; callers invoke returned targets through their
/// existing governed surfaces.
#[allow(clippy::too_many_arguments)]
pub async fn route_compose(
    client: &DaemonClient,
    need: &str,
    max_subtasks: Option<u32>,
    decomposition_mode: Option<&str>,
    sad_hints: Option<u32>,
    sad_iterations: Option<u32>,
    sad_convergence_jaccard: Option<f64>,
    candidates_per_step: Option<u32>,
    rerank: bool,
) -> Result<String, String> {
    let mut params = json!({ "need": need, "rerank": rerank });
    if let Some(v) = max_subtasks {
        params["max_subtasks"] = json!(v);
    }
    if let Some(v) = decomposition_mode {
        params["decomposition_mode"] = json!(v);
    }
    if let Some(v) = sad_hints {
        params["sad_hints"] = json!(v);
    }
    if let Some(v) = sad_iterations {
        params["sad_iterations"] = json!(v);
    }
    if let Some(v) = sad_convergence_jaccard {
        params["sad_convergence_jaccard"] = json!(v);
    }
    if let Some(v) = candidates_per_step {
        params["candidates_per_step"] = json!(v);
    }
    client
        .call("discovery.route_compose", params)
        .await
        .and_then(to_string)
}