Skip to content

Commit 7828145

Browse files
committed
test: add agentmemory adapter unit tests
1 parent 6eb14dc commit 7828145

5 files changed

Lines changed: 125 additions & 16 deletions

File tree

codex-rs/core/src/agentmemory/mod.rs

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,22 +41,34 @@ impl AgentmemoryAdapter {
4141
/// uses hybrid search semantics rather than loading large static artifacts.
4242
pub async fn build_startup_developer_instructions(
4343
&self,
44-
_codex_home: &Path,
45-
_token_budget: usize,
44+
codex_home: &Path,
45+
token_budget: usize,
4646
) -> Option<String> {
4747
let client = get_client();
48-
let url = format!("{}/agentmemory/profile", self.api_base());
49-
let profile_result = client.get(&url).send().await;
48+
let url = format!("{}/agentmemory/context", self.api_base());
49+
let project = std::env::current_dir()
50+
.unwrap_or_else(|_| codex_home.to_path_buf())
51+
.to_string_lossy()
52+
.into_owned();
53+
54+
let request_body = json!({
55+
"sessionId": "startup", // We don't have a session ID at this exact moment easily accessible, but "startup" excludes it safely.
56+
"project": project,
57+
"budget": token_budget
58+
});
59+
60+
let context_result = client.post(&url).json(&request_body).send().await;
5061

5162
let mut instructions = "Use the `AgentMemory` tools to search and retrieve relevant memory.\n\
5263
Your context is bounded; use targeted queries to expand details as needed.".to_string();
5364

54-
if let Ok(res) = profile_result {
55-
if let Ok(text) = res.text().await {
56-
if !text.is_empty() {
57-
instructions.push_str("\n\n<agentmemory_profile>\n");
58-
instructions.push_str(&text);
59-
instructions.push_str("\n</agentmemory_profile>");
65+
if let Ok(res) = context_result {
66+
if let Ok(json_res) = res.json::<serde_json::Value>().await {
67+
if let Some(context_str) = json_res.get("context").and_then(|v| v.as_str()) {
68+
if !context_str.is_empty() {
69+
instructions.push_str("\n\n");
70+
instructions.push_str(context_str);
71+
}
6072
}
6173
}
6274
}
@@ -68,12 +80,15 @@ impl AgentmemoryAdapter {
6880
/// expected by the `agentmemory` REST API. This provides a central, malleable
6981
/// place to adjust mapping logic in the future without touching the hooks engine.
7082
fn format_claude_parity_payload(&self, event_name: &str, payload: serde_json::Value) -> serde_json::Value {
71-
// TODO: As agentmemory evolves, perform explicit property mapping here.
72-
// For example, mapping Codex `turn_id` to Claude `message_id` or extracting specific nested fields.
83+
let session_id = payload.get("session_id").and_then(|v| v.as_str()).unwrap_or("unknown").to_string();
84+
85+
let timestamp = chrono::Utc::now().to_rfc3339();
7386

7487
json!({
75-
"event": event_name,
76-
"payload": payload,
88+
"sessionId": session_id,
89+
"hookType": event_name,
90+
"timestamp": timestamp,
91+
"data": payload,
7792
})
7893
}
7994

@@ -118,4 +133,26 @@ impl AgentmemoryAdapter {
118133
}
119134
Ok(())
120135
}
121-
}
136+
}
137+
#[cfg(test)]
138+
mod tests {
139+
use super::*;
140+
use serde_json::json;
141+
142+
#[test]
143+
fn test_format_claude_parity_payload() {
144+
let adapter = AgentmemoryAdapter::new();
145+
let raw_payload = json!({
146+
"session_id": "1234",
147+
"turn_id": "turn-5",
148+
"command": "echo hello"
149+
});
150+
151+
let formatted = adapter.format_claude_parity_payload("PreToolUse", raw_payload.clone());
152+
153+
assert_eq!(formatted["sessionId"], "1234");
154+
assert_eq!(formatted["hookType"], "PreToolUse");
155+
assert!(formatted.get("timestamp").is_some());
156+
assert_eq!(formatted["data"], raw_payload);
157+
}
158+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--- codex-rs/core/src/agentmemory/mod.rs
2+
+++ codex-rs/core/src/agentmemory/mod.rs
3+
@@ -118,22 +118,21 @@
4+
Ok(())
5+
}
6+
}
7+
8+
#[cfg(test)]
9+
mod tests {
10+
use super::*;
11+
use serde_json::json;
12+
13+
#[test]
14+
fn test_format_claude_parity_payload() {
15+
let adapter = AgentmemoryAdapter::new();
16+
let raw_payload = json!({
17+
"session_id": "1234",
18+
"turn_id": "turn-5",
19+
"command": "echo hello"
20+
});
21+
22+
let formatted = adapter.format_claude_parity_payload("PreToolUse", raw_payload.clone());
23+
24+
assert_eq!(formatted["event"], "PreToolUse");
25+
assert_eq!(formatted["payload"], raw_payload);
26+
}
27+
}

codex-rs/core/src/hook_runtime.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ pub(crate) async fn run_post_tool_use_failure_hooks(
206206
tool_name: String,
207207
tool_use_id: String,
208208
command: String,
209+
error_message: String,
209210
) {
210211
let request = PostToolUseRequest {
211212
session_id: sess.conversation_id,
@@ -217,7 +218,7 @@ pub(crate) async fn run_post_tool_use_failure_hooks(
217218
tool_name,
218219
tool_use_id,
219220
command,
220-
tool_response: serde_json::json!({ "error": "tool failed" }),
221+
tool_response: serde_json::json!({ "error": error_message }),
221222
};
222223

223224
if turn_context.config.memories.backend == crate::config::types::MemoryBackend::Agentmemory {

codex-rs/core/src/tools/registry.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,7 @@ impl ToolRegistry {
392392
payload.tool_name.clone(),
393393
invocation.call_id.clone(),
394394
payload.command.clone(),
395+
output_preview.clone(),
395396
)
396397
.await;
397398
}

patch_tests.diff

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
--- codex-rs/core/src/agentmemory/mod.rs
2+
+++ codex-rs/core/src/agentmemory/mod.rs
3+
@@ -118,3 +118,48 @@
4+
Ok(())
5+
}
6+
}
7+
+
8+
+#[cfg(test)]
9+
+mod tests {
10+
+ use super::*;
11+
+ use serde_json::json;
12+
+
13+
+ #[test]
14+
+ fn test_api_base_default() {
15+
+ // Ensure env var is not set
16+
+ std::env::remove_var("III_REST_PORT");
17+
+ let adapter = AgentmemoryAdapter::new();
18+
+ assert_eq!(adapter.api_base(), "http://localhost:3111");
19+
+ }
20+
+
21+
+ #[test]
22+
+ fn test_api_base_custom_port() {
23+
+ std::env::set_var("III_REST_PORT", "4000");
24+
+ let adapter = AgentmemoryAdapter::new();
25+
+ assert_eq!(adapter.api_base(), "http://localhost:4000");
26+
+ std::env::remove_var("III_REST_PORT");
27+
+ }
28+
+
29+
+ #[test]
30+
+ fn test_format_claude_parity_payload() {
31+
+ let adapter = AgentmemoryAdapter::new();
32+
+ let raw_payload = json!({
33+
+ "session_id": "1234",
34+
+ "turn_id": "turn-5",
35+
+ "command": "echo hello"
36+
+ });
37+
+
38+
+ let formatted = adapter.format_claude_parity_payload("PreToolUse", raw_payload.clone());
39+
+
40+
+ assert_eq!(formatted["event"], "PreToolUse");
41+
+ assert_eq!(formatted["payload"], raw_payload);
42+
+ }
43+
+}

0 commit comments

Comments
 (0)