feat(ai): /ai start|stop agent control + in-room typing indicator
Owner of the spawning client can summon/dismiss a local AI agent from inside the room (default ollama/qwen2.5:3b); the agent emits encrypted typing frames that drive a "thinking" spinner in the client. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -178,6 +178,13 @@ fn decode_msg(room: &fernet::Fernet, m: &Value, live: bool) -> Decoded {
|
||||
Decoded::Skip
|
||||
};
|
||||
}
|
||||
if t.starts_with("{\"_ai\":") {
|
||||
return if live {
|
||||
parse_ai(&t).map(Decoded::Sbx).unwrap_or(Decoded::Skip)
|
||||
} else {
|
||||
Decoded::Skip
|
||||
};
|
||||
}
|
||||
(t, false)
|
||||
}
|
||||
Err(_) => ("[unreadable — wrong room password?]".to_string(), true),
|
||||
@@ -220,6 +227,19 @@ fn parse_sbx(text: &str, sender: &str) -> Option<Net> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a decrypted `{"_ai":"typing",...}` frame — an AI agent signalling that
|
||||
/// it is (or has finished) generating a reply, so the UI can show a spinner.
|
||||
fn parse_ai(text: &str) -> Option<Net> {
|
||||
let v: Value = serde_json::from_str(text).ok()?;
|
||||
if v["_ai"].as_str()? != "typing" {
|
||||
return None;
|
||||
}
|
||||
Some(Net::AiTyping {
|
||||
name: v["name"].as_str().unwrap_or("ai").to_string(),
|
||||
on: v["on"].as_bool().unwrap_or(false),
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse a decrypted `{"_perm":"acl",...}` frame.
|
||||
fn parse_perm(text: &str) -> Option<Net> {
|
||||
let v: Value = serde_json::from_str(text).ok()?;
|
||||
|
||||
Reference in New Issue
Block a user