mirror of
https://github.com/openai/codex.git
synced 2026-11-03 00:21:31 +00:00
Add cwd to memory files (#11591)
Add cwd to memory files so that model can deal with multi cwd memory better. --------- Co-authored-by: jif-oai <jif@openai.com>
This commit is contained in:
1 parent
82acd815e4
commit
88c5ca2573
7 files changed
+29
-3
No files matched your search
@@ -284,6 +284,7 @@ mod tests {
|
|||||||
use codex_state::Stage1Output;
|
use codex_state::Stage1Output;
|
||||||
use codex_state::ThreadMetadataBuilder;
|
use codex_state::ThreadMetadataBuilder;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
@@ -402,6 +403,7 @@ mod tests {
|
|||||||
.expect("valid source_updated_at timestamp"),
|
.expect("valid source_updated_at timestamp"),
|
||||||
raw_memory: "raw memory".to_string(),
|
raw_memory: "raw memory".to_string(),
|
||||||
rollout_summary: "rollout summary".to_string(),
|
rollout_summary: "rollout summary".to_string(),
|
||||||
|
cwd: PathBuf::from("/tmp/workspace"),
|
||||||
generated_at: chrono::DateTime::<Utc>::from_timestamp(124, 0)
|
generated_at: chrono::DateTime::<Utc>::from_timestamp(124, 0)
|
||||||
.expect("valid generated_at timestamp"),
|
.expect("valid generated_at timestamp"),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ async fn rebuild_raw_memories_file(root: &Path, memories: &[Stage1Output]) -> st
|
|||||||
memory.source_updated_at.to_rfc3339()
|
memory.source_updated_at.to_rfc3339()
|
||||||
)
|
)
|
||||||
.map_err(|err| std::io::Error::other(format!("format raw memories: {err}")))?;
|
.map_err(|err| std::io::Error::other(format!("format raw memories: {err}")))?;
|
||||||
|
writeln!(body, "cwd: {}", memory.cwd.display())
|
||||||
|
.map_err(|err| std::io::Error::other(format!("format raw memories: {err}")))?;
|
||||||
writeln!(body)
|
writeln!(body)
|
||||||
.map_err(|err| std::io::Error::other(format!("format raw memories: {err}")))?;
|
.map_err(|err| std::io::Error::other(format!("format raw memories: {err}")))?;
|
||||||
body.push_str(memory.raw_memory.trim());
|
body.push_str(memory.raw_memory.trim());
|
||||||
@@ -138,6 +140,8 @@ async fn write_rollout_summary_for_thread(
|
|||||||
memory.source_updated_at.to_rfc3339()
|
memory.source_updated_at.to_rfc3339()
|
||||||
)
|
)
|
||||||
.map_err(|err| std::io::Error::other(format!("format rollout summary: {err}")))?;
|
.map_err(|err| std::io::Error::other(format!("format rollout summary: {err}")))?;
|
||||||
|
writeln!(body, "cwd: {}", memory.cwd.display())
|
||||||
|
.map_err(|err| std::io::Error::other(format!("format rollout summary: {err}")))?;
|
||||||
writeln!(body)
|
writeln!(body)
|
||||||
.map_err(|err| std::io::Error::other(format!("format rollout summary: {err}")))?;
|
.map_err(|err| std::io::Error::other(format!("format rollout summary: {err}")))?;
|
||||||
body.push_str(&memory.rollout_summary);
|
body.push_str(&memory.rollout_summary);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use codex_protocol::ThreadId;
|
|||||||
use codex_state::Stage1Output;
|
use codex_state::Stage1Output;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
use std::path::PathBuf;
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -65,6 +66,7 @@ async fn sync_rollout_summaries_and_raw_memories_file_keeps_latest_memories_only
|
|||||||
source_updated_at: Utc.timestamp_opt(100, 0).single().expect("timestamp"),
|
source_updated_at: Utc.timestamp_opt(100, 0).single().expect("timestamp"),
|
||||||
raw_memory: "raw memory".to_string(),
|
raw_memory: "raw memory".to_string(),
|
||||||
rollout_summary: "short summary".to_string(),
|
rollout_summary: "short summary".to_string(),
|
||||||
|
cwd: PathBuf::from("/tmp/workspace"),
|
||||||
generated_at: Utc.timestamp_opt(101, 0).single().expect("timestamp"),
|
generated_at: Utc.timestamp_opt(101, 0).single().expect("timestamp"),
|
||||||
}];
|
}];
|
||||||
|
|
||||||
@@ -83,4 +85,5 @@ async fn sync_rollout_summaries_and_raw_memories_file_keeps_latest_memories_only
|
|||||||
.expect("read raw memories");
|
.expect("read raw memories");
|
||||||
assert!(raw_memories.contains("raw memory"));
|
assert!(raw_memories.contains("raw memory"));
|
||||||
assert!(raw_memories.contains(&keep_id));
|
assert!(raw_memories.contains(&keep_id));
|
||||||
|
assert!(raw_memories.contains("cwd: /tmp/workspace"));
|
||||||
}
|
}
|
||||||
@@ -97,6 +97,8 @@ Primary inputs (always read these, if exists):
|
|||||||
Under `{{ memory_root }}/`:
|
Under `{{ memory_root }}/`:
|
||||||
- `raw_memories.md`
|
- `raw_memories.md`
|
||||||
- mechanical merge of `raw_memories` from Phase 1;
|
- mechanical merge of `raw_memories` from Phase 1;
|
||||||
|
- source of rollout-level metadata needed for MEMORY.md header annotations;
|
||||||
|
you should be able to find `cwd` and `updated_at` there.
|
||||||
- `MEMORY.md`
|
- `MEMORY.md`
|
||||||
- merged memories; produce a lightly clustered version if applicable
|
- merged memories; produce a lightly clustered version if applicable
|
||||||
- `rollout_summaries/*.md`
|
- `rollout_summaries/*.md`
|
||||||
@@ -129,8 +131,8 @@ Rules:
|
|||||||
Clustered schema:
|
Clustered schema:
|
||||||
---
|
---
|
||||||
rollout_summary_files:
|
rollout_summary_files:
|
||||||
- <file1.md> (<a few words annotation such as "success, most useful" or "uncertain, no user feedback">)
|
- <file1.md> (<annotation that includes status/usefulness, cwd, and updated_at, e.g. "success, most useful architecture walkthrough, cwd=/repo/path, updated_at=2026-02-12T10:30:00Z">)
|
||||||
- <file2.md> (<annotation>)
|
- <file2.md> (<annotation with cwd=/..., updated_at=...>)
|
||||||
description: brief description of the shared tasks/outcomes
|
description: brief description of the shared tasks/outcomes
|
||||||
keywords: k1, k2, k3, ... <searchable handles (tool names, error names, repo concepts, contracts)>
|
keywords: k1, k2, k3, ... <searchable handles (tool names, error names, repo concepts, contracts)>
|
||||||
---
|
---
|
||||||
@@ -141,6 +143,9 @@ keywords: k1, k2, k3, ... <searchable handles (tool names, error names, repo con
|
|||||||
Schema rules (strict):
|
Schema rules (strict):
|
||||||
- Keep entries compact and retrieval-friendly.
|
- Keep entries compact and retrieval-friendly.
|
||||||
- A single note block may correspond to multiple related tasks; aggregate when tasks and lessons align.
|
- A single note block may correspond to multiple related tasks; aggregate when tasks and lessons align.
|
||||||
|
- In `rollout_summary_files`, each parenthesized annotation must include
|
||||||
|
`cwd=<path>` and `updated_at=<timestamp>` copied from that rollout summary metadata.
|
||||||
|
If missing from an individual rollout summary, recover them from `raw_memories.md`.
|
||||||
- If you need to reference skills, do it in the BODY as bullets, not in the header
|
- If you need to reference skills, do it in the BODY as bullets, not in the header
|
||||||
(e.g., "- Related skill: skills/<skill-name>/SKILL.md").
|
(e.g., "- Related skill: skills/<skill-name>/SKILL.md").
|
||||||
- Use lowercase, hyphenated skill folder names.
|
- Use lowercase, hyphenated skill folder names.
|
||||||
@@ -338,4 +343,4 @@ Use `rg` for fast retrieval while consolidating:
|
|||||||
- Search across memory tree:
|
- Search across memory tree:
|
||||||
`rg -n -i "<pattern>" "{{ memory_root }}" | head -n 50`
|
`rg -n -i "<pattern>" "{{ memory_root }}" | head -n 50`
|
||||||
- Locate rollout summary files:
|
- Locate rollout summary files:
|
||||||
`rg --files "{{ memory_root }}/rollout_summaries" | head -n 200`
|
`rg --files "{{ memory_root }}/rollout_summaries" | head -n 200`
|
||||||
@@ -4,6 +4,7 @@ use chrono::Utc;
|
|||||||
use codex_protocol::ThreadId;
|
use codex_protocol::ThreadId;
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
use sqlx::sqlite::SqliteRow;
|
use sqlx::sqlite::SqliteRow;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use super::ThreadMetadata;
|
use super::ThreadMetadata;
|
||||||
|
|
||||||
@@ -14,6 +15,7 @@ pub struct Stage1Output {
|
|||||||
pub source_updated_at: DateTime<Utc>,
|
pub source_updated_at: DateTime<Utc>,
|
||||||
pub raw_memory: String,
|
pub raw_memory: String,
|
||||||
pub rollout_summary: String,
|
pub rollout_summary: String,
|
||||||
|
pub cwd: PathBuf,
|
||||||
pub generated_at: DateTime<Utc>,
|
pub generated_at: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,6 +25,7 @@ pub(crate) struct Stage1OutputRow {
|
|||||||
source_updated_at: i64,
|
source_updated_at: i64,
|
||||||
raw_memory: String,
|
raw_memory: String,
|
||||||
rollout_summary: String,
|
rollout_summary: String,
|
||||||
|
cwd: String,
|
||||||
generated_at: i64,
|
generated_at: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,6 +36,7 @@ impl Stage1OutputRow {
|
|||||||
source_updated_at: row.try_get("source_updated_at")?,
|
source_updated_at: row.try_get("source_updated_at")?,
|
||||||
raw_memory: row.try_get("raw_memory")?,
|
raw_memory: row.try_get("raw_memory")?,
|
||||||
rollout_summary: row.try_get("rollout_summary")?,
|
rollout_summary: row.try_get("rollout_summary")?,
|
||||||
|
cwd: row.try_get("cwd")?,
|
||||||
generated_at: row.try_get("generated_at")?,
|
generated_at: row.try_get("generated_at")?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -47,6 +51,7 @@ impl TryFrom<Stage1OutputRow> for Stage1Output {
|
|||||||
source_updated_at: epoch_seconds_to_datetime(row.source_updated_at)?,
|
source_updated_at: epoch_seconds_to_datetime(row.source_updated_at)?,
|
||||||
raw_memory: row.raw_memory,
|
raw_memory: row.raw_memory,
|
||||||
rollout_summary: row.rollout_summary,
|
rollout_summary: row.rollout_summary,
|
||||||
|
cwd: PathBuf::from(row.cwd),
|
||||||
generated_at: epoch_seconds_to_datetime(row.generated_at)?,
|
generated_at: epoch_seconds_to_datetime(row.generated_at)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2093,8 +2093,10 @@ WHERE kind = 'memory_stage1'
|
|||||||
assert_eq!(outputs.len(), 2);
|
assert_eq!(outputs.len(), 2);
|
||||||
assert_eq!(outputs[0].thread_id, thread_id_b);
|
assert_eq!(outputs[0].thread_id, thread_id_b);
|
||||||
assert_eq!(outputs[0].rollout_summary, "summary b");
|
assert_eq!(outputs[0].rollout_summary, "summary b");
|
||||||
|
assert_eq!(outputs[0].cwd, codex_home.join("workspace-b"));
|
||||||
assert_eq!(outputs[1].thread_id, thread_id_a);
|
assert_eq!(outputs[1].thread_id, thread_id_a);
|
||||||
assert_eq!(outputs[1].rollout_summary, "summary a");
|
assert_eq!(outputs[1].rollout_summary, "summary a");
|
||||||
|
assert_eq!(outputs[1].cwd, codex_home.join("workspace-a"));
|
||||||
|
|
||||||
let _ = tokio::fs::remove_dir_all(codex_home).await;
|
let _ = tokio::fs::remove_dir_all(codex_home).await;
|
||||||
}
|
}
|
||||||
@@ -2163,6 +2165,7 @@ VALUES (?, ?, ?, ?, ?)
|
|||||||
assert_eq!(outputs.len(), 1);
|
assert_eq!(outputs.len(), 1);
|
||||||
assert_eq!(outputs[0].thread_id, thread_id_non_empty);
|
assert_eq!(outputs[0].thread_id, thread_id_non_empty);
|
||||||
assert_eq!(outputs[0].rollout_summary, "summary");
|
assert_eq!(outputs[0].rollout_summary, "summary");
|
||||||
|
assert_eq!(outputs[0].cwd, codex_home.join("workspace-non-empty"));
|
||||||
|
|
||||||
let _ = tokio::fs::remove_dir_all(codex_home).await;
|
let _ = tokio::fs::remove_dir_all(codex_home).await;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,6 +178,7 @@ LEFT JOIN jobs
|
|||||||
///
|
///
|
||||||
/// Query behavior:
|
/// Query behavior:
|
||||||
/// - filters out rows where both `raw_memory` and `rollout_summary` are blank
|
/// - filters out rows where both `raw_memory` and `rollout_summary` are blank
|
||||||
|
/// - joins `threads` to include thread `cwd`
|
||||||
/// - orders by `source_updated_at DESC, thread_id DESC`
|
/// - orders by `source_updated_at DESC, thread_id DESC`
|
||||||
/// - applies `LIMIT n`
|
/// - applies `LIMIT n`
|
||||||
pub async fn list_stage1_outputs_for_global(
|
pub async fn list_stage1_outputs_for_global(
|
||||||
@@ -191,7 +192,10 @@ LEFT JOIN jobs
|
|||||||
let rows = sqlx::query(
|
let rows = sqlx::query(
|
||||||
r#"
|
r#"
|
||||||
SELECT so.thread_id, so.source_updated_at, so.raw_memory, so.rollout_summary, so.generated_at
|
SELECT so.thread_id, so.source_updated_at, so.raw_memory, so.rollout_summary, so.generated_at
|
||||||
|
, COALESCE(t.cwd, '') AS cwd
|
||||||
FROM stage1_outputs AS so
|
FROM stage1_outputs AS so
|
||||||
|
LEFT JOIN threads AS t
|
||||||
|
ON t.id = so.thread_id
|
||||||
WHERE length(trim(so.raw_memory)) > 0 OR length(trim(so.rollout_summary)) > 0
|
WHERE length(trim(so.raw_memory)) > 0 OR length(trim(so.rollout_summary)) > 0
|
||||||
ORDER BY so.source_updated_at DESC, so.thread_id DESC
|
ORDER BY so.source_updated_at DESC, so.thread_id DESC
|
||||||
LIMIT ?
|
LIMIT ?
|
||||||
|
|||||||
Reference in new issue
Block a user