feat: split memories part 2 (#19860)

Keep extracting memories out of core and moving the write trigger in the
app-server
This is temporary and it should move at the client level as a follow-up
This makes core fully independant from `codex-memories-write`

---------

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
jif-oai
2026-04-28 13:03:28 +02:00
committed by GitHub
parent fd36838cf3
commit 431ebeaef7
47 changed files with 1932 additions and 2735 deletions

View File

@@ -758,12 +758,6 @@ pub enum Op {
/// to generate a summary which will be returned as an AgentMessage event.
Compact,
/// Drop all persisted memory artifacts and memory-tracking DB rows.
DropMemories,
/// Trigger a single pass of the startup memory pipeline.
UpdateMemories,
/// Set a user-facing thread name in the persisted rollout metadata.
/// This is a local-only operation handled by codex-core; it does not
/// involve the model.
@@ -906,8 +900,6 @@ impl Op {
Self::ReloadUserConfig => "reload_user_config",
Self::ListSkills { .. } => "list_skills",
Self::Compact => "compact",
Self::DropMemories => "drop_memories",
Self::UpdateMemories => "update_memories",
Self::SetThreadName { .. } => "set_thread_name",
Self::SetThreadMemoryMode { .. } => "set_thread_memory_mode",
Self::Undo => "undo",
@@ -2640,11 +2632,19 @@ pub enum SessionSource {
Exec,
Mcp,
Custom(String),
Internal(InternalSessionSource),
SubAgent(SubAgentSource),
#[serde(other)]
Unknown,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "snake_case")]
#[ts(rename_all = "snake_case")]
pub enum InternalSessionSource {
MemoryConsolidation,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "snake_case")]
#[ts(rename_all = "snake_case")]
@@ -2673,6 +2673,7 @@ impl fmt::Display for SessionSource {
SessionSource::Exec => f.write_str("exec"),
SessionSource::Mcp => f.write_str("mcp"),
SessionSource::Custom(source) => f.write_str(source),
SessionSource::Internal(source) => write!(f, "internal_{source}"),
SessionSource::SubAgent(sub_source) => write!(f, "subagent_{sub_source}"),
SessionSource::Unknown => f.write_str("unknown"),
}
@@ -2701,19 +2702,28 @@ impl SessionSource {
pub fn thread_source_name(&self) -> Option<&'static str> {
match self {
SessionSource::Cli | SessionSource::VSCode | SessionSource::Exec => Some("user"),
SessionSource::Internal(_) => Some("internal"),
SessionSource::SubAgent(_) => Some("subagent"),
SessionSource::Mcp | SessionSource::Custom(_) | SessionSource::Unknown => None,
}
}
pub fn is_internal(&self) -> bool {
matches!(self, SessionSource::Internal(_))
}
pub fn is_non_root_agent(&self) -> bool {
matches!(
self,
SessionSource::Internal(_) | SessionSource::SubAgent(_)
)
}
pub fn get_nickname(&self) -> Option<String> {
match self {
SessionSource::SubAgent(SubAgentSource::ThreadSpawn { agent_nickname, .. }) => {
agent_nickname.clone()
}
SessionSource::SubAgent(SubAgentSource::MemoryConsolidation) => {
Some("Morpheus".to_string())
}
_ => None,
}
}
@@ -2723,9 +2733,6 @@ impl SessionSource {
SessionSource::SubAgent(SubAgentSource::ThreadSpawn { agent_role, .. }) => {
agent_role.clone()
}
SessionSource::SubAgent(SubAgentSource::MemoryConsolidation) => {
Some("memory builder".to_string())
}
_ => None,
}
}
@@ -2735,9 +2742,6 @@ impl SessionSource {
SessionSource::SubAgent(SubAgentSource::ThreadSpawn { agent_path, .. }) => {
agent_path.clone()
}
SessionSource::SubAgent(SubAgentSource::MemoryConsolidation) => {
Some(AgentPath::morpheus())
}
_ => None,
}
}
@@ -2750,7 +2754,7 @@ impl SessionSource {
| SessionSource::Exec
| SessionSource::Mcp
| SessionSource::Unknown => Some(Product::Codex),
SessionSource::SubAgent(_) => None,
SessionSource::Internal(_) | SessionSource::SubAgent(_) => None,
}
}
@@ -2780,6 +2784,14 @@ impl fmt::Display for SubAgentSource {
}
}
impl fmt::Display for InternalSessionSource {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InternalSessionSource::MemoryConsolidation => f.write_str("memory_consolidation"),
}
}
}
/// SessionMeta contains session-level data that doesn't correspond to a specific turn.
///
/// NOTE: There used to be an `instructions` field here, which stored user_instructions, but we
@@ -4101,6 +4113,10 @@ mod tests {
(SessionSource::Cli, Some("user")),
(SessionSource::VSCode, Some("user")),
(SessionSource::Exec, Some("user")),
(
SessionSource::Internal(InternalSessionSource::MemoryConsolidation),
Some("internal"),
),
(
SessionSource::SubAgent(SubAgentSource::Review),
Some("subagent"),
@@ -4143,6 +4159,11 @@ mod tests {
SessionSource::SubAgent(SubAgentSource::Review).restriction_product(),
None
);
assert_eq!(
SessionSource::Internal(InternalSessionSource::MemoryConsolidation)
.restriction_product(),
None
);
}
#[test]