Skip to main content

agent_status/agents/
opencode.rs

1use crate::agents::Agent;
2
3/// opencode ([opencode.ai](https://opencode.ai)).
4///
5/// Reads `session_id` from the JSON piped in by the bundled opencode plugin at
6/// `extensions/opencode.ts`, which fires on opencode's `session.idle`,
7/// `permission.updated`, `session.created`, and `session.deleted` events.
8pub struct OpencodeAgent;
9
10impl Agent for OpencodeAgent {
11    fn name(&self) -> &'static str {
12        "opencode"
13    }
14
15    fn extract_session_id(&self, stdin_json: &str) -> Option<String> {
16        let v: serde_json::Value = serde_json::from_str(stdin_json).ok()?;
17        let id = v.get("session_id")?.as_str()?;
18        if id.is_empty() {
19            None
20        } else {
21            Some(id.to_string())
22        }
23    }
24
25    fn extract_message(&self, stdin_json: &str) -> Option<String> {
26        let v: serde_json::Value = serde_json::from_str(stdin_json).ok()?;
27        let m = v.get("message")?.as_str()?;
28        if m.is_empty() {
29            None
30        } else {
31            Some(m.to_string())
32        }
33    }
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39
40    #[test]
41    fn name_is_opencode() {
42        assert_eq!(OpencodeAgent.name(), "opencode");
43    }
44
45    #[test]
46    fn extract_session_id_returns_id() {
47        let json = r#"{"session_id":"abc-123","other":"stuff"}"#;
48        assert_eq!(
49            OpencodeAgent.extract_session_id(json).as_deref(),
50            Some("abc-123")
51        );
52    }
53
54    #[test]
55    fn extract_session_id_returns_none_for_missing_field() {
56        assert_eq!(
57            OpencodeAgent.extract_session_id(r#"{"other":1}"#),
58            None
59        );
60    }
61
62    #[test]
63    fn extract_session_id_returns_none_for_empty_string() {
64        assert_eq!(
65            OpencodeAgent.extract_session_id(r#"{"session_id":""}"#),
66            None
67        );
68    }
69
70    #[test]
71    fn extract_session_id_returns_none_for_invalid_json() {
72        assert_eq!(OpencodeAgent.extract_session_id("not json"), None);
73    }
74
75    #[test]
76    fn extract_message_returns_string_when_present() {
77        let json = r#"{"session_id":"x","message":"Plan ready for review"}"#;
78        assert_eq!(
79            OpencodeAgent.extract_message(json).as_deref(),
80            Some("Plan ready for review")
81        );
82    }
83
84    #[test]
85    fn extract_message_returns_none_when_field_missing() {
86        assert!(OpencodeAgent.extract_message(r#"{"session_id":"x"}"#).is_none());
87    }
88
89    #[test]
90    fn extract_message_returns_none_when_empty() {
91        assert!(OpencodeAgent.extract_message(r#"{"session_id":"x","message":""}"#).is_none());
92    }
93
94    #[test]
95    fn extract_message_returns_none_for_non_string_value() {
96        assert!(OpencodeAgent.extract_message(r#"{"session_id":"x","message":[]}"#).is_none());
97    }
98
99    #[test]
100    fn extract_message_returns_none_for_invalid_json() {
101        assert!(OpencodeAgent.extract_message("not json").is_none());
102    }
103}