diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index fbab962cb..464b7d72a 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -2370,6 +2370,8 @@ dependencies = [ "codex-hooks", "codex-login", "codex-mcp", + "codex-memories-read", + "codex-memories-write", "codex-model-provider", "codex-model-provider-info", "codex-models-manager", @@ -2921,6 +2923,40 @@ dependencies = [ "wiremock", ] +[[package]] +name = "codex-memories-read" +version = "0.0.0" +dependencies = [ + "codex-protocol", + "codex-shell-command", + "codex-utils-absolute-path", + "codex-utils-output-truncation", + "codex-utils-template", + "pretty_assertions", + "tempfile", + "tokio", +] + +[[package]] +name = "codex-memories-write" +version = "0.0.0" +dependencies = [ + "anyhow", + "chrono", + "codex-git-utils", + "codex-models-manager", + "codex-protocol", + "codex-state", + "codex-utils-absolute-path", + "codex-utils-output-truncation", + "codex-utils-template", + "pretty_assertions", + "tempfile", + "tokio", + "tracing", + "uuid", +] + [[package]] name = "codex-model-provider" version = "0.0.0" diff --git a/codex-rs/Cargo.toml b/codex-rs/Cargo.toml index 648c184ec..85c947aea 100644 --- a/codex-rs/Cargo.toml +++ b/codex-rs/Cargo.toml @@ -46,6 +46,8 @@ members = [ "login", "codex-mcp", "mcp-server", + "memories/read", + "memories/write", "model-provider-info", "models-manager", "network-proxy", @@ -153,6 +155,8 @@ codex-keyring-store = { path = "keyring-store" } codex-linux-sandbox = { path = "linux-sandbox" } codex-lmstudio = { path = "lmstudio" } codex-login = { path = "login" } +codex-memories-read = { path = "memories/read" } +codex-memories-write = { path = "memories/write" } codex-mcp = { path = "codex-mcp" } codex-mcp-server = { path = "mcp-server" } codex-model-provider-info = { path = "model-provider-info" } diff --git a/codex-rs/core/Cargo.toml b/codex-rs/core/Cargo.toml index b8d3b146f..c95b57b71 100644 --- a/codex-rs/core/Cargo.toml +++ b/codex-rs/core/Cargo.toml @@ -39,6 +39,8 @@ codex-exec-server = { workspace = true } codex-features = { workspace = true } codex-feedback = { workspace = true } codex-login = { workspace = true } +codex-memories-read = { workspace = true } +codex-memories-write = { workspace = true } codex-mcp = { workspace = true } codex-model-provider-info = { workspace = true } codex-models-manager = { workspace = true } diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index ba21b2ea1..47f2e4dd9 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -1,7 +1,6 @@ use crate::agents_md::AgentsMdManager; use crate::config::edit::ConfigEdit; use crate::config::edit::ConfigEditsBuilder; -use crate::memories::memory_root; use crate::path_utils::normalize_for_native_workdir; use crate::unified_exec::DEFAULT_MAX_BACKGROUND_TERMINAL_TIMEOUT_MS; use crate::unified_exec::MIN_EMPTY_YIELD_TIME_MS; @@ -63,6 +62,7 @@ use codex_features::MultiAgentV2ConfigToml; use codex_git_utils::resolve_root_git_project_for_trust; use codex_login::AuthManagerConfig; use codex_mcp::McpConfig; +use codex_memories_write::memory_root; use codex_model_provider_info::LEGACY_OLLAMA_CHAT_PROVIDER_ID; use codex_model_provider_info::ModelProviderInfo; use codex_model_provider_info::OLLAMA_CHAT_PROVIDER_REMOVED_ERROR; diff --git a/codex-rs/core/src/lib.rs b/codex-rs/core/src/lib.rs index c6f879209..c5e0da9b8 100644 --- a/codex-rs/core/src/lib.rs +++ b/codex-rs/core/src/lib.rs @@ -57,7 +57,7 @@ pub use codex_mcp::SandboxState; mod mcp_openai_file; mod mcp_tool_call; mod memories; -pub use memories::clear_memory_roots_contents; +pub use codex_memories_write::clear_memory_roots_contents; pub(crate) mod mention_syntax; pub(crate) mod message_history; pub(crate) mod utils; @@ -200,4 +200,5 @@ pub mod compact; pub(crate) mod memory_trace; pub use memory_trace::BuiltMemory; pub use memory_trace::build_memories_from_trace_files; +mod memory_usage; pub mod otel_init; diff --git a/codex-rs/core/src/memories/mod.rs b/codex-rs/core/src/memories/mod.rs index 023ea9913..efa70f4bb 100644 --- a/codex-rs/core/src/memories/mod.rs +++ b/codex-rs/core/src/memories/mod.rs @@ -1,37 +1,23 @@ -//! Memory subsystem for startup extraction and consolidation. +//! Memory startup extraction and consolidation orchestration. //! //! The startup memory pipeline is split into two phases: //! - Phase 1: select rollouts, extract stage-1 raw memories, persist stage-1 outputs, and enqueue consolidation. //! - Phase 2: claim a global consolidation lock, materialize consolidation inputs, and dispatch one consolidation agent. -pub(crate) mod citations; -mod control; -mod extensions; mod phase1; mod phase2; -pub(crate) mod prompts; mod start; -mod storage; #[cfg(test)] mod tests; -pub(crate) mod usage; -mod workspace; use codex_protocol::openai_models::ReasoningEffort; -pub use control::clear_memory_roots_contents; /// Starts the memory startup pipeline for eligible root sessions. /// This is the single entrypoint that `codex` uses to trigger memory startup. /// /// This is the entry point to read and understand this module. pub(crate) use start::start_memories_startup_task; -mod artifacts { - pub(super) const EXTENSIONS_SUBDIR: &str = "extensions"; - pub(super) const ROLLOUT_SUMMARIES_SUBDIR: &str = "rollout_summaries"; - pub(super) const RAW_MEMORIES_FILENAME: &str = "raw_memories.md"; -} - /// Phase 1 (startup extraction). mod phase_one { /// Default model used for phase 1. @@ -39,21 +25,9 @@ mod phase_one { /// Default reasoning effort used for phase 1. pub(super) const REASONING_EFFORT: super::ReasoningEffort = super::ReasoningEffort::Low; /// Prompt used for phase 1. - pub(super) const PROMPT: &str = include_str!("../../templates/memories/stage_one_system.md"); + pub(super) const PROMPT: &str = codex_memories_write::STAGE_ONE_PROMPT; /// Concurrency cap for startup memory extraction and consolidation scheduling. pub(super) const CONCURRENCY_LIMIT: usize = 8; - /// Fallback stage-1 rollout truncation limit (tokens) when model metadata - /// does not include a valid context window. - pub(super) const DEFAULT_STAGE_ONE_ROLLOUT_TOKEN_LIMIT: usize = 150_000; - /// Maximum number of tokens from `memory_summary.md` injected into memory - /// tool developer instructions. - pub(super) const MEMORY_TOOL_DEVELOPER_INSTRUCTIONS_SUMMARY_TOKEN_LIMIT: usize = 5_000; - /// Portion of the model effective input window reserved for the stage-1 - /// rollout input. - /// - /// Keeping this below 100% leaves room for system instructions, prompt - /// framing, and model output. - pub(super) const CONTEXT_WINDOW_PERCENT: i64 = 70; /// Lease duration (seconds) for phase-1 job ownership. pub(super) const JOB_LEASE_SECONDS: i64 = 3_600; /// Backoff delay (seconds) before retrying a failed stage-1 extraction job. @@ -97,27 +71,3 @@ mod metrics { /// Histogram for aggregate token usage across one phase-2 consolidation run. pub(super) const MEMORY_PHASE_TWO_TOKEN_USAGE: &str = "codex.memory.phase2.token_usage"; } - -use codex_utils_absolute_path::AbsolutePathBuf; -use std::path::Path; -use std::path::PathBuf; - -pub fn memory_root(codex_home: &AbsolutePathBuf) -> AbsolutePathBuf { - codex_home.join("memories") -} - -fn rollout_summaries_dir(root: &Path) -> PathBuf { - root.join(artifacts::ROLLOUT_SUMMARIES_SUBDIR) -} - -fn memory_extensions_root(root: &Path) -> PathBuf { - root.join(artifacts::EXTENSIONS_SUBDIR) -} - -fn raw_memories_file(root: &Path) -> PathBuf { - root.join(artifacts::RAW_MEMORIES_FILENAME) -} - -async fn ensure_layout(root: &Path) -> std::io::Result<()> { - tokio::fs::create_dir_all(rollout_summaries_dir(root)).await -} diff --git a/codex-rs/core/src/memories/phase1.rs b/codex-rs/core/src/memories/phase1.rs index 8fed735c4..40f65c6db 100644 --- a/codex-rs/core/src/memories/phase1.rs +++ b/codex-rs/core/src/memories/phase1.rs @@ -5,13 +5,13 @@ use crate::context::is_memory_excluded_contextual_user_fragment; use crate::memories::metrics; use crate::memories::phase_one; use crate::memories::phase_one::PRUNE_BATCH_SIZE; -use crate::memories::prompts::build_stage_one_input_message; use crate::rollout::INTERACTIVE_SESSION_SOURCES; use crate::rollout::policy::should_persist_response_item_for_memories; use crate::session::session::Session; use crate::session::turn_context::TurnContext; use codex_api::ResponseEvent; use codex_config::types::MemoriesConfig; +use codex_memories_write::build_stage_one_input_message; use codex_otel::SessionTelemetry; use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig; use codex_protocol::config_types::ServiceTier; diff --git a/codex-rs/core/src/memories/phase2.rs b/codex-rs/core/src/memories/phase2.rs index 0b7ffd613..d156d1dee 100644 --- a/codex-rs/core/src/memories/phase2.rs +++ b/codex-rs/core/src/memories/phase2.rs @@ -1,21 +1,21 @@ use crate::agent::AgentStatus; use crate::agent::status::is_final as is_final_agent_status; use crate::config::Config; -use crate::memories::extensions::prune_old_extension_resources; -use crate::memories::memory_root; use crate::memories::metrics; use crate::memories::phase_two; -use crate::memories::prompts::build_consolidation_prompt; -use crate::memories::storage::rebuild_raw_memories_file_from_memories; -use crate::memories::storage::sync_rollout_summaries_from_memories; -use crate::memories::workspace::memory_workspace_diff; -use crate::memories::workspace::prepare_memory_workspace; -use crate::memories::workspace::reset_memory_workspace_baseline; -use crate::memories::workspace::write_workspace_diff; use crate::session::emit_subagent_session_started; use crate::session::session::Session; use codex_config::Constrained; use codex_features::Feature; +use codex_memories_write::build_consolidation_prompt; +use codex_memories_write::memory_root; +use codex_memories_write::prune_old_extension_resources; +use codex_memories_write::rebuild_raw_memories_file_from_memories; +use codex_memories_write::sync_rollout_summaries_from_memories; +use codex_memories_write::workspace::memory_workspace_diff; +use codex_memories_write::workspace::prepare_memory_workspace; +use codex_memories_write::workspace::reset_memory_workspace_baseline; +use codex_memories_write::workspace::write_workspace_diff; use codex_protocol::ThreadId; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::SandboxPolicy; diff --git a/codex-rs/core/src/memories/tests.rs b/codex-rs/core/src/memories/tests.rs index 7c75a6448..c00c77b8e 100644 --- a/codex-rs/core/src/memories/tests.rs +++ b/codex-rs/core/src/memories/tests.rs @@ -1,13 +1,13 @@ -use super::control::clear_memory_root_contents; -use super::storage::rebuild_raw_memories_file_from_memories; -use super::storage::sync_rollout_summaries_from_memories; -use crate::memories::ensure_layout; -use crate::memories::memory_root; -use crate::memories::raw_memories_file; -use crate::memories::rollout_summaries_dir; use chrono::TimeZone; use chrono::Utc; use codex_config::types::DEFAULT_MEMORIES_MAX_RAW_MEMORIES_FOR_CONSOLIDATION; +use codex_memories_write::clear_memory_roots_contents; +use codex_memories_write::ensure_layout; +use codex_memories_write::memory_root; +use codex_memories_write::raw_memories_file; +use codex_memories_write::rebuild_raw_memories_file_from_memories; +use codex_memories_write::rollout_summaries_dir; +use codex_memories_write::sync_rollout_summaries_from_memories; use codex_protocol::ThreadId; use codex_state::Stage1Output; use codex_utils_absolute_path::AbsolutePathBuf; @@ -68,7 +68,7 @@ fn stage_one_output_schema_requires_rollout_slug_and_keeps_it_nullable() { #[tokio::test] async fn clear_memory_root_contents_preserves_root_directory() { let dir = tempdir().expect("tempdir"); - let root = dir.path().join("memory"); + let root = dir.path().join("memories"); let nested_dir = root.join("rollout_summaries"); tokio::fs::create_dir_all(&nested_dir) .await @@ -80,7 +80,7 @@ async fn clear_memory_root_contents_preserves_root_directory() { .await .expect("write rollout summary"); - clear_memory_root_contents(&root) + clear_memory_roots_contents(dir.path()) .await .expect("clear memory root contents"); @@ -116,10 +116,10 @@ async fn clear_memory_root_contents_rejects_symlinked_root() { .await .expect("write target file"); - let root = dir.path().join("memory"); + let root = dir.path().join("memories"); std::os::unix::fs::symlink(&target, &root).expect("create memory root symlink"); - let err = clear_memory_root_contents(&root) + let err = clear_memory_roots_contents(dir.path()) .await .expect_err("symlinked memory root should be rejected"); assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput); @@ -509,13 +509,7 @@ mod phase2 { use crate::agent::AgentControl; use crate::config::Config; use crate::config::test_config; - use crate::memories::memory_root; use crate::memories::phase2; - use crate::memories::raw_memories_file; - use crate::memories::rollout_summaries_dir; - use crate::memories::storage::rebuild_raw_memories_file_from_memories; - use crate::memories::storage::sync_rollout_summaries_from_memories; - use crate::memories::workspace::prepare_memory_workspace; use crate::session::session::Session; use crate::session::tests::make_session_and_context; use chrono::Duration as ChronoDuration; @@ -524,6 +518,12 @@ mod phase2 { use codex_config::types::McpServerConfig; use codex_features::Feature; use codex_login::CodexAuth; + use codex_memories_write::memory_root; + use codex_memories_write::raw_memories_file; + use codex_memories_write::rebuild_raw_memories_file_from_memories; + use codex_memories_write::rollout_summaries_dir; + use codex_memories_write::sync_rollout_summaries_from_memories; + use codex_memories_write::workspace::prepare_memory_workspace; use codex_protocol::AgentPath; use codex_protocol::ThreadId; use codex_protocol::models::PermissionProfile; diff --git a/codex-rs/core/src/memories/usage.rs b/codex-rs/core/src/memory_usage.rs similarity index 60% rename from codex-rs/core/src/memories/usage.rs rename to codex-rs/core/src/memory_usage.rs index 480ef5dd9..02f74ea59 100644 --- a/codex-rs/core/src/memories/usage.rs +++ b/codex-rs/core/src/memory_usage.rs @@ -1,38 +1,17 @@ use crate::tools::context::ToolInvocation; use crate::tools::context::ToolPayload; use crate::tools::handlers::unified_exec::ExecCommandArgs; +use codex_memories_read::usage::MEMORIES_USAGE_METRIC; +use codex_memories_read::usage::memories_usage_kinds_from_command; use codex_protocol::models::ShellCommandToolCallParams; use codex_protocol::models::ShellToolCallParams; -use codex_protocol::parse_command::ParsedCommand; -use codex_shell_command::is_safe_command::is_known_safe_command; -use codex_shell_command::parse_command::parse_command; use std::path::PathBuf; -const MEMORIES_USAGE_METRIC: &str = "codex.memories.usage"; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -enum MemoriesUsageKind { - MemoryMd, - MemorySummary, - RawMemories, - RolloutSummaries, - Skills, -} - -impl MemoriesUsageKind { - fn as_tag(self) -> &'static str { - match self { - Self::MemoryMd => "memory_md", - Self::MemorySummary => "memory_summary", - Self::RawMemories => "raw_memories", - Self::RolloutSummaries => "rollout_summaries", - Self::Skills => "skills", - } - } -} - pub(crate) async fn emit_metric_for_tool_read(invocation: &ToolInvocation, success: bool) { - let kinds = memories_usage_kinds_from_invocation(invocation).await; + let Some((command, _)) = shell_command_for_invocation(invocation) else { + return; + }; + let kinds = memories_usage_kinds_from_command(&command); if kinds.is_empty() { return; } @@ -52,27 +31,6 @@ pub(crate) async fn emit_metric_for_tool_read(invocation: &ToolInvocation, succe } } -async fn memories_usage_kinds_from_invocation( - invocation: &ToolInvocation, -) -> Vec { - let Some((command, _)) = shell_command_for_invocation(invocation) else { - return Vec::new(); - }; - if !is_known_safe_command(&command) { - return Vec::new(); - } - - let parsed_commands = parse_command(&command); - parsed_commands - .into_iter() - .filter_map(|command| match command { - ParsedCommand::Read { path, .. } => get_memory_kind(path.display().to_string()), - ParsedCommand::Search { path, .. } => path.and_then(get_memory_kind), - ParsedCommand::ListFiles { .. } | ParsedCommand::Unknown { .. } => None, - }) - .collect() -} - fn shell_command_for_invocation(invocation: &ToolInvocation) -> Option<(Vec, PathBuf)> { let ToolPayload::Function { arguments } = &invocation.payload else { return None; @@ -129,19 +87,3 @@ fn shell_command_for_invocation(invocation: &ToolInvocation) -> Option<(Vec None, } } - -fn get_memory_kind(path: String) -> Option { - if path.contains("memories/MEMORY.md") { - Some(MemoriesUsageKind::MemoryMd) - } else if path.contains("memories/memory_summary.md") { - Some(MemoriesUsageKind::MemorySummary) - } else if path.contains("memories/raw_memories.md") { - Some(MemoriesUsageKind::RawMemories) - } else if path.contains("memories/rollout_summaries/") { - Some(MemoriesUsageKind::RolloutSummaries) - } else if path.contains("memories/skills/") { - Some(MemoriesUsageKind::Skills) - } else { - None - } -} diff --git a/codex-rs/core/src/session/handlers.rs b/codex-rs/core/src/session/handlers.rs index 86ce79c90..5c11b2d9d 100644 --- a/codex-rs/core/src/session/handlers.rs +++ b/codex-rs/core/src/session/handlers.rs @@ -681,7 +681,7 @@ pub async fn drop_memories(sess: &Arc, config: &Arc, sub_id: St errors.push("state db unavailable; memory rows were not cleared".to_string()); } - if let Err(err) = crate::memories::clear_memory_roots_contents(&config.codex_home).await { + if let Err(err) = codex_memories_write::clear_memory_roots_contents(&config.codex_home).await { errors.push(format!( "failed clearing memory directories under {}: {err}", config.codex_home.display() @@ -689,7 +689,7 @@ pub async fn drop_memories(sess: &Arc, config: &Arc, sub_id: St } if errors.is_empty() { - let memory_root = crate::memories::memory_root(&config.codex_home); + let memory_root = codex_memories_write::memory_root(&config.codex_home); sess.send_event_raw(Event { id: sub_id, msg: EventMsg::Warning(WarningEvent { diff --git a/codex-rs/core/src/session/mod.rs b/codex-rs/core/src/session/mod.rs index 598d9d7dc..1ebe2b4fc 100644 --- a/codex-rs/core/src/session/mod.rs +++ b/codex-rs/core/src/session/mod.rs @@ -3323,7 +3323,7 @@ fn errors_to_info(errors: &[SkillError]) -> Vec { .collect() } -use crate::memories::prompts::build_memory_tool_developer_instructions; +use codex_memories_read::build_memory_tool_developer_instructions; #[cfg(test)] pub(crate) mod tests; diff --git a/codex-rs/core/src/stream_events_utils.rs b/codex-rs/core/src/stream_events_utils.rs index 55c85747e..5a31d1802 100644 --- a/codex-rs/core/src/stream_events_utils.rs +++ b/codex-rs/core/src/stream_events_utils.rs @@ -11,13 +11,13 @@ use tokio_util::sync::CancellationToken; use crate::context::ContextualUserFragment; use crate::context::ImageGenerationInstructions; use crate::function_tool::FunctionCallError; -use crate::memories::citations::parse_memory_citation; -use crate::memories::citations::thread_ids_from_memory_citation; use crate::parse_turn_item; use crate::session::session::Session; use crate::session::turn_context::TurnContext; use crate::tools::parallel::ToolCallRuntime; use crate::tools::router::ToolRouter; +use codex_memories_read::citations::parse_memory_citation; +use codex_memories_read::citations::thread_ids_from_memory_citation; use codex_protocol::error::CodexErr; use codex_protocol::error::Result; use codex_protocol::models::FunctionCallOutputBody; diff --git a/codex-rs/core/src/tools/registry.rs b/codex-rs/core/src/tools/registry.rs index acc1eacbf..29abab69f 100644 --- a/codex-rs/core/src/tools/registry.rs +++ b/codex-rs/core/src/tools/registry.rs @@ -8,7 +8,7 @@ use crate::goals::GoalRuntimeEvent; use crate::hook_runtime::record_additional_contexts; use crate::hook_runtime::run_post_tool_use_hooks; use crate::hook_runtime::run_pre_tool_use_hooks; -use crate::memories::usage::emit_metric_for_tool_read; +use crate::memory_usage::emit_metric_for_tool_read; use crate::sandbox_tags::permission_profile_policy_tag; use crate::sandbox_tags::permission_profile_sandbox_tag; use crate::session::turn_context::TurnContext; diff --git a/codex-rs/core/src/memories/README.md b/codex-rs/memories/README.md similarity index 88% rename from codex-rs/core/src/memories/README.md rename to codex-rs/memories/README.md index 8a885fd86..dfea3969f 100644 --- a/codex-rs/core/src/memories/README.md +++ b/codex-rs/memories/README.md @@ -1,16 +1,28 @@ -# Memories Pipeline (Core) +# Memories -This module runs a startup memory pipeline for eligible sessions. +This directory owns reusable memory crates and the memory pipeline documentation. + +Runtime orchestration for Phase 1 and Phase 2 still lives in `codex-core` under +`codex-rs/core/src/memories/`. + +## Crates + +- `codex-rs/memories/read` (`codex-memories-read`) owns the read path: + memory developer-instruction injection, memory citation parsing, and + read-usage telemetry classification. +- `codex-rs/memories/write` (`codex-memories-write`) owns the write path: + Phase 1 and Phase 2 prompt rendering, filesystem artifact helpers, + workspace diff helpers, and extension resource pruning. ## Prompt Templates -Memory prompt templates live under `codex-rs/core/templates/memories/`. +Memory prompt templates live with the crate that uses them: - The undated template files are the canonical latest versions used at runtime: - - `stage_one_system.md` - - `stage_one_input.md` - - `consolidation.md` - - `read_path.md` + - `read/templates/memories/read_path.md` + - `write/templates/memories/stage_one_system.md` + - `write/templates/memories/stage_one_input.md` + - `write/templates/memories/consolidation.md` - In `codex`, edit those undated template files in place. - The dated snapshot-copy workflow is used in the separate `openai/project/agent_memory/write` harness repo, not here. diff --git a/codex-rs/memories/read/BUILD.bazel b/codex-rs/memories/read/BUILD.bazel new file mode 100644 index 000000000..54cf2e3b0 --- /dev/null +++ b/codex-rs/memories/read/BUILD.bazel @@ -0,0 +1,9 @@ +load("//:defs.bzl", "codex_rust_crate") + +codex_rust_crate( + name = "read", + crate_name = "codex_memories_read", + compile_data = glob([ + "templates/**", + ]), +) diff --git a/codex-rs/memories/read/Cargo.toml b/codex-rs/memories/read/Cargo.toml new file mode 100644 index 000000000..57aff37d6 --- /dev/null +++ b/codex-rs/memories/read/Cargo.toml @@ -0,0 +1,25 @@ +[package] +edition.workspace = true +license.workspace = true +name = "codex-memories-read" +version.workspace = true + +[lib] +name = "codex_memories_read" +path = "src/lib.rs" + +[lints] +workspace = true + +[dependencies] +codex-protocol = { workspace = true } +codex-shell-command = { workspace = true } +codex-utils-absolute-path = { workspace = true } +codex-utils-output-truncation = { workspace = true } +codex-utils-template = { workspace = true } +tokio = { workspace = true, features = ["fs"] } + +[dev-dependencies] +pretty_assertions = { workspace = true } +tempfile = { workspace = true } +tokio = { workspace = true, features = ["fs", "macros"] } diff --git a/codex-rs/core/src/memories/citations.rs b/codex-rs/memories/read/src/citations.rs similarity index 100% rename from codex-rs/core/src/memories/citations.rs rename to codex-rs/memories/read/src/citations.rs diff --git a/codex-rs/core/src/memories/citations_tests.rs b/codex-rs/memories/read/src/citations_tests.rs similarity index 100% rename from codex-rs/core/src/memories/citations_tests.rs rename to codex-rs/memories/read/src/citations_tests.rs diff --git a/codex-rs/memories/read/src/lib.rs b/codex-rs/memories/read/src/lib.rs new file mode 100644 index 000000000..e08568408 --- /dev/null +++ b/codex-rs/memories/read/src/lib.rs @@ -0,0 +1,19 @@ +//! Read-path helpers for Codex memories. +//! +//! This crate owns memory injection, memory citation parsing, and telemetry +//! classification for read access to the memory folder. It intentionally does +//! not depend on the memory write pipeline. + +pub mod citations; +mod prompts; +pub mod usage; + +use codex_utils_absolute_path::AbsolutePathBuf; + +pub use prompts::build_memory_tool_developer_instructions; + +const MEMORY_TOOL_DEVELOPER_INSTRUCTIONS_SUMMARY_TOKEN_LIMIT: usize = 5_000; + +pub fn memory_root(codex_home: &AbsolutePathBuf) -> AbsolutePathBuf { + codex_home.join("memories") +} diff --git a/codex-rs/memories/read/src/prompts.rs b/codex-rs/memories/read/src/prompts.rs new file mode 100644 index 000000000..5bba68aa6 --- /dev/null +++ b/codex-rs/memories/read/src/prompts.rs @@ -0,0 +1,56 @@ +use crate::MEMORY_TOOL_DEVELOPER_INSTRUCTIONS_SUMMARY_TOKEN_LIMIT; +use crate::memory_root; +use codex_utils_absolute_path::AbsolutePathBuf; +use codex_utils_output_truncation::TruncationPolicy; +use codex_utils_output_truncation::truncate_text; +use codex_utils_template::Template; +use std::sync::LazyLock; +use tokio::fs; + +static MEMORY_TOOL_DEVELOPER_INSTRUCTIONS_TEMPLATE: LazyLock