feat: memory read path (#11459)
This commit is contained in:
parent
0697d43aba
commit
d4b2c230f1
4 changed files with 100 additions and 24 deletions
|
|
@ -2295,6 +2295,14 @@ impl Session {
|
|||
DeveloperInstructions::new(SEARCH_TOOL_DEVELOPER_INSTRUCTIONS.to_string()).into(),
|
||||
);
|
||||
}
|
||||
// Add developer instructions for memories.
|
||||
if let Some(memory_prompt) =
|
||||
memories::build_memory_tool_developer_instructions(&turn_context.config.codex_home)
|
||||
.await
|
||||
&& turn_context.features.enabled(Feature::MemoryTool)
|
||||
{
|
||||
items.push(DeveloperInstructions::new(memory_prompt).into());
|
||||
}
|
||||
// Add developer instructions from collaboration_mode if they exist and are non-empty
|
||||
let (collaboration_mode, base_instructions) = {
|
||||
let state = self.state.lock().await;
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ struct StageOneOutput {
|
|||
_rollout_slug: Option<String>,
|
||||
}
|
||||
|
||||
fn memory_root(codex_home: &Path) -> PathBuf {
|
||||
pub fn memory_root(codex_home: &Path) -> PathBuf {
|
||||
codex_home.join("memories")
|
||||
}
|
||||
|
||||
|
|
@ -77,6 +77,7 @@ async fn ensure_layout(root: &Path) -> std::io::Result<()> {
|
|||
tokio::fs::create_dir_all(rollout_summaries_dir(root)).await
|
||||
}
|
||||
|
||||
pub(crate) use prompts::build_memory_tool_developer_instructions;
|
||||
/// Starts the memory startup pipeline for eligible root sessions.
|
||||
///
|
||||
/// This is the single entrypoint that `codex` uses to trigger memory startup.
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use askama::Template;
|
||||
use std::path::Path;
|
||||
use tracing::warn;
|
||||
|
||||
use super::text::prefix_at_char_boundary;
|
||||
use super::text::suffix_at_char_boundary;
|
||||
use crate::memories::memory_root;
|
||||
use askama::Template;
|
||||
use std::path::Path;
|
||||
use tokio::fs;
|
||||
use tracing::warn;
|
||||
|
||||
// TODO(jif) use proper truncation
|
||||
const MAX_ROLLOUT_BYTES_FOR_PROMPT: usize = 100_000;
|
||||
|
|
@ -22,22 +23,24 @@ struct StageOneInputTemplate<'a> {
|
|||
rollout_contents: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "memory_tool/developer_instructions.md", escape = "none")]
|
||||
struct MemoryToolDeveloperInstructionsTemplate<'a> {
|
||||
base_path: &'a str,
|
||||
memory_summary: &'a str,
|
||||
}
|
||||
|
||||
/// Builds the consolidation subagent prompt for a specific memory root.
|
||||
///
|
||||
/// Falls back to a simple string replacement if Askama rendering fails.
|
||||
pub(super) fn build_consolidation_prompt(memory_root: &Path) -> String {
|
||||
let memory_root = memory_root.display().to_string();
|
||||
let template = ConsolidationPromptTemplate {
|
||||
memory_root: &memory_root,
|
||||
};
|
||||
match template.render() {
|
||||
Ok(prompt) => prompt,
|
||||
Err(err) => {
|
||||
warn!("failed to render memories consolidation prompt template: {err}");
|
||||
include_str!("../../templates/memories/consolidation.md")
|
||||
.replace("{{ memory_root }}", &memory_root)
|
||||
}
|
||||
}
|
||||
template.render().unwrap_or_else(|err| {
|
||||
warn!("failed to render memories consolidation prompt template: {err}");
|
||||
format!("## Memory Phase 2 (Consolidation)\nConsolidate Codex memories in: {memory_root}")
|
||||
})
|
||||
}
|
||||
|
||||
/// Builds the stage-1 user message containing rollout metadata and content.
|
||||
|
|
@ -65,17 +68,31 @@ pub(super) fn build_stage_one_input_message(
|
|||
rollout_cwd: &rollout_cwd,
|
||||
rollout_contents: &rollout_contents,
|
||||
};
|
||||
// TODO(jif) use askama
|
||||
match template.render() {
|
||||
Ok(prompt) => prompt,
|
||||
Err(err) => {
|
||||
warn!("failed to render memories stage-one input template: {err}");
|
||||
include_str!("../../templates/memories/stage_one_input.md")
|
||||
.replace("{{ rollout_path }}", &rollout_path)
|
||||
.replace("{{ rollout_cwd }}", &rollout_cwd)
|
||||
.replace("{{ rollout_contents }}", &rollout_contents)
|
||||
}
|
||||
template.render().unwrap_or_else(|err| {
|
||||
warn!("failed to render memories stage-one input template: {err}");
|
||||
format!(
|
||||
"Analyze this rollout and produce JSON with `raw_memory`, `rollout_summary`, and optional `rollout_slug`.\n\nrollout_context:\n- rollout_path: {rollout_path}\n- rollout_cwd: {rollout_cwd}\n\nrendered conversation:\n{rollout_contents}"
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn build_memory_tool_developer_instructions(codex_home: &Path) -> 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();
|
||||
if memory_summary.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let base_path = base_path.display().to_string();
|
||||
let template = MemoryToolDeveloperInstructionsTemplate {
|
||||
base_path: &base_path,
|
||||
memory_summary: &memory_summary,
|
||||
};
|
||||
template.render().ok()
|
||||
}
|
||||
|
||||
fn truncate_rollout_for_prompt(input: &str) -> (String, bool) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
## Memory
|
||||
|
||||
You have a memory folder with guidance from prior runs. This is high priority.
|
||||
Use it before repo inspection or other tool calls unless the task is truly trivial and irrelevant to the memory summary.
|
||||
Treat memory as guidance, not truth. The current tools, code, and environment are the source of truth.
|
||||
|
||||
Memory layout (general -> specific):
|
||||
- {{ base_path }}/memory_summary.md (already provided below; do NOT open again)
|
||||
- {{ base_path }}/MEMORY.md (searchable registry; primary file to query)
|
||||
- {{ base_path }}/skills/<skill-name>/ (skill folder)
|
||||
- SKILL.md (entrypoint instructions)
|
||||
- scripts/ (optional helper scripts)
|
||||
- examples/ (optional example outputs)
|
||||
- templates/ (optional templates)
|
||||
- {{ base_path }}/rollout_summaries/ (per-rollout recaps + evidence snippets)
|
||||
|
||||
Mandatory startup protocol (for any non-trivial and related task):
|
||||
1) Skim MEMORY_SUMMARY in this prompt and extract some relevant keywords that are relevant to the user task
|
||||
(e.g. repo name, component, error strings, tool names).
|
||||
2) Search MEMORY.md for those keywords and for any referenced rollout ids or summary files.
|
||||
3) If a **Related skills** pointer appears, open the skill folder:
|
||||
- Read {{ base_path }}/skills/<skill-name>/SKILL.md first.
|
||||
- Only open supporting files (scripts/examples/templates) if SKILL.md references them.
|
||||
4) If you find relevant rollout summary files, open the matching files.
|
||||
5) If nothing relevant is found, proceed without using memory.
|
||||
|
||||
Example for how to search memory (use shell tool):
|
||||
* Search notes example (fast + line numbers):
|
||||
`rg -n -i "<pattern>" "{{ base_path }}/MEMORY.md"`
|
||||
|
||||
* Search across memory (notes + skills + rollout summaries):
|
||||
`rg -n -i "<pattern>" "{{ base_path }}" | head -n 50`
|
||||
|
||||
* Open a rollout summary example (find by rollout_id, then read a slice):
|
||||
`rg --files "{{ base_path }}/rollout_summaries" | rg "<rollout_id>"`
|
||||
`sed -n '<START>,<END>p' "{{ base_path }}/rollout_summaries/<file>"`
|
||||
(Common slices: `sed -n '1,200p' ...` or `sed -n '200,400p' ...`)
|
||||
|
||||
* Open a skill entrypoint (read a slice):
|
||||
`sed -n '<START>,<END>p' "{{ base_path }}/skills/<skill-name>/SKILL.md"`
|
||||
* If SKILL.md references supporting files, open them directly by path.
|
||||
|
||||
During execution: if you hit repeated errors or confusion, return to memory and check MEMORY.md/skills/rollout_summaries again.
|
||||
If you found stale or contradicting guidance with the current environment, update the memory files accordingly.
|
||||
|
||||
========= MEMORY_SUMMARY BEGINS =========
|
||||
{{ memory_summary }}
|
||||
========= MEMORY_SUMMARY ENDS =========
|
||||
|
||||
Begin with the memory protocol.
|
||||
Loading…
Add table
Reference in a new issue