mirror of
https://github.com/openai/codex.git
synced 2026-11-09 08:40:33 +00:00
chore: split memories part 1 (#19818)
Extract memories into 2 different crates
This commit is contained in:
1 parent
f431ec12c9
commit
bb83eec825
39 files changed
+436
-267
No files matched your search
Generated
+36
@@ -2370,6 +2370,8 @@ dependencies = [
|
|||||||
"codex-hooks",
|
"codex-hooks",
|
||||||
"codex-login",
|
"codex-login",
|
||||||
"codex-mcp",
|
"codex-mcp",
|
||||||
|
"codex-memories-read",
|
||||||
|
"codex-memories-write",
|
||||||
"codex-model-provider",
|
"codex-model-provider",
|
||||||
"codex-model-provider-info",
|
"codex-model-provider-info",
|
||||||
"codex-models-manager",
|
"codex-models-manager",
|
||||||
@@ -2921,6 +2923,40 @@ dependencies = [
|
|||||||
"wiremock",
|
"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]]
|
[[package]]
|
||||||
name = "codex-model-provider"
|
name = "codex-model-provider"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ members = [
|
|||||||
"login",
|
"login",
|
||||||
"codex-mcp",
|
"codex-mcp",
|
||||||
"mcp-server",
|
"mcp-server",
|
||||||
|
"memories/read",
|
||||||
|
"memories/write",
|
||||||
"model-provider-info",
|
"model-provider-info",
|
||||||
"models-manager",
|
"models-manager",
|
||||||
"network-proxy",
|
"network-proxy",
|
||||||
@@ -153,6 +155,8 @@ codex-keyring-store = { path = "keyring-store" }
|
|||||||
codex-linux-sandbox = { path = "linux-sandbox" }
|
codex-linux-sandbox = { path = "linux-sandbox" }
|
||||||
codex-lmstudio = { path = "lmstudio" }
|
codex-lmstudio = { path = "lmstudio" }
|
||||||
codex-login = { path = "login" }
|
codex-login = { path = "login" }
|
||||||
|
codex-memories-read = { path = "memories/read" }
|
||||||
|
codex-memories-write = { path = "memories/write" }
|
||||||
codex-mcp = { path = "codex-mcp" }
|
codex-mcp = { path = "codex-mcp" }
|
||||||
codex-mcp-server = { path = "mcp-server" }
|
codex-mcp-server = { path = "mcp-server" }
|
||||||
codex-model-provider-info = { path = "model-provider-info" }
|
codex-model-provider-info = { path = "model-provider-info" }
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ codex-exec-server = { workspace = true }
|
|||||||
codex-features = { workspace = true }
|
codex-features = { workspace = true }
|
||||||
codex-feedback = { workspace = true }
|
codex-feedback = { workspace = true }
|
||||||
codex-login = { workspace = true }
|
codex-login = { workspace = true }
|
||||||
|
codex-memories-read = { workspace = true }
|
||||||
|
codex-memories-write = { workspace = true }
|
||||||
codex-mcp = { workspace = true }
|
codex-mcp = { workspace = true }
|
||||||
codex-model-provider-info = { workspace = true }
|
codex-model-provider-info = { workspace = true }
|
||||||
codex-models-manager = { workspace = true }
|
codex-models-manager = { workspace = true }
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use crate::agents_md::AgentsMdManager;
|
use crate::agents_md::AgentsMdManager;
|
||||||
use crate::config::edit::ConfigEdit;
|
use crate::config::edit::ConfigEdit;
|
||||||
use crate::config::edit::ConfigEditsBuilder;
|
use crate::config::edit::ConfigEditsBuilder;
|
||||||
use crate::memories::memory_root;
|
|
||||||
use crate::path_utils::normalize_for_native_workdir;
|
use crate::path_utils::normalize_for_native_workdir;
|
||||||
use crate::unified_exec::DEFAULT_MAX_BACKGROUND_TERMINAL_TIMEOUT_MS;
|
use crate::unified_exec::DEFAULT_MAX_BACKGROUND_TERMINAL_TIMEOUT_MS;
|
||||||
use crate::unified_exec::MIN_EMPTY_YIELD_TIME_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_git_utils::resolve_root_git_project_for_trust;
|
||||||
use codex_login::AuthManagerConfig;
|
use codex_login::AuthManagerConfig;
|
||||||
use codex_mcp::McpConfig;
|
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::LEGACY_OLLAMA_CHAT_PROVIDER_ID;
|
||||||
use codex_model_provider_info::ModelProviderInfo;
|
use codex_model_provider_info::ModelProviderInfo;
|
||||||
use codex_model_provider_info::OLLAMA_CHAT_PROVIDER_REMOVED_ERROR;
|
use codex_model_provider_info::OLLAMA_CHAT_PROVIDER_REMOVED_ERROR;
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ pub use codex_mcp::SandboxState;
|
|||||||
mod mcp_openai_file;
|
mod mcp_openai_file;
|
||||||
mod mcp_tool_call;
|
mod mcp_tool_call;
|
||||||
mod memories;
|
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 mention_syntax;
|
||||||
pub(crate) mod message_history;
|
pub(crate) mod message_history;
|
||||||
pub(crate) mod utils;
|
pub(crate) mod utils;
|
||||||
@@ -200,4 +200,5 @@ pub mod compact;
|
|||||||
pub(crate) mod memory_trace;
|
pub(crate) mod memory_trace;
|
||||||
pub use memory_trace::BuiltMemory;
|
pub use memory_trace::BuiltMemory;
|
||||||
pub use memory_trace::build_memories_from_trace_files;
|
pub use memory_trace::build_memories_from_trace_files;
|
||||||
|
mod memory_usage;
|
||||||
pub mod otel_init;
|
pub mod otel_init;
|
||||||
@@ -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:
|
//! 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 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.
|
//! - 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 phase1;
|
||||||
mod phase2;
|
mod phase2;
|
||||||
pub(crate) mod prompts;
|
|
||||||
mod start;
|
mod start;
|
||||||
mod storage;
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
pub(crate) mod usage;
|
|
||||||
mod workspace;
|
|
||||||
|
|
||||||
use codex_protocol::openai_models::ReasoningEffort;
|
use codex_protocol::openai_models::ReasoningEffort;
|
||||||
|
|
||||||
pub use control::clear_memory_roots_contents;
|
|
||||||
/// Starts the memory startup pipeline for eligible root sessions.
|
/// Starts the memory startup pipeline for eligible root sessions.
|
||||||
/// This is the single entrypoint that `codex` uses to trigger memory startup.
|
/// This is the single entrypoint that `codex` uses to trigger memory startup.
|
||||||
///
|
///
|
||||||
/// This is the entry point to read and understand this module.
|
/// This is the entry point to read and understand this module.
|
||||||
pub(crate) use start::start_memories_startup_task;
|
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).
|
/// Phase 1 (startup extraction).
|
||||||
mod phase_one {
|
mod phase_one {
|
||||||
/// Default model used for phase 1.
|
/// Default model used for phase 1.
|
||||||
@@ -39,21 +25,9 @@ mod phase_one {
|
|||||||
/// Default reasoning effort used for phase 1.
|
/// Default reasoning effort used for phase 1.
|
||||||
pub(super) const REASONING_EFFORT: super::ReasoningEffort = super::ReasoningEffort::Low;
|
pub(super) const REASONING_EFFORT: super::ReasoningEffort = super::ReasoningEffort::Low;
|
||||||
/// Prompt used for phase 1.
|
/// 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.
|
/// Concurrency cap for startup memory extraction and consolidation scheduling.
|
||||||
pub(super) const CONCURRENCY_LIMIT: usize = 8;
|
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.
|
/// Lease duration (seconds) for phase-1 job ownership.
|
||||||
pub(super) const JOB_LEASE_SECONDS: i64 = 3_600;
|
pub(super) const JOB_LEASE_SECONDS: i64 = 3_600;
|
||||||
/// Backoff delay (seconds) before retrying a failed stage-1 extraction job.
|
/// 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.
|
/// 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";
|
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
|
|
||||||
}
|
|
||||||
@@ -5,13 +5,13 @@ use crate::context::is_memory_excluded_contextual_user_fragment;
|
|||||||
use crate::memories::metrics;
|
use crate::memories::metrics;
|
||||||
use crate::memories::phase_one;
|
use crate::memories::phase_one;
|
||||||
use crate::memories::phase_one::PRUNE_BATCH_SIZE;
|
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::INTERACTIVE_SESSION_SOURCES;
|
||||||
use crate::rollout::policy::should_persist_response_item_for_memories;
|
use crate::rollout::policy::should_persist_response_item_for_memories;
|
||||||
use crate::session::session::Session;
|
use crate::session::session::Session;
|
||||||
use crate::session::turn_context::TurnContext;
|
use crate::session::turn_context::TurnContext;
|
||||||
use codex_api::ResponseEvent;
|
use codex_api::ResponseEvent;
|
||||||
use codex_config::types::MemoriesConfig;
|
use codex_config::types::MemoriesConfig;
|
||||||
|
use codex_memories_write::build_stage_one_input_message;
|
||||||
use codex_otel::SessionTelemetry;
|
use codex_otel::SessionTelemetry;
|
||||||
use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig;
|
use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig;
|
||||||
use codex_protocol::config_types::ServiceTier;
|
use codex_protocol::config_types::ServiceTier;
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
use crate::agent::AgentStatus;
|
use crate::agent::AgentStatus;
|
||||||
use crate::agent::status::is_final as is_final_agent_status;
|
use crate::agent::status::is_final as is_final_agent_status;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::memories::extensions::prune_old_extension_resources;
|
|
||||||
use crate::memories::memory_root;
|
|
||||||
use crate::memories::metrics;
|
use crate::memories::metrics;
|
||||||
use crate::memories::phase_two;
|
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::emit_subagent_session_started;
|
||||||
use crate::session::session::Session;
|
use crate::session::session::Session;
|
||||||
use codex_config::Constrained;
|
use codex_config::Constrained;
|
||||||
use codex_features::Feature;
|
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::ThreadId;
|
||||||
use codex_protocol::protocol::AskForApproval;
|
use codex_protocol::protocol::AskForApproval;
|
||||||
use codex_protocol::protocol::SandboxPolicy;
|
use codex_protocol::protocol::SandboxPolicy;
|
||||||
|
|||||||
@@ -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::TimeZone;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use codex_config::types::DEFAULT_MEMORIES_MAX_RAW_MEMORIES_FOR_CONSOLIDATION;
|
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_protocol::ThreadId;
|
||||||
use codex_state::Stage1Output;
|
use codex_state::Stage1Output;
|
||||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||||
@@ -68,7 +68,7 @@ fn stage_one_output_schema_requires_rollout_slug_and_keeps_it_nullable() {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn clear_memory_root_contents_preserves_root_directory() {
|
async fn clear_memory_root_contents_preserves_root_directory() {
|
||||||
let dir = tempdir().expect("tempdir");
|
let dir = tempdir().expect("tempdir");
|
||||||
let root = dir.path().join("memory");
|
let root = dir.path().join("memories");
|
||||||
let nested_dir = root.join("rollout_summaries");
|
let nested_dir = root.join("rollout_summaries");
|
||||||
tokio::fs::create_dir_all(&nested_dir)
|
tokio::fs::create_dir_all(&nested_dir)
|
||||||
.await
|
.await
|
||||||
@@ -80,7 +80,7 @@ async fn clear_memory_root_contents_preserves_root_directory() {
|
|||||||
.await
|
.await
|
||||||
.expect("write rollout summary");
|
.expect("write rollout summary");
|
||||||
|
|
||||||
clear_memory_root_contents(&root)
|
clear_memory_roots_contents(dir.path())
|
||||||
.await
|
.await
|
||||||
.expect("clear memory root contents");
|
.expect("clear memory root contents");
|
||||||
|
|
||||||
@@ -116,10 +116,10 @@ async fn clear_memory_root_contents_rejects_symlinked_root() {
|
|||||||
.await
|
.await
|
||||||
.expect("write target file");
|
.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");
|
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
|
.await
|
||||||
.expect_err("symlinked memory root should be rejected");
|
.expect_err("symlinked memory root should be rejected");
|
||||||
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
|
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
|
||||||
@@ -509,13 +509,7 @@ mod phase2 {
|
|||||||
use crate::agent::AgentControl;
|
use crate::agent::AgentControl;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::config::test_config;
|
use crate::config::test_config;
|
||||||
use crate::memories::memory_root;
|
|
||||||
use crate::memories::phase2;
|
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::session::Session;
|
||||||
use crate::session::tests::make_session_and_context;
|
use crate::session::tests::make_session_and_context;
|
||||||
use chrono::Duration as ChronoDuration;
|
use chrono::Duration as ChronoDuration;
|
||||||
@@ -524,6 +518,12 @@ mod phase2 {
|
|||||||
use codex_config::types::McpServerConfig;
|
use codex_config::types::McpServerConfig;
|
||||||
use codex_features::Feature;
|
use codex_features::Feature;
|
||||||
use codex_login::CodexAuth;
|
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::AgentPath;
|
||||||
use codex_protocol::ThreadId;
|
use codex_protocol::ThreadId;
|
||||||
use codex_protocol::models::PermissionProfile;
|
use codex_protocol::models::PermissionProfile;
|
||||||
|
|||||||
@@ -1,38 +1,17 @@
|
|||||||
use crate::tools::context::ToolInvocation;
|
use crate::tools::context::ToolInvocation;
|
||||||
use crate::tools::context::ToolPayload;
|
use crate::tools::context::ToolPayload;
|
||||||
use crate::tools::handlers::unified_exec::ExecCommandArgs;
|
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::ShellCommandToolCallParams;
|
||||||
use codex_protocol::models::ShellToolCallParams;
|
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;
|
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) {
|
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() {
|
if kinds.is_empty() {
|
||||||
return;
|
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<MemoriesUsageKind> {
|
|
||||||
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<String>, PathBuf)> {
|
fn shell_command_for_invocation(invocation: &ToolInvocation) -> Option<(Vec<String>, PathBuf)> {
|
||||||
let ToolPayload::Function { arguments } = &invocation.payload else {
|
let ToolPayload::Function { arguments } = &invocation.payload else {
|
||||||
return None;
|
return None;
|
||||||
@@ -129,19 +87,3 @@ fn shell_command_for_invocation(invocation: &ToolInvocation) -> Option<(Vec<Stri
|
|||||||
(Some(_), _) | (None, _) => None,
|
(Some(_), _) | (None, _) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_memory_kind(path: String) -> Option<MemoriesUsageKind> {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -681,7 +681,7 @@ pub async fn drop_memories(sess: &Arc<Session>, config: &Arc<Config>, sub_id: St
|
|||||||
errors.push("state db unavailable; memory rows were not cleared".to_string());
|
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!(
|
errors.push(format!(
|
||||||
"failed clearing memory directories under {}: {err}",
|
"failed clearing memory directories under {}: {err}",
|
||||||
config.codex_home.display()
|
config.codex_home.display()
|
||||||
@@ -689,7 +689,7 @@ pub async fn drop_memories(sess: &Arc<Session>, config: &Arc<Config>, sub_id: St
|
|||||||
}
|
}
|
||||||
|
|
||||||
if errors.is_empty() {
|
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 {
|
sess.send_event_raw(Event {
|
||||||
id: sub_id,
|
id: sub_id,
|
||||||
msg: EventMsg::Warning(WarningEvent {
|
msg: EventMsg::Warning(WarningEvent {
|
||||||
|
|||||||
@@ -3323,7 +3323,7 @@ fn errors_to_info(errors: &[SkillError]) -> Vec<SkillErrorInfo> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
use crate::memories::prompts::build_memory_tool_developer_instructions;
|
use codex_memories_read::build_memory_tool_developer_instructions;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod tests;
|
pub(crate) mod tests;
|
||||||
@@ -11,13 +11,13 @@ use tokio_util::sync::CancellationToken;
|
|||||||
use crate::context::ContextualUserFragment;
|
use crate::context::ContextualUserFragment;
|
||||||
use crate::context::ImageGenerationInstructions;
|
use crate::context::ImageGenerationInstructions;
|
||||||
use crate::function_tool::FunctionCallError;
|
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::parse_turn_item;
|
||||||
use crate::session::session::Session;
|
use crate::session::session::Session;
|
||||||
use crate::session::turn_context::TurnContext;
|
use crate::session::turn_context::TurnContext;
|
||||||
use crate::tools::parallel::ToolCallRuntime;
|
use crate::tools::parallel::ToolCallRuntime;
|
||||||
use crate::tools::router::ToolRouter;
|
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::CodexErr;
|
||||||
use codex_protocol::error::Result;
|
use codex_protocol::error::Result;
|
||||||
use codex_protocol::models::FunctionCallOutputBody;
|
use codex_protocol::models::FunctionCallOutputBody;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use crate::goals::GoalRuntimeEvent;
|
|||||||
use crate::hook_runtime::record_additional_contexts;
|
use crate::hook_runtime::record_additional_contexts;
|
||||||
use crate::hook_runtime::run_post_tool_use_hooks;
|
use crate::hook_runtime::run_post_tool_use_hooks;
|
||||||
use crate::hook_runtime::run_pre_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_policy_tag;
|
||||||
use crate::sandbox_tags::permission_profile_sandbox_tag;
|
use crate::sandbox_tags::permission_profile_sandbox_tag;
|
||||||
use crate::session::turn_context::TurnContext;
|
use crate::session::turn_context::TurnContext;
|
||||||
|
|||||||
@@ -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
|
## 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:
|
- The undated template files are the canonical latest versions used at runtime:
|
||||||
- `stage_one_system.md`
|
- `read/templates/memories/read_path.md`
|
||||||
- `stage_one_input.md`
|
- `write/templates/memories/stage_one_system.md`
|
||||||
- `consolidation.md`
|
- `write/templates/memories/stage_one_input.md`
|
||||||
- `read_path.md`
|
- `write/templates/memories/consolidation.md`
|
||||||
- In `codex`, edit those undated template files in place.
|
- 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.
|
- The dated snapshot-copy workflow is used in the separate `openai/project/agent_memory/write` harness repo, not here.
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
load("//:defs.bzl", "codex_rust_crate")
|
||||||
|
|
||||||
|
codex_rust_crate(
|
||||||
|
name = "read",
|
||||||
|
crate_name = "codex_memories_read",
|
||||||
|
compile_data = glob([
|
||||||
|
"templates/**",
|
||||||
|
]),
|
||||||
|
)
|
||||||
@@ -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"] }
|
||||||
File renamed without changes.
File renamed without changes.
@@ -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")
|
||||||
|
}
|
||||||
@@ -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<Template> = LazyLock::new(|| {
|
||||||
|
parse_embedded_template(
|
||||||
|
include_str!("../templates/memories/read_path.md"),
|
||||||
|
"memories/read_path.md",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
fn parse_embedded_template(source: &'static str, template_name: &str) -> Template {
|
||||||
|
match Template::parse(source) {
|
||||||
|
Ok(template) => template,
|
||||||
|
Err(err) => panic!("embedded template {template_name} is invalid: {err}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build the read-path prompt that is added to developer instructions.
|
||||||
|
///
|
||||||
|
/// Large `memory_summary.md` files are truncated at
|
||||||
|
/// [MEMORY_TOOL_DEVELOPER_INSTRUCTIONS_SUMMARY_TOKEN_LIMIT].
|
||||||
|
pub async fn build_memory_tool_developer_instructions(
|
||||||
|
codex_home: &AbsolutePathBuf,
|
||||||
|
) -> Option<String> {
|
||||||
|
let base_path = memory_root(codex_home);
|
||||||
|
let memory_summary_path = base_path.join("memory_summary.md");
|
||||||
|
let memory_summary = fs::read_to_string(&memory_summary_path)
|
||||||
|
.await
|
||||||
|
.ok()?
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
let memory_summary = truncate_text(
|
||||||
|
&memory_summary,
|
||||||
|
TruncationPolicy::Tokens(MEMORY_TOOL_DEVELOPER_INSTRUCTIONS_SUMMARY_TOKEN_LIMIT),
|
||||||
|
);
|
||||||
|
if memory_summary.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let base_path = base_path.display().to_string();
|
||||||
|
MEMORY_TOOL_DEVELOPER_INSTRUCTIONS_TEMPLATE
|
||||||
|
.render([
|
||||||
|
("base_path", base_path.as_str()),
|
||||||
|
("memory_summary", memory_summary.as_str()),
|
||||||
|
])
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "prompts_tests.rs"]
|
||||||
|
mod tests;
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
use super::*;
|
||||||
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use tempfile::tempdir;
|
||||||
|
use tokio::fs as tokio_fs;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn build_memory_tool_developer_instructions_renders_embedded_template() {
|
||||||
|
let temp = tempdir().unwrap();
|
||||||
|
let codex_home = AbsolutePathBuf::from_absolute_path(temp.path()).unwrap();
|
||||||
|
let memories_dir = codex_home.join("memories");
|
||||||
|
tokio_fs::create_dir_all(&memories_dir).await.unwrap();
|
||||||
|
tokio_fs::write(
|
||||||
|
memories_dir.join("memory_summary.md"),
|
||||||
|
"Short memory summary for tests.",
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let instructions = build_memory_tool_developer_instructions(&codex_home)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(instructions.contains(&format!(
|
||||||
|
"- {}/memory_summary.md (already provided below; do NOT open again)",
|
||||||
|
memories_dir.display()
|
||||||
|
)));
|
||||||
|
assert!(instructions.contains("Short memory summary for tests."));
|
||||||
|
assert_eq!(
|
||||||
|
instructions
|
||||||
|
.matches("========= MEMORY_SUMMARY BEGINS =========")
|
||||||
|
.count(),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
pub const MEMORIES_USAGE_METRIC: &str = "codex.memories.usage";
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub enum MemoriesUsageKind {
|
||||||
|
MemoryMd,
|
||||||
|
MemorySummary,
|
||||||
|
RawMemories,
|
||||||
|
RolloutSummaries,
|
||||||
|
Skills,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoriesUsageKind {
|
||||||
|
pub 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 fn memories_usage_kinds_from_command(command: &[String]) -> Vec<MemoriesUsageKind> {
|
||||||
|
if !is_known_safe_command(command) {
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_command(command)
|
||||||
|
.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 get_memory_kind(path: String) -> Option<MemoriesUsageKind> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
File renamed without changes.
@@ -0,0 +1,9 @@
|
|||||||
|
load("//:defs.bzl", "codex_rust_crate")
|
||||||
|
|
||||||
|
codex_rust_crate(
|
||||||
|
name = "write",
|
||||||
|
crate_name = "codex_memories_write",
|
||||||
|
compile_data = glob([
|
||||||
|
"templates/**",
|
||||||
|
]),
|
||||||
|
)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
[package]
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
name = "codex-memories-write"
|
||||||
|
version.workspace = true
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "codex_memories_write"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = { workspace = true }
|
||||||
|
chrono = { workspace = true }
|
||||||
|
codex-git-utils = { workspace = true }
|
||||||
|
codex-protocol = { workspace = true }
|
||||||
|
codex-state = { workspace = true }
|
||||||
|
codex-utils-absolute-path = { workspace = true }
|
||||||
|
codex-utils-output-truncation = { workspace = true }
|
||||||
|
codex-utils-template = { workspace = true }
|
||||||
|
tokio = { workspace = true, features = ["fs"] }
|
||||||
|
tracing = { workspace = true, features = ["log"] }
|
||||||
|
uuid = { workspace = true, features = ["v4", "v5"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
codex-models-manager = { workspace = true }
|
||||||
|
pretty_assertions = { workspace = true }
|
||||||
|
tempfile = { workspace = true }
|
||||||
|
tokio = { workspace = true, features = ["fs", "macros"] }
|
||||||
File renamed without changes.
+3
-3
@@ -1,4 +1,4 @@
|
|||||||
use crate::memories::memory_extensions_root;
|
use crate::memory_extensions_root;
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
use chrono::Duration;
|
use chrono::Duration;
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
@@ -7,9 +7,9 @@ use std::path::Path;
|
|||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
const FILENAME_TS_FORMAT: &str = "%Y-%m-%dT%H-%M-%S";
|
const FILENAME_TS_FORMAT: &str = "%Y-%m-%dT%H-%M-%S";
|
||||||
pub(super) const EXTENSION_RESOURCE_RETENTION_DAYS: i64 = 7;
|
const EXTENSION_RESOURCE_RETENTION_DAYS: i64 = 7;
|
||||||
|
|
||||||
pub(super) async fn prune_old_extension_resources(memory_root: &Path) {
|
pub async fn prune_old_extension_resources(memory_root: &Path) {
|
||||||
prune_old_extension_resources_with_now(memory_root, Utc::now()).await
|
prune_old_extension_resources_with_now(memory_root, Utc::now()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
File renamed without changes.
@@ -0,0 +1,63 @@
|
|||||||
|
//! Write-path helpers for Codex memories.
|
||||||
|
//!
|
||||||
|
//! This crate owns the file-backed memory artifact helpers, Phase 1 and Phase
|
||||||
|
//! 2 prompt rendering, extension pruning, and workspace diffing. Runtime
|
||||||
|
//! orchestration for Phase 1 and Phase 2 remains in `codex-core`.
|
||||||
|
|
||||||
|
mod control;
|
||||||
|
mod extensions;
|
||||||
|
mod prompts;
|
||||||
|
mod storage;
|
||||||
|
pub mod workspace;
|
||||||
|
|
||||||
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub use control::clear_memory_roots_contents;
|
||||||
|
pub use extensions::prune_old_extension_resources;
|
||||||
|
pub use prompts::build_consolidation_prompt;
|
||||||
|
pub use prompts::build_stage_one_input_message;
|
||||||
|
pub use storage::rebuild_raw_memories_file_from_memories;
|
||||||
|
pub use storage::rollout_summary_file_stem;
|
||||||
|
pub use storage::sync_rollout_summaries_from_memories;
|
||||||
|
|
||||||
|
/// Prompt used for phase 1 extraction.
|
||||||
|
pub const STAGE_ONE_PROMPT: &str = include_str!("../templates/memories/stage_one_system.md");
|
||||||
|
|
||||||
|
/// Fallback stage-1 rollout truncation limit (tokens) when model metadata
|
||||||
|
/// does not include a valid context window.
|
||||||
|
pub const DEFAULT_STAGE_ONE_ROLLOUT_TOKEN_LIMIT: usize = 150_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 const STAGE_ONE_CONTEXT_WINDOW_PERCENT: i64 = 70;
|
||||||
|
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn memory_root(codex_home: &AbsolutePathBuf) -> AbsolutePathBuf {
|
||||||
|
codex_home.join("memories")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rollout_summaries_dir(root: &Path) -> PathBuf {
|
||||||
|
root.join(artifacts::ROLLOUT_SUMMARIES_SUBDIR)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn memory_extensions_root(root: &Path) -> PathBuf {
|
||||||
|
root.join(artifacts::EXTENSIONS_SUBDIR)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn raw_memories_file(root: &Path) -> PathBuf {
|
||||||
|
root.join(artifacts::RAW_MEMORIES_FILENAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn ensure_layout(root: &Path) -> std::io::Result<()> {
|
||||||
|
tokio::fs::create_dir_all(rollout_summaries_dir(root)).await
|
||||||
|
}
|
||||||
@@ -1,35 +1,27 @@
|
|||||||
use crate::memories::memory_extensions_root;
|
use crate::DEFAULT_STAGE_ONE_ROLLOUT_TOKEN_LIMIT;
|
||||||
use crate::memories::memory_root;
|
use crate::STAGE_ONE_CONTEXT_WINDOW_PERCENT;
|
||||||
use crate::memories::phase_one;
|
use crate::memory_extensions_root;
|
||||||
use crate::memories::workspace::WORKSPACE_DIFF_FILENAME;
|
use crate::workspace::WORKSPACE_DIFF_FILENAME;
|
||||||
use codex_protocol::openai_models::ModelInfo;
|
use codex_protocol::openai_models::ModelInfo;
|
||||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
|
||||||
use codex_utils_output_truncation::TruncationPolicy;
|
use codex_utils_output_truncation::TruncationPolicy;
|
||||||
use codex_utils_output_truncation::truncate_text;
|
use codex_utils_output_truncation::truncate_text;
|
||||||
use codex_utils_template::Template;
|
use codex_utils_template::Template;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
use tokio::fs;
|
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
static CONSOLIDATION_PROMPT_TEMPLATE: LazyLock<Template> = LazyLock::new(|| {
|
static CONSOLIDATION_PROMPT_TEMPLATE: LazyLock<Template> = LazyLock::new(|| {
|
||||||
parse_embedded_template(
|
parse_embedded_template(
|
||||||
include_str!("../../templates/memories/consolidation.md"),
|
include_str!("../templates/memories/consolidation.md"),
|
||||||
"memories/consolidation.md",
|
"memories/consolidation.md",
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
static STAGE_ONE_INPUT_TEMPLATE: LazyLock<Template> = LazyLock::new(|| {
|
static STAGE_ONE_INPUT_TEMPLATE: LazyLock<Template> = LazyLock::new(|| {
|
||||||
parse_embedded_template(
|
parse_embedded_template(
|
||||||
include_str!("../../templates/memories/stage_one_input.md"),
|
include_str!("../templates/memories/stage_one_input.md"),
|
||||||
"memories/stage_one_input.md",
|
"memories/stage_one_input.md",
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
static MEMORY_TOOL_DEVELOPER_INSTRUCTIONS_TEMPLATE: LazyLock<Template> = LazyLock::new(|| {
|
|
||||||
parse_embedded_template(
|
|
||||||
include_str!("../../templates/memories/read_path.md"),
|
|
||||||
"memories/read_path.md",
|
|
||||||
)
|
|
||||||
});
|
|
||||||
static MEMORY_EXTENSIONS_FOLDER_STRUCTURE_TEMPLATE: LazyLock<Template> = LazyLock::new(|| {
|
static MEMORY_EXTENSIONS_FOLDER_STRUCTURE_TEMPLATE: LazyLock<Template> = LazyLock::new(|| {
|
||||||
parse_embedded_template(
|
parse_embedded_template(
|
||||||
MEMORY_EXTENSIONS_FOLDER_STRUCTURE,
|
MEMORY_EXTENSIONS_FOLDER_STRUCTURE,
|
||||||
@@ -77,7 +69,7 @@ signal to remove stale memories derived only from those resources.
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
/// Builds the consolidation subagent prompt for a specific memory root.
|
/// Builds the consolidation subagent prompt for a specific memory root.
|
||||||
pub(super) fn build_consolidation_prompt(memory_root: &Path) -> String {
|
pub fn build_consolidation_prompt(memory_root: &Path) -> String {
|
||||||
let memory_extensions_root = memory_extensions_root(memory_root);
|
let memory_extensions_root = memory_extensions_root(memory_root);
|
||||||
let memory_extensions_exist = memory_extensions_root.is_dir();
|
let memory_extensions_exist = memory_extensions_root.is_dir();
|
||||||
let memory_root = memory_root.display().to_string();
|
let memory_root = memory_root.display().to_string();
|
||||||
@@ -136,7 +128,7 @@ fn render_memory_extensions_block(template: &Template, memory_extensions_root: &
|
|||||||
///
|
///
|
||||||
/// Large rollout payloads are truncated to 70% of the active model's effective
|
/// Large rollout payloads are truncated to 70% of the active model's effective
|
||||||
/// input window token budget while keeping both head and tail context.
|
/// input window token budget while keeping both head and tail context.
|
||||||
pub(super) fn build_stage_one_input_message(
|
pub fn build_stage_one_input_message(
|
||||||
model_info: &ModelInfo,
|
model_info: &ModelInfo,
|
||||||
rollout_path: &Path,
|
rollout_path: &Path,
|
||||||
rollout_cwd: &Path,
|
rollout_cwd: &Path,
|
||||||
@@ -146,9 +138,9 @@ pub(super) fn build_stage_one_input_message(
|
|||||||
.resolved_context_window()
|
.resolved_context_window()
|
||||||
.and_then(|limit| (limit > 0).then_some(limit))
|
.and_then(|limit| (limit > 0).then_some(limit))
|
||||||
.map(|limit| limit.saturating_mul(model_info.effective_context_window_percent) / 100)
|
.map(|limit| limit.saturating_mul(model_info.effective_context_window_percent) / 100)
|
||||||
.map(|limit| (limit.saturating_mul(phase_one::CONTEXT_WINDOW_PERCENT) / 100).max(1))
|
.map(|limit| (limit.saturating_mul(STAGE_ONE_CONTEXT_WINDOW_PERCENT) / 100).max(1))
|
||||||
.and_then(|limit| usize::try_from(limit).ok())
|
.and_then(|limit| usize::try_from(limit).ok())
|
||||||
.unwrap_or(phase_one::DEFAULT_STAGE_ONE_ROLLOUT_TOKEN_LIMIT);
|
.unwrap_or(DEFAULT_STAGE_ONE_ROLLOUT_TOKEN_LIMIT);
|
||||||
let truncated_rollout_contents = truncate_text(
|
let truncated_rollout_contents = truncate_text(
|
||||||
rollout_contents,
|
rollout_contents,
|
||||||
TruncationPolicy::Tokens(rollout_token_limit),
|
TruncationPolicy::Tokens(rollout_token_limit),
|
||||||
@@ -163,35 +155,6 @@ pub(super) fn build_stage_one_input_message(
|
|||||||
])?)
|
])?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build prompt used for read path. This prompt must be added to the developer instructions. In
|
|
||||||
/// case of large memory files, the `memory_summary.md` is truncated at
|
|
||||||
/// [phase_one::MEMORY_TOOL_DEVELOPER_INSTRUCTIONS_SUMMARY_TOKEN_LIMIT].
|
|
||||||
pub(crate) async fn build_memory_tool_developer_instructions(
|
|
||||||
codex_home: &AbsolutePathBuf,
|
|
||||||
) -> Option<String> {
|
|
||||||
let base_path = memory_root(codex_home);
|
|
||||||
let memory_summary_path = base_path.join("memory_summary.md");
|
|
||||||
let memory_summary = fs::read_to_string(&memory_summary_path)
|
|
||||||
.await
|
|
||||||
.ok()?
|
|
||||||
.trim()
|
|
||||||
.to_string();
|
|
||||||
let memory_summary = truncate_text(
|
|
||||||
&memory_summary,
|
|
||||||
TruncationPolicy::Tokens(phase_one::MEMORY_TOOL_DEVELOPER_INSTRUCTIONS_SUMMARY_TOKEN_LIMIT),
|
|
||||||
);
|
|
||||||
if memory_summary.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let base_path = base_path.display().to_string();
|
|
||||||
MEMORY_TOOL_DEVELOPER_INSTRUCTIONS_TEMPLATE
|
|
||||||
.render([
|
|
||||||
("base_path", base_path.as_str()),
|
|
||||||
("memory_summary", memory_summary.as_str()),
|
|
||||||
])
|
|
||||||
.ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "prompts_tests.rs"]
|
#[path = "prompts_tests.rs"]
|
||||||
mod tests;
|
mod tests;
|
||||||
+2
-35
@@ -1,9 +1,6 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use codex_models_manager::model_info::model_info_from_slug;
|
use codex_models_manager::model_info::model_info_from_slug;
|
||||||
use core_test_support::PathExt;
|
|
||||||
use pretty_assertions::assert_eq;
|
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
use tokio::fs as tokio_fs;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn build_stage_one_input_message_truncates_rollout_using_model_context_window() {
|
fn build_stage_one_input_message_truncates_rollout_using_model_context_window() {
|
||||||
@@ -12,7 +9,7 @@ fn build_stage_one_input_message_truncates_rollout_using_model_context_window()
|
|||||||
model_info.context_window = Some(123_000);
|
model_info.context_window = Some(123_000);
|
||||||
let expected_rollout_token_limit = usize::try_from(
|
let expected_rollout_token_limit = usize::try_from(
|
||||||
((123_000_i64 * model_info.effective_context_window_percent) / 100)
|
((123_000_i64 * model_info.effective_context_window_percent) / 100)
|
||||||
* phase_one::CONTEXT_WINDOW_PERCENT
|
* STAGE_ONE_CONTEXT_WINDOW_PERCENT
|
||||||
/ 100,
|
/ 100,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -42,7 +39,7 @@ fn build_stage_one_input_message_uses_default_limit_when_model_context_window_mi
|
|||||||
model_info.max_context_window = None;
|
model_info.max_context_window = None;
|
||||||
let expected_truncated = truncate_text(
|
let expected_truncated = truncate_text(
|
||||||
&input,
|
&input,
|
||||||
TruncationPolicy::Tokens(phase_one::DEFAULT_STAGE_ONE_ROLLOUT_TOKEN_LIMIT),
|
TruncationPolicy::Tokens(DEFAULT_STAGE_ONE_ROLLOUT_TOKEN_LIMIT),
|
||||||
);
|
);
|
||||||
let message = build_stage_one_input_message(
|
let message = build_stage_one_input_message(
|
||||||
&model_info,
|
&model_info,
|
||||||
@@ -72,33 +69,3 @@ fn build_consolidation_prompt_points_to_workspace_diff_and_extension_tree() {
|
|||||||
)));
|
)));
|
||||||
assert!(prompt.contains("workspace diff shows deleted extension resource files"));
|
assert!(prompt.contains("workspace diff shows deleted extension resource files"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn build_memory_tool_developer_instructions_renders_embedded_template() {
|
|
||||||
let temp = tempdir().unwrap();
|
|
||||||
let codex_home = temp.path().abs();
|
|
||||||
let memories_dir = codex_home.join("memories");
|
|
||||||
tokio_fs::create_dir_all(&memories_dir).await.unwrap();
|
|
||||||
tokio_fs::write(
|
|
||||||
memories_dir.join("memory_summary.md"),
|
|
||||||
"Short memory summary for tests.",
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let instructions = build_memory_tool_developer_instructions(&codex_home)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(instructions.contains(&format!(
|
|
||||||
"- {}/memory_summary.md (already provided below; do NOT open again)",
|
|
||||||
memories_dir.display()
|
|
||||||
)));
|
|
||||||
assert!(instructions.contains("Short memory summary for tests."));
|
|
||||||
assert_eq!(
|
|
||||||
instructions
|
|
||||||
.matches("========= MEMORY_SUMMARY BEGINS =========")
|
|
||||||
.count(),
|
|
||||||
1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -5,12 +5,12 @@ use std::path::Path;
|
|||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::memories::ensure_layout;
|
use crate::ensure_layout;
|
||||||
use crate::memories::raw_memories_file;
|
use crate::raw_memories_file;
|
||||||
use crate::memories::rollout_summaries_dir;
|
use crate::rollout_summaries_dir;
|
||||||
|
|
||||||
/// Rebuild `raw_memories.md` from DB-backed stage-1 outputs.
|
/// Rebuild `raw_memories.md` from DB-backed stage-1 outputs.
|
||||||
pub(super) async fn rebuild_raw_memories_file_from_memories(
|
pub async fn rebuild_raw_memories_file_from_memories(
|
||||||
root: &Path,
|
root: &Path,
|
||||||
memories: &[Stage1Output],
|
memories: &[Stage1Output],
|
||||||
max_raw_memories_for_consolidation: usize,
|
max_raw_memories_for_consolidation: usize,
|
||||||
@@ -20,7 +20,7 @@ pub(super) async fn rebuild_raw_memories_file_from_memories(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Syncs canonical rollout summary files from DB-backed stage-1 output rows.
|
/// Syncs canonical rollout summary files from DB-backed stage-1 output rows.
|
||||||
pub(super) async fn sync_rollout_summaries_from_memories(
|
pub async fn sync_rollout_summaries_from_memories(
|
||||||
root: &Path,
|
root: &Path,
|
||||||
memories: &[Stage1Output],
|
memories: &[Stage1Output],
|
||||||
max_raw_memories_for_consolidation: usize,
|
max_raw_memories_for_consolidation: usize,
|
||||||
@@ -150,7 +150,7 @@ fn rollout_summary_format_error(err: std::fmt::Error) -> std::io::Error {
|
|||||||
std::io::Error::other(format!("format rollout summary: {err}"))
|
std::io::Error::other(format!("format rollout summary: {err}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn rollout_summary_file_stem(memory: &Stage1Output) -> String {
|
pub fn rollout_summary_file_stem(memory: &Stage1Output) -> String {
|
||||||
rollout_summary_file_stem_from_parts(
|
rollout_summary_file_stem_from_parts(
|
||||||
memory.thread_id,
|
memory.thread_id,
|
||||||
memory.source_updated_at,
|
memory.source_updated_at,
|
||||||
@@ -158,7 +158,7 @@ pub(crate) fn rollout_summary_file_stem(memory: &Stage1Output) -> String {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn rollout_summary_file_stem_from_parts(
|
fn rollout_summary_file_stem_from_parts(
|
||||||
thread_id: codex_protocol::ThreadId,
|
thread_id: codex_protocol::ThreadId,
|
||||||
source_updated_at: chrono::DateTime<chrono::Utc>,
|
source_updated_at: chrono::DateTime<chrono::Utc>,
|
||||||
rollout_slug: Option<&str>,
|
rollout_slug: Option<&str>,
|
||||||
-9
@@ -1,5 +1,4 @@
|
|||||||
use super::rollout_summary_file_stem;
|
use super::rollout_summary_file_stem;
|
||||||
use super::rollout_summary_file_stem_from_parts;
|
|
||||||
use chrono::TimeZone;
|
use chrono::TimeZone;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use codex_protocol::ThreadId;
|
use codex_protocol::ThreadId;
|
||||||
@@ -32,14 +31,6 @@ fn rollout_summary_file_stem_uses_uuid_timestamp_and_hash_when_slug_missing() {
|
|||||||
let memory = stage1_output_with_slug(thread_id, /*rollout_slug*/ None);
|
let memory = stage1_output_with_slug(thread_id, /*rollout_slug*/ None);
|
||||||
|
|
||||||
assert_eq!(rollout_summary_file_stem(&memory), FIXED_PREFIX);
|
assert_eq!(rollout_summary_file_stem(&memory), FIXED_PREFIX);
|
||||||
assert_eq!(
|
|
||||||
rollout_summary_file_stem_from_parts(
|
|
||||||
memory.thread_id,
|
|
||||||
memory.source_updated_at,
|
|
||||||
memory.rollout_slug.as_deref(),
|
|
||||||
),
|
|
||||||
FIXED_PREFIX
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -6,7 +6,7 @@ use codex_git_utils::reset_git_repository;
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
/// Generated diff file the Phase 2 consolidation agent reads before editing memories.
|
/// Generated diff file the Phase 2 consolidation agent reads before editing memories.
|
||||||
pub(super) const WORKSPACE_DIFF_FILENAME: &str = "phase2_workspace_diff.md";
|
pub const WORKSPACE_DIFF_FILENAME: &str = "phase2_workspace_diff.md";
|
||||||
|
|
||||||
const WORKSPACE_DIFF_MAX_BYTES: usize = 4 * 1024 * 1024;
|
const WORKSPACE_DIFF_MAX_BYTES: usize = 4 * 1024 * 1024;
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ const WORKSPACE_DIFF_MAX_BYTES: usize = 4 * 1024 * 1024;
|
|||||||
/// This keeps an existing usable `.git/` baseline intact. It initializes a new git baseline when the
|
/// This keeps an existing usable `.git/` baseline intact. It initializes a new git baseline when the
|
||||||
/// metadata is missing or unusable, and removes any stale generated `phase2_workspace_diff.md` file
|
/// metadata is missing or unusable, and removes any stale generated `phase2_workspace_diff.md` file
|
||||||
/// so that the next diff does not include a previous prompt artifact.
|
/// so that the next diff does not include a previous prompt artifact.
|
||||||
pub(super) async fn prepare_memory_workspace(root: &Path) -> anyhow::Result<()> {
|
pub async fn prepare_memory_workspace(root: &Path) -> anyhow::Result<()> {
|
||||||
tokio::fs::create_dir_all(root)
|
tokio::fs::create_dir_all(root)
|
||||||
.await
|
.await
|
||||||
.with_context(|| format!("create memory workspace {}", root.display()))?;
|
.with_context(|| format!("create memory workspace {}", root.display()))?;
|
||||||
@@ -28,16 +28,13 @@ pub(super) async fn prepare_memory_workspace(root: &Path) -> anyhow::Result<()>
|
|||||||
///
|
///
|
||||||
/// The removed file is only `phase2_workspace_diff.md`; memory artifacts and `.git/` metadata are
|
/// The removed file is only `phase2_workspace_diff.md`; memory artifacts and `.git/` metadata are
|
||||||
/// left intact.
|
/// left intact.
|
||||||
pub(super) async fn memory_workspace_diff(root: &Path) -> anyhow::Result<GitBaselineDiff> {
|
pub async fn memory_workspace_diff(root: &Path) -> anyhow::Result<GitBaselineDiff> {
|
||||||
remove_workspace_diff(root).await?;
|
remove_workspace_diff(root).await?;
|
||||||
diff_since_latest_init(root).await
|
diff_since_latest_init(root).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes `phase2_workspace_diff.md` with a bounded git-style diff from the current baseline.
|
/// Writes `phase2_workspace_diff.md` with a bounded git-style diff from the current baseline.
|
||||||
pub(super) async fn write_workspace_diff(
|
pub async fn write_workspace_diff(root: &Path, diff: &GitBaselineDiff) -> anyhow::Result<()> {
|
||||||
root: &Path,
|
|
||||||
diff: &GitBaselineDiff,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let path = root.join(WORKSPACE_DIFF_FILENAME);
|
let path = root.join(WORKSPACE_DIFF_FILENAME);
|
||||||
tokio::fs::write(&path, render_workspace_diff_file(diff))
|
tokio::fs::write(&path, render_workspace_diff_file(diff))
|
||||||
.await
|
.await
|
||||||
@@ -48,7 +45,7 @@ pub(super) async fn write_workspace_diff(
|
|||||||
///
|
///
|
||||||
/// The generated diff file is removed before resetting the baseline so deleted memory content is
|
/// The generated diff file is removed before resetting the baseline so deleted memory content is
|
||||||
/// not retained in the prompt artifact or in unreachable git objects.
|
/// not retained in the prompt artifact or in unreachable git objects.
|
||||||
pub(super) async fn reset_memory_workspace_baseline(root: &Path) -> anyhow::Result<()> {
|
pub async fn reset_memory_workspace_baseline(root: &Path) -> anyhow::Result<()> {
|
||||||
remove_workspace_diff(root).await?;
|
remove_workspace_diff(root).await?;
|
||||||
reset_git_repository(root).await
|
reset_git_repository(root).await
|
||||||
}
|
}
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
Reference in new issue
Block a user