agentmux_srv/server/agent_handlers/
history.rs1use std::sync::Arc;
6
7
8use crate::backend::rpc::engine::WshRpcEngine;
9use crate::backend::rpc_types::{
10 COMMAND_APPEND_AGENT_HISTORY, COMMAND_LIST_AGENT_HISTORY, COMMAND_SEARCH_AGENT_HISTORY,
11 CommandAppendAgentHistoryData, CommandListAgentHistoryData, CommandSearchAgentHistoryData,
12};
13
14use super::super::AppState;
15
16pub fn register(engine: &Arc<WshRpcEngine>, state: &AppState) {
17 let mstore_afh = state.mstore.clone();
19 let broker_afh = state.broker.clone();
20 engine.register_typed(
21 COMMAND_APPEND_AGENT_HISTORY,
22 move |cmd: CommandAppendAgentHistoryData, _ctx| {
23 let mstore = mstore_afh.clone();
24 let broker = broker_afh.clone();
25 async move {
26 let entry = mstore.agent_history_append(&cmd.agent_id, &cmd.entry)
27 .map_err(|e| format!("appendagenthistory: {e}"))?;
28 broker.publish_event::<crate::backend::mps_events::AgentHistoryChanged>(&(), vec![], 0);
29 Ok(entry)
30 }
31 },
32 );
33
34 let mstore_lfh = state.mstore.clone();
36 engine.register_typed(
37 COMMAND_LIST_AGENT_HISTORY,
38 move |cmd: CommandListAgentHistoryData, _ctx| {
39 let mstore = mstore_lfh.clone();
40 async move {
41 let entries = mstore.agent_history_list(
42 &cmd.agent_id,
43 cmd.session_date.as_deref(),
44 cmd.limit,
45 cmd.offset,
46 ).map_err(|e| format!("listagenthistory: {e}"))?;
47 Ok(entries)
48 }
49 },
50 );
51
52 let mstore_sfh = state.mstore.clone();
54 engine.register_typed(
55 COMMAND_SEARCH_AGENT_HISTORY,
56 move |cmd: CommandSearchAgentHistoryData, _ctx| {
57 let mstore = mstore_sfh.clone();
58 async move {
59 let entries = mstore.agent_history_search(&cmd.agent_id, &cmd.query, cmd.limit)
60 .map_err(|e| format!("searchagenthistory: {e}"))?;
61 Ok(entries)
62 }
63 },
64 );
65
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71 use crate::backend::rpc_types::{CommandListAgentHistoryData, CommandSearchAgentHistoryData};
72
73 #[tokio::test]
75 async fn register_typed_records_every_history_command() {
76 let state = crate::server::tests::test_state();
77 let (engine, _rx) = WshRpcEngine::new();
78 register(&engine, &state);
79 let schema = engine.schema_json();
80 let rows = schema.as_array().unwrap();
81 let find = |cmd: &str| {
82 rows.iter()
83 .find(|r| r["command"] == cmd)
84 .unwrap_or_else(|| panic!("{cmd} missing from the schema"))
85 };
86
87 let append = find(COMMAND_APPEND_AGENT_HISTORY);
88 assert_eq!(append["requestName"], "CommandAppendAgentHistoryData");
89 assert_eq!(append["responseName"], "AgentHistory");
90
91 for (cmd, req) in [
92 (COMMAND_LIST_AGENT_HISTORY, "CommandListAgentHistoryData"),
93 (COMMAND_SEARCH_AGENT_HISTORY, "CommandSearchAgentHistoryData"),
94 ] {
95 let row = find(cmd);
96 assert_eq!(row["requestName"], req, "{cmd} request");
97 let resp = row["responseName"].as_str().unwrap();
98 assert!(
99 resp.starts_with("alloc::vec::Vec<") && resp.contains("AgentHistory"),
100 "{cmd} should answer Vec<AgentHistory>, got {resp:?}",
101 );
102 }
103 }
104
105 #[test]
110 fn the_pagination_fields_may_be_omitted() {
111 let list: CommandListAgentHistoryData =
112 serde_json::from_value(serde_json::json!({ "agent_id": "a1" }))
113 .expect("limit and offset are both defaulted");
114 assert_eq!(list.limit, 50, "listagenthistory defaults limit to 50");
115 assert_eq!(list.offset, 0);
116 assert!(list.session_date.is_none());
117
118 let search: CommandSearchAgentHistoryData =
119 serde_json::from_value(serde_json::json!({ "agent_id": "a1", "query": "x" }))
120 .expect("limit is defaulted");
121 assert_eq!(search.limit, 50, "searchagenthistory shares the same default");
122 }
123}