Skip to main content

objectiveai_sdk/mcp/
queue_notification.rs

1//! Shared format + parse for the `<system-reminder-{token}>` wrapper
2//! that the MCP proxy prepends to tool responses when surfacing
3//! pending `message_queue` content. The proxy emits via
4//! [`format_prefix`]; `run_agent_loop` (in `objectiveai-api`)
5//! matches via [`extract_tokens`]. Owning both in one module
6//! keeps the two ends in lockstep — a format change here
7//! updates the matcher implicitly.
8//!
9//! The confirmation token is embedded directly in the opening tag
10//! name: `<system-reminder-<UUID>>`. The API delegate generates the
11//! token on every `read_pending_blocks` call and stashes the
12//! `token → ids` mapping until the run-loop sees the token in a tool
13//! message and confirms delivery. Tokens never echoed back stay in
14//! "pending" limbo and re-deliver on the next loop's reads —
15//! that's the robustness win over a naive ban-list-only design.
16
17/// Format the wrapper opening tag the proxy prepends to a tool
18/// response when surfacing queued blocks. The token is embedded in
19/// the tag name and is opaque to the proxy — it round-trips through
20/// the agent's tool-message text to `run_agent_loop`'s confirmation
21/// scan.
22pub fn format_prefix(token: &str) -> String {
23    format!("<system-reminder-{token}>\nThe user sent a new message while you were working:\n")
24}
25
26/// Format the matching closing tag. The token is embedded in the tag
27/// name too, so the closing tag pairs with [`format_prefix`]'s opening
28/// tag: `</system-reminder-<UUID>>`. The trailing blank line demarcates
29/// the wrapper from the real tool-result content that follows it.
30pub fn format_suffix(token: &str) -> String {
31    format!("\n\n</system-reminder-{token}>\n\n")
32}
33
34/// Scan one text chunk for the prefix pattern; return every
35/// captured token in document order. Typical case is zero or one
36/// match per tool message; multiple are possible (one delegate
37/// call per tool call, but the proxy could splice multiple
38/// reminders in pathological scenarios), and the regex captures
39/// all to be safe.
40///
41/// The regex is compiled lazily — first call costs a few µs to
42/// build the DFA; subsequent calls reuse the cached `Regex` via
43/// `OnceLock` with zero overhead.
44pub fn extract_tokens(text: &str) -> Vec<String> {
45    static RE: std::sync::OnceLock<regex::Regex> = std::sync::OnceLock::new();
46    let re = RE.get_or_init(|| {
47        // Match just the opening tag and capture the UUID token in its
48        // name. The human-readable line that follows the tag is
49        // ignored — only `<system-reminder-{token}>` is matched. Not
50        // anchored to start/end and no newline requirement (the model
51        // may reflow whitespace around the tag), but the token keeps
52        // the strict UUID v4 shape (lowercase hex, 8-4-4-4-12) so
53        // arbitrary tool output can't be mistaken for a token. The
54        // pattern needs `<` immediately followed by `system-reminder-`,
55        // so the `</system-reminder-{token}>` closing tag (a `/` follows
56        // its `<`) is never captured — even though both tags now carry
57        // the token, only the opening one is extracted.
58        regex::Regex::new(
59            r"<system-reminder-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})>",
60        )
61        .expect("static regex pattern is well-formed")
62    });
63    re.captures_iter(text)
64        .map(|c| c.get(1).expect("group 1 is present in pattern").as_str().to_string())
65        .collect()
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn format_and_extract_round_trip() {
74        let token = "12345678-1234-1234-1234-1234567890ab";
75        let prefix = format_prefix(token);
76        let extracted = extract_tokens(&prefix);
77        assert_eq!(extracted, vec![token.to_string()]);
78    }
79
80    #[test]
81    fn no_match_on_unrelated_text() {
82        assert!(extract_tokens("plain old tool output").is_empty());
83        assert!(extract_tokens("(id: bogus)").is_empty());
84        assert!(extract_tokens("<system-reminder>\nDifferent text\n").is_empty());
85    }
86
87    #[test]
88    fn extracts_multiple_tokens() {
89        let t1 = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa";
90        let t2 = "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb";
91        let body = format!(
92            "{p1}block one{s1}{p2}block two{s2}",
93            p1 = format_prefix(t1),
94            p2 = format_prefix(t2),
95            s1 = format_suffix(t1),
96            s2 = format_suffix(t2),
97        );
98        let tokens = extract_tokens(&body);
99        assert_eq!(tokens, vec![t1.to_string(), t2.to_string()]);
100    }
101
102    #[test]
103    fn closing_tag_token_is_not_extracted() {
104        // A full open+close round-trip yields exactly the opening tag's
105        // token — the token-bearing closing tag must not be captured too.
106        let token = "12345678-1234-1234-1234-1234567890ab";
107        let body =
108            format!("{}body{}", format_prefix(token), format_suffix(token));
109        assert_eq!(extract_tokens(&body), vec![token.to_string()]);
110    }
111
112    #[test]
113    fn uppercase_hex_does_not_match() {
114        // UUID v4 in the regex is lowercase-only. Catches accidental
115        // case drift in the format function.
116        let prefix = "<system-reminder-ABCDEF01-1234-1234-1234-1234567890AB>";
117        assert!(extract_tokens(prefix).is_empty());
118    }
119}