1mod database;
4mod discovery;
5mod editing;
6mod locations;
7
8pub use editing::save;
9
10use crate::FileContent;
11use serde::{Deserialize, Serialize};
12use std::path::PathBuf;
13use thiserror::Error;
14
15pub const SOURCE_LIMIT: usize = 1_024;
16
17#[derive(Clone, Debug, Default, Deserialize, Serialize)]
18#[serde(rename_all = "camelCase", deny_unknown_fields)]
19pub struct MemoryRequest {
20 pub folder: Option<PathBuf>,
21 pub source_id: Option<String>,
22 pub examples_root: Option<PathBuf>,
23}
24
25#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
26#[serde(rename_all = "camelCase")]
27pub enum MemoryHarness {
28 Codex,
29 ClaudeCode,
30 Antigravity,
31}
32
33#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
34#[serde(rename_all = "camelCase")]
35pub enum MemoryScope {
36 Global,
37 Folder,
38 OtherFolder,
39}
40
41#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
42#[serde(rename_all = "camelCase")]
43pub enum MemoryKind {
44 Summary,
45 Durable,
46 RawMemory,
47 RolloutSummary,
48 Instructions,
49 Rule,
50 AgentMemory,
51 Skill,
52 Extension,
53 Artifact,
54 StoreStatus,
55 Configuration,
56}
57
58#[derive(Clone, Debug, Serialize)]
59#[serde(rename_all = "camelCase")]
60pub struct MemorySource {
61 pub id: String,
62 pub title: String,
63 pub harness: MemoryHarness,
64 pub scope: MemoryScope,
65 pub kind: MemoryKind,
66 pub location: String,
67 pub group: String,
68 pub format: String,
69 pub modified_at: Option<u64>,
70 pub example: bool,
71 pub association: Option<String>,
72}
73
74#[derive(Clone, Debug, Serialize)]
75#[serde(rename_all = "camelCase")]
76pub struct MemoryCatalog {
77 pub sources: Vec<MemorySource>,
78 pub diagnostics: Vec<String>,
79}
80
81#[derive(Clone, Debug, Serialize)]
82#[serde(rename_all = "camelCase")]
83pub struct MemoryRead {
84 pub source: MemorySource,
85 pub text: Option<String>,
86 pub message: Option<String>,
87 #[serde(skip)]
88 pub file: Option<crate::FilePreview>,
89}
90
91#[derive(Debug, Error)]
92pub enum MemoryError {
93 #[error("Memory discovery requires absolute folder paths.")]
94 InvalidPath,
95 #[error("The source is no longer available. Refresh the source list.")]
96 MissingSource,
97 #[error("Only local Markdown files can be edited from Memories.")]
98 UnsupportedEdit,
99 #[error(transparent)]
100 File(#[from] crate::FileError),
101}
102
103pub fn catalog(request: &MemoryRequest) -> Result<MemoryCatalog, MemoryError> {
108 let (sources, diagnostics) = discovery::discover(request)?;
109 Ok(MemoryCatalog {
110 sources: sources.into_iter().map(|e| e.source).collect(),
111 diagnostics,
112 })
113}
114
115pub fn read(request: &MemoryRequest) -> Result<MemoryRead, MemoryError> {
120 let id = request
121 .source_id
122 .as_ref()
123 .ok_or(MemoryError::MissingSource)?;
124 let (entries, _) = discovery::discover(request)?;
125 let entry = entries
126 .into_iter()
127 .find(|e| &e.source.id == id)
128 .ok_or(MemoryError::MissingSource)?;
129 let mut file = None;
130 let content = match entry.backing {
131 discovery::Backing::File { root, path } => {
132 let preview = crate::files::read_preview(&root, &path);
133 let content = preview.content.clone();
134 file = Some(preview);
135 content
136 }
137 discovery::Backing::Text(text) => FileContent::Text(text),
138 discovery::Backing::Database {
139 root,
140 path,
141 thread,
142 column,
143 } => database::read_record(&root, &path, &thread, &column),
144 };
145 let (text, message) = match content {
146 FileContent::Text(text) => (Some(text), None),
147 FileContent::Binary => (
148 None,
149 Some("This source is binary, rather than UTF-8 text.".into()),
150 ),
151 FileContent::TooLarge => (
152 None,
153 Some("This source exceeds the 2 MiB reading limit.".into()),
154 ),
155 FileContent::Missing => (
156 None,
157 Some("This source was removed. Refresh the list.".into()),
158 ),
159 FileContent::Unsupported => (
160 None,
161 Some("Symbolic links and special files cannot be read.".into()),
162 ),
163 FileContent::Unavailable(message) => (None, Some(message)),
164 };
165 Ok(MemoryRead {
166 source: entry.source,
167 text,
168 message,
169 file,
170 })
171}