fix(agent): MCP 读日志/诊断崩溃改走 session_index::event_file(D2 第三处读取点)
M212(M211 复审观察 7 确认的真 bug):
- `agent/ports/events.rs::event_path` 仍在 `root.join("game").join(&entry.log_path)`,
而 `log_path` 已经是**相对会话根**的 `game/<id>.jsonl` ⇒ 对每个真实会话解析成
`game/game/<id>.jsonl`。后果是 `ull agent` / MCP 的 read_game_log 与 diagnose_crash
两个工具对真实会话全报「读会话事件流失败」——内置 AI Agent 的看家本领是坏的。
现在解析只走 M211 建立的唯一真相源 `session_index::event_file`(新旧两个根都走它)。
- 夹具:`ports/session.rs`、`ports/diagnose.rs` 的索引夹具此前写裸文件名
`20261029T124501-9f3c2a.jsonl`,与生产差一层 `game/`,所以这个 bug 在测试里永远看不见。
改成 `session_index::log_path_for(id)` + `session_index::event_file` 落盘。
- 用例:`ports/events.rs` 新增三条(解析结果 = 写入侧落盘的那一个 / 文件缺失时指向的仍是
生产形状 / 旧根回落走同一套解析);`cli/tests/agent_mcp_stdio_test.rs` 新增一条真机
MCP 用例(起 `ull agent serve --stdio`,对一份生产形状的会话调 list_game_sessions →
read_game_log → diagnose_crash)。旧实现下两条路径都红,红的是
`...\logs\game\game\20261030T101500-abcdef.jsonl`。
This commit is contained in:
1 parent
994ffda626
commit
fbb4a79955
4 files changed
+197
-13
No files matched your search
@@ -10,6 +10,9 @@ use std::process::{Command, Stdio};
|
||||
|
||||
use serde_json::{json, Value};
|
||||
|
||||
#[path = "support/mod.rs"]
|
||||
mod support;
|
||||
|
||||
fn data_dir(tag: &str) -> PathBuf {
|
||||
let dir = std::env::temp_dir().join(format!("ull-agent-stdio-{tag}-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
@@ -183,3 +186,72 @@ fn stdio_handshake_lists_tools_and_calls_them() {
|
||||
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
/// 抓得住:真实会话(索引 + 事件流按**生产形状**落盘)经 MCP 走 `read_game_log` /
|
||||
/// `diagnose_crash` 时真读得到行,而不是「0 行 / 找不到事件流」。
|
||||
/// 抓不住:事件流内容本身对不对(`ports::session` / `ports::diagnose` 的用例守),
|
||||
/// 也抓不住 `ull agent` 之外的读取点(`ull logs` / GUI 由 M211 与 presenter 用例守)。
|
||||
#[test]
|
||||
fn mcp_reads_a_real_session_log_and_diagnoses_it() {
|
||||
let dir = data_dir("session-read");
|
||||
let sid = support::write_session(&dir);
|
||||
let mut session = Session::start(&dir);
|
||||
|
||||
session.rpc(
|
||||
1,
|
||||
"initialize",
|
||||
json!({
|
||||
"protocolVersion": "2025-03-26",
|
||||
"capabilities": {},
|
||||
"clientInfo": { "name": "ull-test", "version": "0.1" }
|
||||
}),
|
||||
);
|
||||
session.send(&json!({ "jsonrpc": "2.0", "method": "notifications/initialized" }));
|
||||
|
||||
let listed = session.rpc(
|
||||
2,
|
||||
"tools/call",
|
||||
json!({ "name": "list_game_sessions", "arguments": {} }),
|
||||
);
|
||||
let text = listed["result"]["content"][0]["text"]
|
||||
.as_str()
|
||||
.unwrap_or_default();
|
||||
assert!(text.contains(&sid), "应列出该会话:{text}");
|
||||
|
||||
// 读日志:三行事件都在;只要 stderr 时正好那一行 OOM。
|
||||
let page = session.rpc(
|
||||
3,
|
||||
"tools/call",
|
||||
json!({ "name": "read_game_log", "arguments": { "session_id": sid, "kinds": ["stderr"] } }),
|
||||
);
|
||||
assert_ne!(page["result"]["isError"], json!(true), "{page}");
|
||||
let text = page["result"]["content"][0]["text"]
|
||||
.as_str()
|
||||
.unwrap_or_default();
|
||||
assert!(
|
||||
text.contains("\"total_events\":3"),
|
||||
"应读到全部 3 行事件:{text}"
|
||||
);
|
||||
assert!(
|
||||
text.contains("OutOfMemoryError"),
|
||||
"应读到 stderr 原文:{text}"
|
||||
);
|
||||
|
||||
// 诊断:同一份 OOM 事件流要命中规则,而不是 needs_more_info。
|
||||
let report = session.rpc(
|
||||
4,
|
||||
"tools/call",
|
||||
json!({ "name": "diagnose_crash", "arguments": { "session_id": sid } }),
|
||||
);
|
||||
assert_ne!(report["result"]["isError"], json!(true), "{report}");
|
||||
let text = report["result"]["content"][0]["text"]
|
||||
.as_str()
|
||||
.unwrap_or_default();
|
||||
assert!(text.contains("\"needs_more_info\":false"), "{text}");
|
||||
assert!(
|
||||
!text.contains("\"candidates\":[]"),
|
||||
"应命中至少一条规则:{text}"
|
||||
);
|
||||
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
Reference in new issue
Block a user