//! Shared core-level schemas and contracts used across adapters (RPC, CLI, etc.). //! //! This module defines the foundational types for OpenHuman's controller system, //! which provides a transport-agnostic way to define and invoke domain logic. //! It also exports submodules for CLI handling, event bus, and RPC server. use serde::Serialize; pub mod agent_cli; pub mod all; pub mod auth; pub mod autocomplete_cli_adapter; pub mod cli; pub mod dispatch; pub mod event_bind_tokens; pub mod event_bus; pub mod jsonrpc; pub mod legacy_aliases; pub mod logging; pub mod memory_cli; pub mod observability; pub mod rpc_log; pub mod runtime; pub mod shutdown; pub mod socketio; pub mod types; /// Canonical function contract for domain controllers. /// /// This shape is transport-agnostic and can be consumed by RPC and CLI layers /// in different ways. It defines the identity, purpose, and I/O signature /// of a specific piece of domain logic. #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub struct ControllerSchema { /// Domain/group identifier, e.g. `memory`, `config`, `credentials`. /// This forms the first part of the RPC method name. pub namespace: &'static str, /// Function identifier inside namespace, e.g. `doc_put`. /// This forms the second part of the RPC method name. pub function: &'static str, /// One-line human-readable purpose, used for CLI help and API documentation. pub description: &'static str, /// Ordered input parameters accepted by the controller function. /// Each input is a field with a name, type, and description. pub inputs: Vec, /// Ordered output fields returned by the controller function. /// This defines the structure of the successful response. pub outputs: Vec, } impl ControllerSchema { /// Canonical dotted name for routing, e.g. `memory.doc_put`. /// This is used internally to identify the controller. pub fn method_name(&self) -> String { format!("{}.{}", self.namespace, self.function) } } /// Schema for one input/output field. /// /// Defines the properties of a single parameter or return value, /// enabling validation and documentation generation. #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub struct FieldSchema { /// Field name. Used as the key in JSON objects or as a CLI flag. pub name: &'static str, /// Field type, defining the expected data shape and enabling validation. pub ty: TypeSchema, /// Human-readable description for docs/help. Should explain what the field is for. pub comment: &'static str, /// Requiredness for adapters: /// - input: if true, the argument/flag MUST be provided. /// - output: if true, the field is guaranteed to be present in the response. pub required: bool, } /// Type-system shape used by controller input/output schema fields. /// /// This enum represents the set of supported types that can be passed /// across the controller boundary. #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub enum TypeSchema { /// A boolean value (true/false). Bool, /// A 64-bit signed integer. I64, /// A 64-bit unsigned integer. U64, /// A 64-bit floating point number. F64, /// A UTF-8 encoded string. String, /// A generic JSON value (serde_json::Value). Json, /// Raw binary data. Bytes, /// An ordered list of values of a specific type. Array(Box), /// String-keyed map/object with homogeneous values. Map(Box), /// An optional value that may be null or a value of the inner type. Option(Box), /// A string that must match one of the predefined variants. Enum { /// The list of allowed string variants. variants: Vec<&'static str>, }, /// A nested object with its own set of fields. Object { /// The fields defining the object's structure. fields: Vec, }, /// Reference to a named shared/domain type defined elsewhere. Ref(&'static str), } #[cfg(test)] mod tests { use super::*; fn mk(namespace: &'static str, function: &'static str) -> ControllerSchema { ControllerSchema { namespace, function, description: "", inputs: vec![], outputs: vec![], } } #[test] fn method_name_joins_namespace_and_function_with_dot() { let s = mk("memory", "doc_put"); assert_eq!(s.method_name(), "memory.doc_put"); } #[test] fn method_name_is_not_an_rpc_method_name() { // The dotted controller key and the `openhuman._` RPC method // name are intentionally different — guard against drift. let s = mk("memory", "doc_put"); assert_eq!(s.method_name(), "memory.doc_put"); assert_eq!( crate::core::all::rpc_method_name(&s), "openhuman.memory_doc_put" ); } #[test] fn method_name_preserves_underscores_in_function() { let s = mk("team", "change_member_role"); assert_eq!(s.method_name(), "team.change_member_role"); } #[test] fn controller_schema_equality_considers_all_fields() { let a = ControllerSchema { namespace: "a", function: "b", description: "x", inputs: vec![], outputs: vec![], }; let b = ControllerSchema { namespace: "a", function: "b", description: "x", inputs: vec![], outputs: vec![], }; let c = ControllerSchema { namespace: "a", function: "b", description: "different", inputs: vec![], outputs: vec![], }; assert_eq!(a, b); assert_ne!(a, c); } #[test] fn type_schema_nesting_is_equality_comparable() { let a = TypeSchema::Array(Box::new(TypeSchema::Option(Box::new(TypeSchema::String)))); let b = TypeSchema::Array(Box::new(TypeSchema::Option(Box::new(TypeSchema::String)))); let c = TypeSchema::Array(Box::new(TypeSchema::Option(Box::new(TypeSchema::I64)))); assert_eq!(a, b); assert_ne!(a, c); } #[test] fn field_schema_required_flag_changes_equality() { let a = FieldSchema { name: "x", ty: TypeSchema::Bool, comment: "", required: true, }; let b = FieldSchema { name: "x", ty: TypeSchema::Bool, comment: "", required: false, }; assert_ne!(a, b); } #[test] fn controller_schema_serializes_to_json() { // Schema must be JSON-serializable: the /schema endpoint depends on it. let s = ControllerSchema { namespace: "health", function: "snapshot", description: "d", inputs: vec![FieldSchema { name: "limit", ty: TypeSchema::U64, comment: "cap", required: false, }], outputs: vec![FieldSchema { name: "ok", ty: TypeSchema::Bool, comment: "", required: true, }], }; let json = serde_json::to_value(&s).unwrap(); assert_eq!(json["namespace"], "health"); assert_eq!(json["function"], "snapshot"); assert_eq!(json["inputs"][0]["name"], "limit"); assert_eq!(json["outputs"][0]["required"], true); } }