Skip to main content

ai_memory/
lib.rs

1// Copyright 2026 AlphaOne LLC
2// SPDX-License-Identifier: Apache-2.0
3
4#![recursion_limit = "256"]
5// The library target was added by the proptest infra (Agent G) to expose
6// production modules to the integration test crate. The bin target's
7// clippy run already gates CI — re-running pedantic against the same
8// modules through the lib target would re-flag the same pre-existing
9// lint backlog the bin target already passes. Allow at the lib level;
10// the bin target is the authoritative gate for production-code linting.
11#![allow(clippy::pedantic, clippy::all)]
12
13// Library interface for ai-memory. Exposes public modules for testing and external use.
14
15pub mod audit;
16pub mod autonomy;
17pub mod bench;
18pub mod cli;
19pub mod color;
20pub mod config;
21pub mod curator;
22pub mod daemon_runtime;
23pub mod db;
24pub mod embeddings;
25pub mod errors;
26pub mod federation;
27pub mod handlers;
28pub mod hnsw;
29pub mod identity;
30pub mod llm;
31pub mod log_paths;
32pub mod logging;
33pub mod mcp;
34pub mod metrics;
35pub mod mine;
36pub mod models;
37pub mod profile;
38pub mod replication;
39pub mod reranker;
40pub mod sizes;
41pub mod subscriptions;
42pub mod tls;
43pub mod toon;
44pub mod validate;
45
46#[cfg(feature = "sal")]
47pub mod migrate;
48
49#[cfg(feature = "sal")]
50pub mod store;
51
52// ---------------------------------------------------------------------------
53// Router construction
54// ---------------------------------------------------------------------------
55//
56// `build_router` is the single source of truth for the daemon's HTTP route
57// table. It is exposed through the lib crate so the integration test suite
58// can construct an in-process `axum::Router` and exercise endpoints via
59// `Router::oneshot()` instead of spawning a subprocess + curl, which:
60//   1. eliminates the OS-level daemon-spawn overhead per test (~200-500ms),
61//   2. exposes the routes' line coverage to `cargo llvm-cov` (subprocess
62//      coverage attribution requires extra `LLVM_PROFILE_FILE` plumbing
63//      that the test harness doesn't provide), and
64//   3. lets test failures surface assertion-level diagnostics instead of
65//      "curl returned 000" black holes.
66//
67// The function takes the same two state values that `serve()` constructs
68// inline (the API key middleware state and the composite app state) so
69// the production binary and the test harness share a single route map.
70pub fn build_router(
71    api_key_state: handlers::ApiKeyState,
72    app_state: handlers::AppState,
73) -> axum::Router {
74    use axum::{
75        extract::DefaultBodyLimit,
76        routing::{delete, get, post, put},
77    };
78    use tower_http::{cors::CorsLayer, trace::TraceLayer};
79
80    axum::Router::new()
81        .route("/api/v1/health", get(handlers::health))
82        // v0.6.0.0: Prometheus scrape endpoint. Exposed at both /metrics
83        // (the community convention) and /api/v1/metrics (consistent with
84        // the rest of the REST surface).
85        .route("/metrics", get(handlers::prometheus_metrics))
86        .route("/api/v1/metrics", get(handlers::prometheus_metrics))
87        .route("/api/v1/memories", get(handlers::list_memories))
88        .route("/api/v1/memories", post(handlers::create_memory))
89        .route("/api/v1/memories/bulk", post(handlers::bulk_create))
90        .route("/api/v1/memories/{id}", get(handlers::get_memory))
91        .route("/api/v1/memories/{id}", put(handlers::update_memory))
92        .route("/api/v1/memories/{id}", delete(handlers::delete_memory))
93        .route(
94            "/api/v1/memories/{id}/promote",
95            post(handlers::promote_memory),
96        )
97        .route("/api/v1/search", get(handlers::search_memories))
98        .route("/api/v1/recall", get(handlers::recall_memories_get))
99        .route("/api/v1/recall", post(handlers::recall_memories_post))
100        .route("/api/v1/forget", post(handlers::forget_memories))
101        .route("/api/v1/consolidate", post(handlers::consolidate_memories))
102        .route(
103            "/api/v1/contradictions",
104            get(handlers::detect_contradictions),
105        )
106        .route("/api/v1/links", post(handlers::create_link))
107        .route("/api/v1/links", delete(handlers::delete_link))
108        .route("/api/v1/links/{id}", get(handlers::get_links))
109        // HTTP parity for MCP-only tools. The `/api/v1/namespaces` surface
110        // serves three verbs: GET lists namespaces OR (when ?namespace=…)
111        // fetches the namespace standard, POST sets a standard, DELETE
112        // clears one. S34/S35 use the query-string form; the path form
113        // (`/api/v1/namespaces/{ns}/standard`) is kept for MCP-tool parity.
114        .route(
115            "/api/v1/namespaces",
116            get(handlers::get_namespace_standard_qs),
117        )
118        .route(
119            "/api/v1/namespaces",
120            post(handlers::set_namespace_standard_qs),
121        )
122        .route(
123            "/api/v1/namespaces",
124            delete(handlers::clear_namespace_standard_qs),
125        )
126        .route(
127            "/api/v1/namespaces/{ns}/standard",
128            post(handlers::set_namespace_standard),
129        )
130        .route(
131            "/api/v1/namespaces/{ns}/standard",
132            get(handlers::get_namespace_standard),
133        )
134        .route(
135            "/api/v1/namespaces/{ns}/standard",
136            delete(handlers::clear_namespace_standard),
137        )
138        // Pillar 1 / Stream A — hierarchical namespace taxonomy.
139        .route("/api/v1/taxonomy", get(handlers::get_taxonomy))
140        // Pillar 2 / Stream D — pre-write near-duplicate check.
141        .route("/api/v1/check_duplicate", post(handlers::check_duplicate))
142        // Pillar 2 / Stream B — entity registry.
143        .route("/api/v1/entities", post(handlers::entity_register))
144        .route(
145            "/api/v1/entities/by_alias",
146            get(handlers::entity_get_by_alias),
147        )
148        // Pillar 2 / Stream C — KG timeline.
149        .route("/api/v1/kg/timeline", get(handlers::kg_timeline))
150        // Pillar 2 / Stream C — KG link supersession.
151        .route("/api/v1/kg/invalidate", post(handlers::kg_invalidate))
152        // Pillar 2 / Stream C — KG outbound traversal.
153        .route("/api/v1/kg/query", post(handlers::kg_query))
154        .route("/api/v1/stats", get(handlers::get_stats))
155        .route("/api/v1/gc", post(handlers::run_gc))
156        .route("/api/v1/export", get(handlers::export_memories))
157        .route("/api/v1/import", post(handlers::import_memories))
158        .route("/api/v1/archive", get(handlers::list_archive))
159        .route("/api/v1/archive", post(handlers::archive_by_ids))
160        .route("/api/v1/archive", delete(handlers::purge_archive))
161        .route(
162            "/api/v1/archive/{id}/restore",
163            post(handlers::restore_archive),
164        )
165        .route("/api/v1/archive/stats", get(handlers::archive_stats))
166        .route("/api/v1/agents", get(handlers::list_agents))
167        .route("/api/v1/agents", post(handlers::register_agent))
168        .route("/api/v1/pending", get(handlers::list_pending))
169        .route(
170            "/api/v1/pending/{id}/approve",
171            post(handlers::approve_pending),
172        )
173        .route(
174            "/api/v1/pending/{id}/reject",
175            post(handlers::reject_pending),
176        )
177        // Phase 3 foundation (issue #224) — peer-to-peer sync endpoints.
178        .route("/api/v1/sync/push", post(handlers::sync_push))
179        .route("/api/v1/sync/since", get(handlers::sync_since))
180        // HTTP parity for MCP-only tools.
181        .route("/api/v1/capabilities", get(handlers::get_capabilities))
182        .route("/api/v1/notify", post(handlers::notify))
183        .route("/api/v1/inbox", get(handlers::get_inbox))
184        .route("/api/v1/subscriptions", post(handlers::subscribe))
185        .route("/api/v1/subscriptions", delete(handlers::unsubscribe))
186        .route("/api/v1/subscriptions", get(handlers::list_subscriptions))
187        .route("/api/v1/session/start", post(handlers::session_start))
188        .layer(axum::middleware::from_fn_with_state(
189            api_key_state,
190            handlers::api_key_auth,
191        ))
192        .layer(TraceLayer::new_for_http())
193        .layer(DefaultBodyLimit::max(2 * 1024 * 1024))
194        .layer(CorsLayer::new())
195        .with_state(app_state)
196}