feat(rag): harden post-LightRAG runtime
Retire legacy OCR/media/evidence fallbacks, add local-folder event bus and Page Aggregate guards, and archive completed design checklists. Validation: cargo test -p mnote-web -- --test-threads=1; cargo test --workspace -- --test-threads=1; git diff --check; codegraph sync .; codegraph_status.
This commit is contained in:
@@ -10608,7 +10608,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn page_ai_capabilities_expose_local_index_and_toggle_tools() {
|
||||
async fn page_ai_capabilities_expose_knowledge_rag_and_toggle_tools() {
|
||||
let _env_guard = env_lock().lock().expect("env lock");
|
||||
let hermes_home = std::env::temp_dir().join(format!(
|
||||
"mnote-web-ai-capability-policy-{}",
|
||||
@@ -13223,20 +13223,16 @@ mod tests {
|
||||
)
|
||||
.await
|
||||
.expect("response");
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
|
||||
let body = to_bytes(response.into_body(), usize::MAX)
|
||||
.await
|
||||
.expect("body");
|
||||
let payload: Value = serde_json::from_slice(&body).expect("json");
|
||||
assert_eq!(payload["ok"], true);
|
||||
assert_eq!(payload["sessions"][0]["sessionId"], "sess_1");
|
||||
assert_eq!(payload["persistence"], "convex_acp_runtime_store");
|
||||
assert_eq!(payload["ok"], false);
|
||||
assert_eq!(payload["code"], "convex_retired");
|
||||
|
||||
let query_body = captured_body.lock().expect("captured convex body").clone();
|
||||
assert_eq!(query_body["path"], "aiSessions:listRuntimeRuns");
|
||||
assert_eq!(query_body["args"]["userId"], "user_1");
|
||||
assert_eq!(query_body["args"]["workspaceId"], "ws_1");
|
||||
assert_eq!(query_body["args"]["documentId"], "doc_1");
|
||||
assert_eq!(query_body, Value::Null);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -13317,28 +13313,16 @@ mod tests {
|
||||
)
|
||||
.await
|
||||
.expect("response");
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
|
||||
let body = to_bytes(response.into_body(), usize::MAX)
|
||||
.await
|
||||
.expect("body");
|
||||
let payload: Value = serde_json::from_slice(&body).expect("json");
|
||||
assert_eq!(payload["ok"], true);
|
||||
assert_eq!(payload["persistence"], "convex_acp_runtime_store");
|
||||
assert_eq!(payload["session"]["sessionId"], "sess_1");
|
||||
assert_eq!(payload["session"]["runs"][0]["runId"], "run_1");
|
||||
assert_eq!(payload["runtime"]["runId"], "run_1");
|
||||
assert_eq!(payload["events"][0]["eventType"], "message.delta");
|
||||
assert_eq!(
|
||||
payload["session"]["messages"].as_array().map(Vec::len),
|
||||
Some(0)
|
||||
);
|
||||
assert_eq!(payload["ok"], false);
|
||||
assert_eq!(payload["code"], "convex_retired");
|
||||
|
||||
let bodies = captured_bodies.lock().expect("captured convex bodies");
|
||||
assert_eq!(bodies[0]["path"], "aiSessions:listRuntimeRuns");
|
||||
assert_eq!(bodies[0]["args"]["userId"], "user_1");
|
||||
assert_eq!(bodies[0]["args"]["sessionId"], "sess_1");
|
||||
assert_eq!(bodies[1]["path"], "aiSessions:listRuntimeEvents");
|
||||
assert_eq!(bodies[1]["args"]["runId"], "run_1");
|
||||
assert!(bodies.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -13403,15 +13387,13 @@ mod tests {
|
||||
)
|
||||
.await
|
||||
.expect("response");
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
|
||||
let body = to_bytes(response.into_body(), usize::MAX)
|
||||
.await
|
||||
.expect("body");
|
||||
let payload: Value = serde_json::from_slice(&body).expect("json");
|
||||
assert_eq!(payload["ok"], true);
|
||||
assert_eq!(payload["resumed"], true);
|
||||
assert_eq!(payload["resumeSource"], "convex_acp_runtime_store");
|
||||
assert_eq!(payload["session"]["runs"][0]["runId"], "run_1");
|
||||
assert_eq!(payload["ok"], false);
|
||||
assert_eq!(payload["code"], "convex_retired");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -13486,7 +13468,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn hermes_client_acp_session_search_returns_convex_snippets() {
|
||||
async fn hermes_client_acp_session_search_legacy_convex_returns_retired_guard() {
|
||||
let captured_body = Arc::new(Mutex::new(Value::Null));
|
||||
let captured_for_route = Arc::clone(&captured_body);
|
||||
let mock = axum::Router::new().route(
|
||||
@@ -13548,21 +13530,16 @@ mod tests {
|
||||
)
|
||||
.await
|
||||
.expect("response");
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
|
||||
let body = to_bytes(response.into_body(), usize::MAX)
|
||||
.await
|
||||
.expect("body");
|
||||
let payload: Value = serde_json::from_slice(&body).expect("json");
|
||||
assert_eq!(payload["ok"], true);
|
||||
assert_eq!(payload["results"][0]["sessionId"], "sess_1");
|
||||
assert_eq!(payload["results"][0]["snippet"], "帮我总结化学页面");
|
||||
assert_eq!(payload["ok"], false);
|
||||
assert_eq!(payload["code"], "convex_retired");
|
||||
|
||||
let query_body = captured_body.lock().expect("captured convex body").clone();
|
||||
assert_eq!(query_body["path"], "aiSessions:searchRuntimeSessions");
|
||||
assert_eq!(query_body["args"]["userId"], "user_1");
|
||||
assert_eq!(query_body["args"]["workspaceId"], "ws_1");
|
||||
assert_eq!(query_body["args"]["q"], "化学");
|
||||
assert_eq!(query_body["args"]["limit"], 5);
|
||||
assert_eq!(query_body, Value::Null);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -14520,13 +14497,40 @@ mod tests {
|
||||
&"/api/hermes/client/runs".parse().expect("uri"),
|
||||
&headers,
|
||||
);
|
||||
let root = std::env::temp_dir().join(format!(
|
||||
"mnote-local-agent-run-receipt-{}",
|
||||
std::process::id()
|
||||
));
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
std::fs::create_dir_all(&root).expect("create root");
|
||||
std::fs::write(root.join("README.md"), "# Readme\nold\n").expect("write readme");
|
||||
std::fs::write(root.join("Other.md"), "# Other\nold\n").expect("write other");
|
||||
let root_uri = format!("file://{}", root.display());
|
||||
let payload = json!({
|
||||
"workspaceId": "local-workspace-1",
|
||||
"documentId": "local-md:README.md",
|
||||
"sessionId": "sess_local_1",
|
||||
"rootUri": "file:///tmp/mnote-agent-run-receipt",
|
||||
"actorId": "user_1"
|
||||
"rootUri": root_uri,
|
||||
"actorId": "user_1",
|
||||
"contextRefs": [{
|
||||
"kind": "folder",
|
||||
"rootUri": root_uri,
|
||||
"relativePath": ""
|
||||
}],
|
||||
"targetPackage": {
|
||||
"schema": "mnote.agent_target_package.v1",
|
||||
"allowedFiles": ["README.md"],
|
||||
"currentFile": {
|
||||
"relativePath": "README.md"
|
||||
}
|
||||
}
|
||||
});
|
||||
let before = local_agent_audit_collect_snapshot_for_payload(&payload, None)
|
||||
.expect("before allowed-files snapshot");
|
||||
std::fs::write(root.join("README.md"), "# Readme\nnew\n").expect("modify readme");
|
||||
std::fs::write(root.join("Other.md"), "# Other\nnew\n").expect("modify other");
|
||||
let after = local_agent_audit_collect_snapshot_for_payload(&payload, Some(&before))
|
||||
.expect("after allowed-files snapshot");
|
||||
let changed_files = json!([
|
||||
{
|
||||
"path": "README.md",
|
||||
@@ -14541,8 +14545,8 @@ mod tests {
|
||||
"reasonix",
|
||||
"completed",
|
||||
changed_files,
|
||||
None,
|
||||
None,
|
||||
Some(&before),
|
||||
Some(&after),
|
||||
false,
|
||||
);
|
||||
let receipt = &event["agentRunReceipt"];
|
||||
@@ -14553,6 +14557,11 @@ mod tests {
|
||||
assert_eq!(receipt["status"], "completed");
|
||||
assert_eq!(receipt["changedFiles"][0]["path"], "README.md");
|
||||
assert_eq!(receipt["refresh"]["touchesCurrentFile"], true);
|
||||
assert_eq!(event["auditScope"]["scope"], "allowed_files");
|
||||
assert_eq!(event["auditScope"]["fileCount"], 1);
|
||||
assert_eq!(receipt["auditScope"]["scope"], "allowed_files");
|
||||
assert_eq!(receipt["auditScope"]["fileCount"], 1);
|
||||
let _ = std::fs::remove_dir_all(root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -15066,7 +15075,7 @@ mod tests {
|
||||
"/api/hermes/client/profile-memory?profile=chemist",
|
||||
None,
|
||||
),
|
||||
("GET", "/api/hermes/client/skills?profile=chemist", None),
|
||||
("GET", "/api/hermes/client/skills", None),
|
||||
(
|
||||
"PUT",
|
||||
"/api/hermes/client/profiles/active",
|
||||
@@ -15107,7 +15116,7 @@ mod tests {
|
||||
);
|
||||
let request = Request::builder()
|
||||
.method("GET")
|
||||
.uri("/api/hermes/client/skills?profile=chemist")
|
||||
.uri("/api/hermes/client/skills")
|
||||
.header("x-mnote-actor-id", "user_1")
|
||||
.body(Body::empty())
|
||||
.expect("request");
|
||||
|
||||
Reference in New Issue
Block a user