1pub mod brand;
9pub mod brief;
10pub mod briefing;
11pub mod communities;
12pub mod entities;
13pub mod events;
14pub mod import;
15pub mod lint;
16pub mod memory;
17pub mod memory_type;
18pub mod narrative;
19pub mod onboarding;
20pub mod outbox;
21pub mod page_map;
22pub mod pages;
23pub mod repair;
24pub mod repair_plan;
25pub mod requests;
26pub mod responses;
27pub mod sources;
28pub mod space_context;
29pub mod system_info;
30
31pub use brief::*;
33pub use briefing::{BriefingResponse, ContradictionItem};
34pub use communities::{
35 CommunityListResponse, CommunityMember, CommunityMemberCursor, CommunityMembersResponse,
36 CommunityPageAssignment, CommunityPageAssignmentsResponse, CommunityProposalAcceptResponse,
37 CommunityProposalAction, CommunityProposalPayload, CommunityProposalSummary,
38 CommunityReadScope, CommunitySummary, ListCommunityProposalsResponse,
39 PageCommunityAssignmentState,
40};
41pub use entities::{
42 Entity, EntityDetail, EntitySearchResult, GraphMemoryLink, GraphMemoryNode, GraphPageLink,
43 GraphPageNode, GraphRef, GraphRelation, KnowledgeGraphResponse, Observation, Relation,
44 RelationWithEntity,
45};
46pub use lint::{LintCheckResult, LintQuery, LintReport};
47pub use memory::{
48 ActivityBadge, ActivityKind, AgentActivityRow, AgentConnection, DomainInfo,
49 EnrichmentStatusResponse, EnrichmentStepStatus, HomeStats, IndexedFileInfo, MemoryItem,
50 MemoryStats, MemoryVersionItem, PageChange, PageChangeKind, Profile, RecentActivityItem,
51 RejectionRecord, RetrievalEvent, SearchResult, SessionSnapshot, SnapshotCapture,
52 SnapshotCaptureWithContent, Space, TopMemory, TypeBreakdown,
53};
54pub use memory_type::{MEMORY_TYPE_CAPTURE_DESCRIPTION, MEMORY_TYPE_FILTER_DESCRIPTION};
55pub use narrative::NarrativeResponse;
56pub use outbox::{
57 OutboxDrainDetail, OutboxDrainReport, OutboxEnvelope, OutboxPayload, OUTBOX_SCHEMA,
58};
59pub use pages::{Page, PageEvidence};
60pub use repair::*;
61pub use repair_plan::*;
62pub use requests::{
63 AcceptRefinementRequest, CreatePageDraftRequest, PageDraftVersionRequest,
64 UpdatePageDraftRequest,
65};
66pub use responses::{
67 ContradictionDismissResponse, ExportStats, ListMemoryRevisionsResponse,
68 ListPageRevisionsResponse, ListRefinementsResponse, MemoryDetail, MemoryRevisionEntry,
69 OnDeviceModelEntry, OnDeviceModelResponse, OrphanLink, OrphanLinksResponse, PageChangelogEntry,
70 PageDraftResponse, PageWriteResponse, PendingRevision, PendingRevisionItem, ProposalAction,
71 RefinementCardAction, RefinementPayload, RefinementProposalSummary, RejectRefinementResponse,
72 RevisionAcceptResponse, RevisionDismissResponse,
73};
74pub use sources::{MemoryType, RawDocument, SourceType, StabilityTier, SyncStatus};
75pub use space_context::{WriteOutcome, WriteSpaceSource, WriteSpaceTarget};
76
77use serde::{Deserialize, Serialize};
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct ChangelogEntry {
82 pub version: i64,
83 pub at: i64,
85 pub delta: String,
88 #[serde(skip_serializing_if = "Option::is_none")]
89 pub source_agent: Option<String>,
90 #[serde(skip_serializing_if = "Option::is_none")]
92 pub incoming_source_id: Option<String>,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct PageSource {
99 pub page_id: String,
100 pub memory_source_id: String,
101 pub linked_at: i64,
103 #[serde(skip_serializing_if = "Option::is_none")]
104 pub link_reason: Option<String>,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct PageSourceWithMemory {
110 pub source: PageSource,
111 pub memory: Option<crate::memory::MemoryItem>,
112}
113
114pub fn version() -> &'static str {
116 env!("CARGO_PKG_VERSION")
117}
118
119#[cfg(test)]
120#[path = "repair_tests.rs"]
121mod repair_tests;
122
123#[cfg(test)]
124#[path = "repair_plan_tests.rs"]
125mod repair_plan_tests;
126
127#[cfg(test)]
128#[path = "entities_freeze_tests.rs"]
129mod entities_freeze_tests;
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn version_is_set() {
137 assert!(!version().is_empty());
138 }
139
140 #[test]
141 fn memory_type_roundtrip() {
142 for variant in [
143 MemoryType::Identity,
144 MemoryType::Preference,
145 MemoryType::Decision,
146 MemoryType::Lesson,
147 MemoryType::Gotcha,
148 MemoryType::Fact,
149 ] {
150 let s = variant.to_string();
151 let parsed: MemoryType = s.parse().unwrap();
152 assert_eq!(parsed, variant);
153 }
154 }
155
156 #[test]
157 fn search_result_serializes() {
158 let sr = SearchResult {
159 id: "1".into(),
160 content: "test".into(),
161 source: "memory".into(),
162 source_id: "mem_abc".into(),
163 title: "Test".into(),
164 url: None,
165 chunk_index: 0,
166 last_modified: 1000,
167 score: 0.9,
168 chunk_type: None,
169 language: None,
170 semantic_unit: None,
171 memory_type: Some("fact".into()),
172 space: None,
173 source_agent: None,
174 confidence: Some(0.8),
175 confirmed: Some(true),
176 stability: None,
177 supersedes: None,
178 summary: None,
179 entity_id: None,
180 entity_name: None,
181 quality: None,
182 importance: None,
183 event_date: None,
184 is_archived: false,
185 is_recap: false,
186 structured_fields: None,
187 retrieval_cue: None,
188 source_text: None,
189 content_hash: None,
190 raw_score: 0.0,
191 version: 0,
192 pending_revision: false,
193 merged_from: None,
194 last_delta_summary: None,
195 };
196 let json = serde_json::to_string(&sr).unwrap();
197 assert!(json.contains("mem_abc"));
198 assert!(!json.contains("entity_id"));
200 }
201
202 #[test]
203 fn raw_document_default() {
204 let doc = RawDocument::default();
205 assert_eq!(doc.enrichment_status, "raw");
206 assert_eq!(doc.supersede_mode, "hide");
207 assert!(!doc.pending_revision);
208 assert!(!doc.is_recap);
209 }
210
211 #[test]
212 fn stability_tier_mapping() {
213 use sources::stability_tier;
214 assert_eq!(stability_tier(Some("identity")), StabilityTier::Protected);
215 assert_eq!(stability_tier(Some("preference")), StabilityTier::Protected);
216 assert_eq!(stability_tier(Some("fact")), StabilityTier::Standard);
217 assert_eq!(stability_tier(Some("decision")), StabilityTier::Standard);
218 assert_eq!(stability_tier(Some("lesson")), StabilityTier::Standard);
219 assert_eq!(stability_tier(Some("gotcha")), StabilityTier::Standard);
220 assert_eq!(stability_tier(Some("goal")), StabilityTier::Protected);
223 assert_eq!(stability_tier(None), StabilityTier::Ephemeral);
224 }
225}
226
227#[cfg(test)]
228mod retrieval_event_tests {
229 use super::*;
230
231 #[test]
232 fn retrieval_event_roundtrips() {
233 let e = RetrievalEvent {
234 timestamp_ms: 1_700_000_000_000,
235 agent_name: "claude-code".into(),
236 query: Some("origin positioning".into()),
237 page_titles: vec!["Wenlan positioning".into(), "Daemon architecture".into()],
238 page_ids: vec![],
239 memory_snippets: vec![],
240 };
241 let s = serde_json::to_string(&e).unwrap();
242 let back: RetrievalEvent = serde_json::from_str(&s).unwrap();
243 assert_eq!(back.agent_name, "claude-code");
244 assert_eq!(back.page_titles.len(), 2);
245 assert_eq!(back.query.as_deref(), Some("origin positioning"));
246 }
247
248 #[test]
249 fn retrieval_event_omits_none_query() {
250 let e = RetrievalEvent {
251 timestamp_ms: 1_700_000_000_000,
252 agent_name: "claude-code".into(),
253 query: None,
254 page_titles: vec![],
255 page_ids: vec![],
256 memory_snippets: vec![],
257 };
258 let s = serde_json::to_string(&e).unwrap();
259 assert!(
260 !s.contains("\"query\""),
261 "expected None query to be skipped on the wire, got: {s}",
262 );
263 let back: RetrievalEvent = serde_json::from_str(&s).unwrap();
264 assert_eq!(back.query, None);
265 assert!(back.page_titles.is_empty());
266 }
267
268 #[test]
269 fn page_change_roundtrips() {
270 let c = PageChange {
271 page_id: "page_abc".into(),
272 title: "Wiki-style prose pages".into(),
273 change_kind: PageChangeKind::Revised,
274 changed_at_ms: 1_700_000_000_000,
275 };
276 let s = serde_json::to_string(&c).unwrap();
277 assert!(
278 s.contains("\"change_kind\":\"revised\""),
279 "expected snake_case change_kind on the wire, got: {s}",
280 );
281 let back: PageChange = serde_json::from_str(&s).unwrap();
282 assert_eq!(back.change_kind, PageChangeKind::Revised);
283 }
284}