Codex Handbook
ext/memories/src/backend.rs 186 lines
use schemars::JsonSchema;use serde::Deserialize;use serde::Serialize;use std::future::Future;/// Storage interface behind the memories tools.////// Implementations should return paths relative to the memory store and enforce/// their own storage-specific access rules. The local implementation uses the/// filesystem today; a later implementation can satisfy the same contract from a/// remote backend.pub trait MemoriesBackend: Clone + Send + Sync + 'static {    fn add_ad_hoc_note(        &self,        request: AddAdHocMemoryNoteRequest,    ) -> impl Future<Output = Result<AddAdHocMemoryNoteResponse, MemoriesBackendError>> + Send;    fn list(        &self,        request: ListMemoriesRequest,    ) -> impl Future<Output = Result<ListMemoriesResponse, MemoriesBackendError>> + Send;    fn read(        &self,        request: ReadMemoryRequest,    ) -> impl Future<Output = Result<ReadMemoryResponse, MemoriesBackendError>> + Send;    fn search(        &self,        request: SearchMemoriesRequest,    ) -> impl Future<Output = Result<SearchMemoriesResponse, MemoriesBackendError>> + Send;}#[derive(Debug, Clone, PartialEq, Eq)]pub struct AddAdHocMemoryNoteRequest {    pub filename: String,    pub note: String,}#[derive(Debug, Clone, PartialEq, Eq, Serialize, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct AddAdHocMemoryNoteResponse {}#[derive(Debug, Clone, PartialEq, Eq)]pub struct ListMemoriesRequest {    pub path: Option<String>,    pub cursor: Option<String>,    pub max_results: usize,}#[derive(Debug, Clone, PartialEq, Eq, Serialize, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct ListMemoriesResponse {    pub path: Option<String>,    pub entries: Vec<MemoryEntry>,    pub next_cursor: Option<String>,    pub truncated: bool,}#[derive(Debug, Clone, PartialEq, Eq)]pub struct ReadMemoryRequest {    pub path: String,    pub line_offset: usize,    pub max_lines: Option<usize>,    pub max_tokens: usize,}#[derive(Debug, Clone, PartialEq, Eq, Serialize, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct ReadMemoryResponse {    pub path: String,    pub start_line_number: usize,    pub content: String,    pub truncated: bool,}#[derive(Debug, Clone, PartialEq, Eq)]pub struct SearchMemoriesRequest {    pub queries: Vec<String>,    pub match_mode: SearchMatchMode,    pub path: Option<String>,    pub cursor: Option<String>,    pub context_lines: usize,    pub case_sensitive: bool,    pub normalized: bool,    pub max_results: usize,}#[derive(Debug, Clone, PartialEq, Eq, Serialize, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct SearchMemoriesResponse {    pub queries: Vec<String>,    pub match_mode: SearchMatchMode,    pub path: Option<String>,    pub matches: Vec<MemorySearchMatch>,    pub next_cursor: Option<String>,    pub truncated: bool,}#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]#[serde(tag = "type", rename_all = "snake_case")]pub enum SearchMatchMode {    Any,    AllOnSameLine,    AllWithinLines {        #[schemars(range(min = 1))]        line_count: usize,    },}#[derive(Debug, Clone, PartialEq, Eq, Serialize, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct MemoryEntry {    pub path: String,    pub entry_type: MemoryEntryType,}#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, JsonSchema)]#[serde(rename_all = "snake_case")]pub enum MemoryEntryType {    File,    Directory,}#[derive(Debug, Clone, PartialEq, Eq, Serialize, JsonSchema)]#[schemars(deny_unknown_fields)]pub struct MemorySearchMatch {    pub path: String,    pub match_line_number: usize,    pub content_start_line_number: usize,    pub content: String,    pub matched_queries: Vec<String>,}#[derive(Debug, thiserror::Error)]pub enum MemoriesBackendError {    #[error("filename '{filename}' {reason}")]    InvalidFilename { filename: String, reason: String },    #[error("ad-hoc note must not be empty")]    EmptyAdHocNote,    #[error("ad-hoc note '{filename}' already exists")]    AdHocNoteAlreadyExists { filename: String },    #[error("path '{path}' {reason}")]    InvalidPath { path: String, reason: String },    #[error("cursor '{cursor}' {reason}")]    InvalidCursor { cursor: String, reason: String },    #[error("path '{path}' was not found")]    NotFound { path: String },    #[error("line_offset must be a 1-indexed line number")]    InvalidLineOffset,    #[error("max_lines must be a positive integer")]    InvalidMaxLines,    #[error("line_offset exceeds file length")]    LineOffsetExceedsFileLength,    #[error("path '{path}' is not a file")]    NotFile { path: String },    #[error("queries must not be empty or contain empty strings")]    EmptyQuery,    #[error("all_within_lines.line_count must be a positive integer")]    InvalidMatchWindow,    #[error("I/O error while reading memories: {0}")]    Io(#[from] std::io::Error),}impl MemoriesBackendError {    pub fn invalid_filename(filename: impl Into<String>, reason: impl Into<String>) -> Self {        Self::InvalidFilename {            filename: filename.into(),            reason: reason.into(),        }    }    pub fn invalid_path(path: impl Into<String>, reason: impl Into<String>) -> Self {        Self::InvalidPath {            path: path.into(),            reason: reason.into(),        }    }    pub fn invalid_cursor(cursor: impl Into<String>, reason: impl Into<String>) -> Self {        Self::InvalidCursor {            cursor: cursor.into(),            reason: reason.into(),        }    }}