diff --git a/Cargo.lock b/Cargo.lock index 4e91de7..56cccda 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1802,7 +1802,9 @@ dependencies = [ "serde_json", "tinymemory-api", "tinymemory-conformance", + "tinymemory-remote", "tinymemory-sync", + "tinymemory-tinycortex", "tokio", ] @@ -1819,6 +1821,7 @@ dependencies = [ "serde_json", "sha2 0.11.0", "thiserror 2.0.20", + "tokio", "toml 0.9.12+spec-1.1.0", "uuid", ] @@ -1879,7 +1882,6 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.9", - "tinymemory", "tinymemory-api", "tinymemory-conformance", "tokio", @@ -1906,7 +1908,6 @@ dependencies = [ "serde", "serde_json", "tinycortex", - "tinymemory", "tinymemory-api", "tinymemory-conformance", "tinymemory-core", diff --git a/Cargo.toml b/Cargo.toml index 5b33af5..132779b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,16 @@ exclude = [ # one dependency rather than two, and so `tinymemory::MemoryProvider` and # `tinymemory_api::provider::MemoryProvider` are the same type. tinymemory-api = { path = "api" } +# The engines, each behind its own feature (#18 §D1). Optional, so the default +# build is still the contract, the registry and the mandatory composition and +# nothing that links C. +# +# This direction only became legal once `mandatory` and the reserved driver ids +# moved into `tinymemory-api`: the adapters used to depend on this crate for +# them, and a facade depending back on the adapters is a package cycle cargo +# forbids. +tinymemory-tinycortex = { path = "adapters/tinycortex", optional = true } +tinymemory-remote = { path = "adapters/remote", optional = true } # The mandatory capability families are `async fn`s on object-safe traits. async-trait = "0.1" # `Memory` is anyhow-typed; `mandatory::engine_error` maps it onto `MemoryError`. @@ -85,8 +95,23 @@ tinymemory-conformance = { path = "conformance" } tinymemory-sync = { path = "sync" } [features] +# Nothing by default. A host that names no engine links no engine. default = [] +# --- Engines --------------------------------------------------------------- +# Each pulls exactly the adapter that serves it. `supermemory`, `mem0` and +# `cognee` share one adapter crate, so enabling several costs one dependency +# rather than three. +tinycortex = ["dep:tinymemory-tinycortex"] +supermemory = ["dep:tinymemory-remote"] +mem0 = ["dep:tinymemory-remote"] +cognee = ["dep:tinymemory-remote"] + +# --- Capability add-ons ---------------------------------------------------- +# Each requires the engine that serves it, so asking for a capability cannot +# produce a build where nothing implements it. +memory-git = ["tinycortex", "tinymemory-tinycortex/memory-git"] + # Lints apply to this package only, deliberately. `api/` is contract code moved # verbatim from `tinycortex-api` and is held byte-identical; subjecting it to a # stricter lint set than it was written under would force cosmetic edits through diff --git a/adapters/remote/Cargo.toml b/adapters/remote/Cargo.toml index 3bb4da5..2690528 100644 --- a/adapters/remote/Cargo.toml +++ b/adapters/remote/Cargo.toml @@ -10,7 +10,6 @@ repository = "https://github.com/tinyhumansai/tinymemory" [dependencies] # The engine-neutral contract and mandatory-family composition. -tinymemory = { path = "../.." } tinymemory-api = { path = "../../api" } # Memory is an object-safe async trait and each native HTTP dialect is async. async-trait = "0.1" diff --git a/adapters/remote/src/cognee.rs b/adapters/remote/src/cognee.rs index 6b2a60d..e0bb8fc 100644 --- a/adapters/remote/src/cognee.rs +++ b/adapters/remote/src/cognee.rs @@ -11,7 +11,7 @@ use tinymemory_api::types::MemoryTaint; use crate::common::{stable_id, Dialect, HttpClient, RemoteMemory, StoredEntry}; /// Stable driver id used by configuration and status output. -pub use tinymemory::registry::COGNEE_DRIVER_ID; +pub use tinymemory_api::drivers::COGNEE_DRIVER_ID; /// Default base URL for Cognee's managed API. pub const COGNEE_API_ENDPOINT: &str = "https://api.cognee.ai"; diff --git a/adapters/remote/src/lib.rs b/adapters/remote/src/lib.rs index 1b9d559..70ab990 100644 --- a/adapters/remote/src/lib.rs +++ b/adapters/remote/src/lib.rs @@ -3,7 +3,7 @@ //! The adapters preserve TinyMemory's exact `(namespace, key)` upsert contract //! in backend metadata while delegating semantic recall to each engine's native //! search API. They advertise Core, Recall, and Portability through -//! [`tinymemory::mandatory::MemoryTraitProvider`]. +//! [`tinymemory_api::mandatory::MemoryTraitProvider`]. //! //! Credentials are accepted only at construction and are never exposed by //! `Debug` implementations or error messages. @@ -19,7 +19,7 @@ pub use supermemory::{SupermemoryMemory, SUPERMEMORY_API_ENDPOINT, SUPERMEMORY_D use std::sync::Arc; -use tinymemory::mandatory::MemoryTraitProvider; +use tinymemory_api::mandatory::MemoryTraitProvider; /// Wrap a Supermemory HTTP backend as a bound TinyMemory provider. #[must_use] diff --git a/adapters/remote/src/mem0.rs b/adapters/remote/src/mem0.rs index 53221d9..d92546d 100644 --- a/adapters/remote/src/mem0.rs +++ b/adapters/remote/src/mem0.rs @@ -11,7 +11,7 @@ use tinymemory_api::types::MemoryTaint; use crate::common::{category, Dialect, HttpClient, RemoteMemory, StoredEntry}; /// Stable driver id used by configuration and status output. -pub use tinymemory::registry::MEM0_DRIVER_ID; +pub use tinymemory_api::drivers::MEM0_DRIVER_ID; /// A self-hosted Mem0 server exposed through TinyMemory's storage contract. #[derive(Debug)] diff --git a/adapters/remote/src/supermemory.rs b/adapters/remote/src/supermemory.rs index d69c599..d1c70d0 100644 --- a/adapters/remote/src/supermemory.rs +++ b/adapters/remote/src/supermemory.rs @@ -10,7 +10,7 @@ use tinymemory_api::types::MemoryTaint; use crate::common::{category, stable_id, Dialect, HttpClient, RemoteMemory, StoredEntry}; /// Stable driver id used by configuration and status output. -pub use tinymemory::registry::SUPERMEMORY_DRIVER_ID; +pub use tinymemory_api::drivers::SUPERMEMORY_DRIVER_ID; /// Default base URL for Supermemory's managed API. pub const SUPERMEMORY_API_ENDPOINT: &str = "https://api.supermemory.ai"; diff --git a/adapters/tinycortex/Cargo.toml b/adapters/tinycortex/Cargo.toml index 73ba1e7..ce0b7c3 100644 --- a/adapters/tinycortex/Cargo.toml +++ b/adapters/tinycortex/Cargo.toml @@ -13,7 +13,6 @@ repository = "https://github.com/tinyhumansai/tinymemory" [dependencies] # The contract this adapter targets. -tinymemory = { path = "../.." } tinymemory-api = { path = "../../api" } # The engine being adapted. A version requirement rather than a path, so a host # that already pins its own TinyCortex checkout unifies both onto one copy diff --git a/adapters/tinycortex/src/engine/mod.rs b/adapters/tinycortex/src/engine/mod.rs index ebfe0eb..a9a2c25 100644 --- a/adapters/tinycortex/src/engine/mod.rs +++ b/adapters/tinycortex/src/engine/mod.rs @@ -21,7 +21,6 @@ use std::sync::Arc; use crate::TinycortexMemory; use async_trait::async_trait; use chrono::Utc; -use tinymemory::mandatory::MemoryTraitProvider; use tinymemory_api::capabilities::Capabilities; use tinymemory_api::chunks::Chunk; use tinymemory_api::error::MemoryError; @@ -31,6 +30,7 @@ use tinymemory_api::host::{ CloudProviderCreds, ComposioMode, LocalAiConfig, MemoryConfig, MemoryHostConfig, MemoryTreeConfig, SchedulerGateConfig, }; +use tinymemory_api::mandatory::MemoryTraitProvider; use tinymemory_api::provider::types::{ EntityHit, EntityRef, ExportPage, ExportRecord, ImportOutcome, IngestItem, IngestOutcome, MaintenanceReport, SourceItem, SourceScope, diff --git a/adapters/tinycortex/src/lib.rs b/adapters/tinycortex/src/lib.rs index fba0063..57ae0bf 100644 --- a/adapters/tinycortex/src/lib.rs +++ b/adapters/tinycortex/src/lib.rs @@ -55,14 +55,14 @@ pub use memory::TinycortexMemory; use std::sync::Arc; -use tinymemory::mandatory::MemoryTraitProvider; +use tinymemory_api::mandatory::MemoryTraitProvider; /// The driver id this adapter binds under. /// -/// Matches [`tinymemory::registry::TINYCORTEX_DRIVER_ID`], which is where +/// Matches [`tinymemory_api::drivers::TINYCORTEX_DRIVER_ID`], which is where /// admission reserves it — the constant lives there so a host that compiles /// this adapter out still refuses to bind something else under the name. -pub use tinymemory::registry::TINYCORTEX_DRIVER_ID; +pub use tinymemory_api::drivers::TINYCORTEX_DRIVER_ID; /// Wrap a TinyCortex backend as a bound memory driver. /// diff --git a/api/Cargo.toml b/api/Cargo.toml index 3d5f32d..8031c85 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -61,6 +61,10 @@ uuid = { version = "1", features = ["v4"] } # The moved `host::` config sections are parsed from TOML in their own tests, # exactly as the host parses them from `config.toml`. toml = "0.9" +# The mandatory-composition tests are async — they drive a `MemoryProvider`. +# Dev-only, so it does not touch the forbidden-dependency rule this crate's +# manifest enforces for its normal graph (#18 §D4). +tokio = { version = "1", features = ["macros", "rt"] } [features] default = [] diff --git a/api/src/drivers.rs b/api/src/drivers.rs new file mode 100644 index 0000000..e849e3a --- /dev/null +++ b/api/src/drivers.rs @@ -0,0 +1,44 @@ +//! The reserved driver ids. +//! +//! These name the engines this workspace ships. They live in the contract crate +//! rather than in the facade's registry for the same reason +//! [`crate::null::NULL_DRIVER_ID`] already did: an adapter has to spell the id +//! it binds under, and reaching into the facade for it made every adapter +//! depend on the facade — which in turn made the facade unable to depend on the +//! adapters, a package cycle cargo forbids. That cycle is what blocked #18 +//! §D1's per-engine features. +//! +//! Reserving them here does **not** mean the contract knows about these +//! engines. It knows their *names*, so that admission can refuse something else +//! binding under one — a host that compiles an adapter out must still reject an +//! impostor claiming its id. The class each id is admitted under stays in the +//! facade's registry, where the trust decision belongs. +//! +//! `tinymemory::registry` re-exports all of these, so existing paths resolve +//! unchanged. + +/// The driver id of the bundled TinyCortex embedded engine. +pub const TINYCORTEX_DRIVER_ID: &str = "tinycortex"; + +/// The driver id of `tinymemory-core`'s own in-process store. +/// +/// Distinct from [`TINYCORTEX_DRIVER_ID`], and the distinction is the point: +/// that one names the bundled TinyCortex engine, this one names +/// `tinymemory_core::store::UnifiedMemory` — a separate SQLite store this +/// workspace implements itself. Both are `Embedded`; they are not the same +/// engine, and a host that binds one has not bound the other. +/// +/// Named for what `create_memory` has always called this backend +/// (`effective_memory_backend_name` returns `"namespace"`), so the id an +/// operator sees in status matches the name already in the logs rather than +/// introducing a third vocabulary for one store. +pub const NAMESPACE_DRIVER_ID: &str = "namespace"; + +/// Driver id of the native Supermemory HTTP adapter. +pub const SUPERMEMORY_DRIVER_ID: &str = "supermemory"; + +/// Driver id of the native Mem0 HTTP adapter. +pub const MEM0_DRIVER_ID: &str = "mem0"; + +/// Driver id of the native Cognee HTTP adapter. +pub const COGNEE_DRIVER_ID: &str = "cognee"; diff --git a/api/src/lib.rs b/api/src/lib.rs index f34647a..bf237aa 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -66,10 +66,22 @@ pub mod capabilities; pub mod chunks; +pub mod drivers; pub mod error; pub mod goals; pub mod health; pub mod host; +/// The mandatory-family composition: wrap any [`traits::Memory`] backend as a +/// complete [`provider::MemoryProvider`]. +/// +/// Lives here rather than in the `tinymemory` facade because every adapter +/// needs it, and an adapter that reached for it in the facade made the facade +/// unable to depend on adapters in turn — a package cycle cargo forbids, and +/// the reason #18 §D1's engine features could not be declared. It costs this +/// crate nothing: the module names only `async_trait`, `std`, and this crate's +/// own contract types. The facade re-exports it, so `tinymemory::mandatory` +/// keeps resolving. +pub mod mandatory; pub mod null; pub mod provider; pub mod recall; diff --git a/src/mandatory/mod.rs b/api/src/mandatory/mod.rs similarity index 88% rename from src/mandatory/mod.rs rename to api/src/mandatory/mod.rs index 1e4fa0a..578fc9d 100644 --- a/src/mandatory/mod.rs +++ b/api/src/mandatory/mod.rs @@ -1,34 +1,34 @@ //! The three mandatory capability families, composed over the storage trait. //! -//! [`MemoryCore`](tinymemory_api::provider::MemoryCore), [`MemoryRecall`](tinymemory_api::provider::MemoryRecall) and [`MemoryPortability`](tinymemory_api::provider::MemoryPortability) are supertraits of -//! [`MemoryProvider`](tinymemory_api::provider::MemoryProvider): a driver +//! [`MemoryCore`](crate::provider::MemoryCore), [`MemoryRecall`](crate::provider::MemoryRecall) and [`MemoryPortability`](crate::provider::MemoryPortability) are supertraits of +//! [`MemoryProvider`](crate::provider::MemoryProvider): a driver //! missing any of them cannot be constructed at all. For a backend that already -//! implements [`Memory`], almost all three are mechanical — and the parts that +//! implements [`Memory`](crate::traits::Memory), almost all three are mechanical — and the parts that //! are *not* mechanical are the parts every such backend gets wrong the same //! way. So they live here once rather than in each driver. //! //! ## The four things that are not a straight delegation //! -//! 1. **`store` maps onto [`Memory::store_with_taint`], never [`Memory::store`].** -//! The contract's `store` always carries a [`MemoryTaint`](tinymemory_api::types::MemoryTaint), because +//! 1. **`store` maps onto [`Memory::store_with_taint`](crate::traits::Memory::store_with_taint), never [`Memory::store`](crate::traits::Memory::store).** +//! The contract's `store` always carries a [`MemoryTaint`](crate::types::MemoryTaint), because //! provenance is stamped by the host's policy layer *before* the call. -//! [`Memory::store`] hard-codes [`MemoryTaint::Internal`](tinymemory_api::types::MemoryTaint::Internal), so routing through +//! [`Memory::store`](crate::traits::Memory::store) hard-codes [`MemoryTaint::Internal`](crate::types::MemoryTaint::Internal), so routing through //! it would launder externally-sourced content into internal-trust content — //! the one failure mode a provenance guard exists to prevent. Note -//! [`Memory::store_with_taint`]'s *trait default* also silently drops the +//! [`Memory::store_with_taint`](crate::traits::Memory::store_with_taint)'s *trait default* also silently drops the //! taint, so a backend that does not override it is unsafe here; that is a //! backend bug, not something this layer can paper over. //! //! 2. **`list(None, ..)` spans every namespace.** The contract says an -//! all-`None` list returns everything the driver holds. A typical [`Memory`] +//! all-`None` list returns everything the driver holds. A typical [`Memory`](crate::traits::Memory) //! implementation normalises a `None` namespace to -//! [`GLOBAL_NAMESPACE`], so a naive delegation returns one namespace and -//! calls it "everything". [`list_everything`] composes `namespace_summaries` +//! [`GLOBAL_NAMESPACE`](crate::types::GLOBAL_NAMESPACE), so a naive delegation returns one namespace and +//! calls it "everything". [`list_everything`](crate::mandatory::list_everything) composes `namespace_summaries` //! with a per-namespace `list` instead. //! //! 3. **A scoped recall is refused, not ignored.** See [`recall`]. //! -//! 4. **Import must not re-stamp provenance.** See [`import_records`]. +//! 4. **Import must not re-stamp provenance.** See [`import_records`](crate::mandatory::import_records). //! //! ## Why free functions rather than a blanket impl //! @@ -36,14 +36,14 @@ //! driver that wants to override one method, and would force every driver to //! resolve its handle through one shape. These are plain functions taking //! `&dyn Memory`, so a driver delegates the parts it wants and keeps its own -//! logging, laziness, and error context. [`MemoryTraitProvider`] is the +//! logging, laziness, and error context. [`MemoryTraitProvider`](crate::mandatory::MemoryTraitProvider) is the //! batteries-included alternative for a backend that wants all three whole. -use tinymemory_api::error::MemoryError; -use tinymemory_api::provider::types::{ExportPage, ExportRecord, ImportOutcome, SourceScope}; -use tinymemory_api::recall::OwnedRecallOpts; -use tinymemory_api::traits::Memory; -use tinymemory_api::types::{MemoryCategory, MemoryEntry, RecallOpts, GLOBAL_NAMESPACE}; +use crate::error::MemoryError; +use crate::provider::types::{ExportPage, ExportRecord, ImportOutcome, SourceScope}; +use crate::recall::OwnedRecallOpts; +use crate::traits::Memory; +use crate::types::{MemoryCategory, MemoryEntry, RecallOpts, GLOBAL_NAMESPACE}; mod provider; @@ -168,7 +168,7 @@ fn parse_cursor(cursor: Option<&str>) -> Result<(usize, usize), MemoryError> { /// Renders one entry as an export record. /// -/// A record round-trips the five fields [`MemoryCore`](tinymemory_api::provider::MemoryCore) +/// A record round-trips the five fields [`MemoryCore`](crate::provider::MemoryCore) /// owns — `key`, `content`, `category`, `session_id`, `taint` — plus its /// namespace and timestamp. Document-tier attributes (`title`, `tags`, /// `metadata`, `source_type`, `priority`) belong to the `Documents` family and @@ -332,10 +332,10 @@ pub async fn export_page( /// `MemoryPortability::import_records` over a [`Memory`] backend. /// -/// Each record is stored with its **own** [`MemoryTaint`](tinymemory_api::types::MemoryTaint) via +/// Each record is stored with its **own** [`MemoryTaint`](crate::types::MemoryTaint) via /// [`Memory::store_with_taint`]: an importing driver must persist the /// provenance it is given and must not re-stamp it. [`Memory::store`] would -/// stamp [`MemoryTaint::Internal`](tinymemory_api::types::MemoryTaint::Internal), quietly upgrading the trust of every +/// stamp [`MemoryTaint::Internal`](crate::types::MemoryTaint::Internal), quietly upgrading the trust of every /// externally-sourced record in a restore. /// /// Per-record rejection is reported in [`ImportOutcome`], never fatal: a diff --git a/src/mandatory/provider.rs b/api/src/mandatory/provider.rs similarity index 87% rename from src/mandatory/provider.rs rename to api/src/mandatory/provider.rs index 4964bb3..5eee27f 100644 --- a/src/mandatory/provider.rs +++ b/api/src/mandatory/provider.rs @@ -1,12 +1,12 @@ -//! [`MemoryTraitProvider`] — a complete, mandatory-only -//! [`MemoryProvider`](tinymemory_api::provider::MemoryProvider) over any -//! [`Memory`] backend. +//! [`MemoryTraitProvider`](crate::mandatory::MemoryTraitProvider) — a complete, mandatory-only +//! [`MemoryProvider`](crate::provider::MemoryProvider) over any +//! [`Memory`](crate::traits::Memory) backend. //! //! ## What this is for //! //! Two things, and it is worth being clear which is which. //! -//! **A real driver for a simple backend.** A store that implements [`Memory`] +//! **A real driver for a simple backend.** A store that implements [`Memory`](crate::traits::Memory) //! becomes a bindable memory driver by wrapping it here — no capability //! plumbing, no export format to invent. It advertises exactly the three //! mandatory families, so a host binding it gets a memory subsystem whose @@ -21,7 +21,7 @@ //! No optional families. Every `as_*` accessor keeps the contract's `None` //! default, and [`capabilities`](MemoryTraitProvider::capabilities) reports the //! mandatory three — so the two halves agree and -//! [`audit_provider`](tinymemory_api::provider::audit_provider) passes. A driver +//! [`audit_provider`](crate::provider::audit_provider) passes. A driver //! that wants documents, trees, or a diff ledger implements those families over //! its own engine and delegates only the mandatory three here. //! @@ -30,19 +30,19 @@ use std::sync::Arc; +use crate::capabilities::{Capabilities, Capability}; +use crate::error::MemoryError; +use crate::health::MemoryHealth; +use crate::provider::types::{ExportPage, ExportRecord, ImportOutcome, SourceScope}; +use crate::provider::{MemoryCore, MemoryPortability, MemoryProvider, MemoryRecall}; +use crate::recall::OwnedRecallOpts; +use crate::traits::Memory; +use crate::types::{MemoryCategory, MemoryEntry, MemoryTaint, NamespaceSummary}; use async_trait::async_trait; -use tinymemory_api::capabilities::{Capabilities, Capability}; -use tinymemory_api::error::MemoryError; -use tinymemory_api::health::MemoryHealth; -use tinymemory_api::provider::types::{ExportPage, ExportRecord, ImportOutcome, SourceScope}; -use tinymemory_api::provider::{MemoryCore, MemoryPortability, MemoryProvider, MemoryRecall}; -use tinymemory_api::recall::OwnedRecallOpts; -use tinymemory_api::traits::Memory; -use tinymemory_api::types::{MemoryCategory, MemoryEntry, MemoryTaint, NamespaceSummary}; use super::{engine_error, export_page, import_records, list_everything, recall}; -/// A mandatory-only memory driver over an [`Memory`] backend. +/// A mandatory-only memory driver over an [`Memory`](crate::traits::Memory) backend. #[derive(Clone)] pub struct MemoryTraitProvider { memory: Arc, diff --git a/src/mandatory/test.rs b/api/src/mandatory/test.rs similarity index 97% rename from src/mandatory/test.rs rename to api/src/mandatory/test.rs index 704f535..1f7e818 100644 --- a/src/mandatory/test.rs +++ b/api/src/mandatory/test.rs @@ -1,6 +1,6 @@ //! Tests for the shared mandatory-family logic. //! -//! These run against [`VecMemory`], a deliberately dumb in-process [`Memory`] +//! These run against [`VecMemory`], a deliberately dumb in-process [`Memory`](crate::traits::Memory) //! backend defined here rather than borrowed from an engine crate: the point of //! this module is that the logic is engine-neutral, and a test that needed a //! real engine would not demonstrate that. @@ -12,7 +12,7 @@ use std::collections::BTreeMap; use std::sync::Mutex; -use tinymemory_api::provider::audit_provider; +use crate::provider::audit_provider; use super::*; @@ -46,8 +46,8 @@ impl VecMemory { use std::sync::Arc; +use crate::types::NamespaceSummary; use async_trait::async_trait; -use tinymemory_api::types::NamespaceSummary; #[async_trait] impl Memory for VecMemory { @@ -65,7 +65,7 @@ impl Memory for VecMemory { content, category, session_id, - tinymemory_api::types::MemoryTaint::Internal, + crate::types::MemoryTaint::Internal, ) .await } @@ -77,7 +77,7 @@ impl Memory for VecMemory { content: &str, category: MemoryCategory, session_id: Option<&str>, - taint: tinymemory_api::types::MemoryTaint, + taint: crate::types::MemoryTaint, ) -> anyhow::Result<()> { let entry = MemoryEntry { id: format!("{namespace}/{key}"), @@ -561,7 +561,7 @@ fn debug_renders_the_driver_id_and_not_the_backend() { assert!(!rendered.contains("VecMemory")); } -use tinymemory_api::capabilities::Capability; -use tinymemory_api::health::MemoryHealth; -use tinymemory_api::provider::{MemoryCore, MemoryPortability, MemoryProvider, MemoryRecall}; -use tinymemory_api::types::MemoryTaint; +use crate::capabilities::Capability; +use crate::health::MemoryHealth; +use crate::provider::{MemoryCore, MemoryPortability, MemoryProvider, MemoryRecall}; +use crate::types::MemoryTaint; diff --git a/core/Cargo.toml b/core/Cargo.toml index 4526c3d..25d9b17 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -21,7 +21,6 @@ tinymemory-api = { path = "../api" } # different engine could not have Composio sync despite none of this code # caring which engine is bound. tinymemory-sync = { path = "../sync" } -tinymemory = { path = ".." } # The default embedded engine. `store/`, `tree/` and `sync/` drive it directly; # `tinycortex-api` is a direct dependency because `tinycortex::memory` aliases @@ -78,6 +77,13 @@ tinymemory-conformance = { path = "../conformance" } # which put a web framework in the normal graph of every build linking this # crate (#18 §D2). axum = { version = "0.8", default-features = false, features = ["http1", "json", "tokio", "query", "ws", "macros"] } +# The facade, for `registry::DriverRegistry` in the admission test — the registry +# is where the trust decision lives and it stays in the facade, so a test that +# exercises admission has to name it. A **dev**-dependency: cargo permits a cycle +# through dev-deps, and this must not become a normal one, which is exactly what +# #18 §D1 removed (a facade that depends on adapters cannot have adapters that +# depend, through core, back on it). +tinymemory = { path = ".." } tempfile = "3" tokio = { version = "1", features = ["test-util"] } diff --git a/core/src/store/factories.rs b/core/src/store/factories.rs index 0e22dee..e44e658 100644 --- a/core/src/store/factories.rs +++ b/core/src/store/factories.rs @@ -389,7 +389,7 @@ pub fn create_memory( /// /// [`MemoryProvider`]: tinymemory_api::provider::MemoryProvider /// [`Memory`]: tinymemory_api::traits::Memory -/// [`MemoryTraitProvider`]: tinymemory::mandatory::MemoryTraitProvider +/// [`MemoryTraitProvider`]: tinymemory_api::mandatory::MemoryTraitProvider pub fn create_memory_provider( config: &MemoryConfig, workspace_dir: &Path, @@ -407,14 +407,14 @@ pub fn create_memory_provider( /// bypass allowlists exist to refuse, so the seam that avoids it belongs here /// rather than at each call site. /// -/// [`NAMESPACE_DRIVER_ID`]: tinymemory::registry::NAMESPACE_DRIVER_ID +/// [`NAMESPACE_DRIVER_ID`]: tinymemory_api::drivers::NAMESPACE_DRIVER_ID #[must_use] pub fn bind_as_provider( memory: Box, ) -> Arc { - Arc::new(tinymemory::mandatory::MemoryTraitProvider::new( + Arc::new(tinymemory_api::mandatory::MemoryTraitProvider::new( Arc::from(memory), - tinymemory::registry::NAMESPACE_DRIVER_ID, + tinymemory_api::drivers::NAMESPACE_DRIVER_ID, )) } diff --git a/core/src/store/factories_provider_test.rs b/core/src/store/factories_provider_test.rs index 04c84be..0815733 100644 --- a/core/src/store/factories_provider_test.rs +++ b/core/src/store/factories_provider_test.rs @@ -58,7 +58,7 @@ async fn it_binds_under_the_reserved_namespace_id() { let dir = tempfile::tempdir().expect("temp dir"); assert_eq!( provider(dir.path()).driver_id(), - tinymemory::registry::NAMESPACE_DRIVER_ID, + tinymemory_api::drivers::NAMESPACE_DRIVER_ID, "the bundled store must not bind under another engine's id" ); } diff --git a/deny.toml b/deny.toml index 049b6f9..0aa3ac0 100644 --- a/deny.toml +++ b/deny.toml @@ -18,6 +18,20 @@ allow = [ "Apache-2.0 WITH LLVM-exception", "BSD-2-Clause", "BSD-3-Clause", + # `webpki-roots` — Mozilla's CA root bundle, reached through + # `reqwest`'s rustls-tls, which the hosted-engine adapter needs for HTTPS. + # + # A *data* licence rather than a code one, which is why it is not on the + # usual list: the crate is Mozilla's trust store rendered as a Rust array, + # not software. CDLA-Permissive-2.0 places no conditions on use or + # redistribution of that data and adds no obligations to the binaries that + # embed it. + # + # It reached this graph when #18 §D1 gave the facade per-engine features: + # `[graph] all-features = true` above now enables `supermemory`/`mem0`/ + # `cognee`, so the remote adapter's TLS stack is evaluated where before + # nothing on the facade pulled it. + "CDLA-Permissive-2.0", "GPL-3.0-only", "ISC", "MIT", diff --git a/src/lib.rs b/src/lib.rs index 646626a..1b829b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,7 +63,31 @@ //! let capabilities = provider.capabilities(); //! ``` -pub mod mandatory; +/// The mandatory-family composition, re-exported from the contract crate. +/// +/// The module itself moved to `tinymemory-api` so the adapters can reach it +/// without depending on this crate — which is what lets this crate depend on +/// *them* and declare the per-engine features (#18 §D1). Re-exported rather +/// than relocated silently: `tinymemory::mandatory::MemoryTraitProvider` is a +/// path downstream code already uses. +pub use tinymemory_api::mandatory; + +/// The bundled TinyCortex embedded engine, when the `tinycortex` feature is on. +/// +/// Re-exported so a host selects an engine by feature rather than by taking a +/// second dependency: `tinymemory = { features = ["tinycortex"] }` is the whole +/// wiring, and `tinymemory::tinycortex::provider(backend)` binds it (#18 §D1). +#[cfg(feature = "tinycortex")] +pub use tinymemory_tinycortex as tinycortex; + +/// The hosted HTTP engines, when any of `supermemory`, `mem0` or `cognee` is on. +/// +/// One module for all three because they share one adapter crate — enabling +/// two of them costs one dependency, not two. The per-engine features still +/// exist so a host states which it actually uses, and so a future split can +/// happen without changing how hosts ask for them. +#[cfg(any(feature = "supermemory", feature = "mem0", feature = "cognee"))] +pub use tinymemory_remote as remote; pub mod registry; // The contract, re-exported wholesale. Listed module by module rather than as a diff --git a/src/registry/mod.rs b/src/registry/mod.rs index c31cad2..ba1f0dc 100644 --- a/src/registry/mod.rs +++ b/src/registry/mod.rs @@ -54,38 +54,23 @@ mod test; /// contract so hosts and adapters agree on the spelling. pub use tinymemory_api::null::NULL_DRIVER_ID; -/// The driver id of the bundled TinyCortex embedded engine. +/// The reserved driver ids, re-exported from the contract crate. /// -/// Lives here rather than in the adapter crate so admission can reserve the id -/// without depending on the adapter — a host that compiles the adapter out must -/// still refuse to bind something *else* under this name. -pub const TINYCORTEX_DRIVER_ID: &str = "tinycortex"; - -/// The driver id of `tinymemory-core`'s own in-process store. -/// -/// Distinct from [`TINYCORTEX_DRIVER_ID`], and the distinction is the point: -/// that one names the bundled TinyCortex engine, this one names -/// `tinymemory_core::store::UnifiedMemory` — a separate SQLite store this -/// workspace implements itself and which, until now, no host could reach -/// through [`MemoryProvider`]. Both are `Embedded`; they are not the same -/// engine, and a host that binds one has not bound the other. -/// -/// Named for what `create_memory` has always called this backend -/// (`effective_memory_backend_name` returns `"namespace"`), so the id an -/// operator sees in status matches the name already in the logs rather than -/// introducing a third vocabulary for one store. +/// They moved to [`tinymemory_api::drivers`] so an adapter can spell the id it +/// binds under without depending on this crate — that dependency is what made +/// this crate unable to depend on the adapters in turn, and so blocked #18 +/// §D1's per-engine features. Re-exported here because +/// `tinymemory::registry::TINYCORTEX_DRIVER_ID` is a path both the adapters and +/// downstream hosts already use. /// -/// [`MemoryProvider`]: tinymemory_api::provider::MemoryProvider -pub const NAMESPACE_DRIVER_ID: &str = "namespace"; - -/// Driver id of the native Supermemory HTTP adapter. -pub const SUPERMEMORY_DRIVER_ID: &str = "supermemory"; - -/// Driver id of the native Mem0 HTTP adapter. -pub const MEM0_DRIVER_ID: &str = "mem0"; - -/// Driver id of the native Cognee HTTP adapter. -pub const COGNEE_DRIVER_ID: &str = "cognee"; +/// `NAMESPACE_DRIVER_ID` moved with them rather than staying a `const` here: +/// it is a driver id like the other four, and splitting the set across two +/// crates would mean the next one lands in whichever place its author happened +/// to be reading. +pub use tinymemory_api::drivers::{ + COGNEE_DRIVER_ID, MEM0_DRIVER_ID, NAMESPACE_DRIVER_ID, SUPERMEMORY_DRIVER_ID, + TINYCORTEX_DRIVER_ID, +}; /// The trust state a driver entry must carry for an external class to bind. pub const TRUSTED: &str = "trusted";