feat(#2569): migrate matrix producer to the in-agent todo socket (unread + invites); drop dead mcp.sock/wake plumbing

This commit is contained in:
damocles 2026-07-20 23:09:54 +02:00
commit 21f1569a04
5 changed files with 113 additions and 166 deletions

View file

@ -512,44 +512,6 @@ pub fn list_invites(client: &Client) -> DaemonResponse {
DaemonResponse::ok(&invites)
}
/// Rewrite `mcp-loose-ends/matrix.json` with a summary of all pending
/// room invites. The harness scans this directory generically in
/// `get_loose_ends` — no matrix-specific code needed there.
///
/// Called after an invite arrives (from the sync handler) and after a
/// room is joined (to remove the accepted invite from loose-ends).
/// Atomic write (tmp + rename) so the harness never reads a partial file.
pub async fn refresh_invite_loose_ends(client: &Client) {
let invites = client.invited_rooms();
let dir = crate::paths::mcp_loose_ends_dir();
if let Err(e) = tokio::fs::create_dir_all(&dir).await {
tracing::warn!(error = ?e, "matrix: create mcp-loose-ends dir failed");
return;
}
let items: Vec<String> = invites
.iter()
.map(|room| {
let label = room_label(room);
format!(
"[matrix] pending invite: {label} — use list_invites to see, resolve_invite to accept or reject"
)
})
.collect();
let dest = dir.join("matrix.json");
let tmp = dest.with_extension("json.tmp");
let json = serde_json::to_string(&items).unwrap_or_else(|_| "[]".to_owned());
match tokio::fs::write(&tmp, &json).await {
Ok(()) => {
if let Err(e) = tokio::fs::rename(&tmp, &dest).await {
tracing::warn!(error = ?e, "matrix: rename mcp-loose-ends/matrix.json failed");
}
}
Err(e) => {
tracing::warn!(error = ?e, "matrix: write mcp-loose-ends/matrix.json.tmp failed");
}
}
}
pub async fn join_room(client: &Client, room_ref: &str) -> DaemonResponse {
let parsed: &RoomOrAliasId = match room_ref.try_into() {
Ok(p) => p,
@ -560,9 +522,14 @@ pub async fn join_room(client: &Client, room_ref: &str) -> DaemonResponse {
let server_names: Vec<OwnedServerName> = vec![];
match client.join_room_by_id_or_alias(parsed, &server_names).await {
Ok(room) => {
// Refresh loose-ends so the accepted invite is removed from
// `get_loose_ends` output immediately after the agent joins.
refresh_invite_loose_ends(client).await;
// Clear the invite todo so the accepted invite drops out of
// `get_loose_ends` immediately (the sweep would also clear it
// on its next tick, but this makes it instant).
let _ = crate::wake::send_todo_clear(
Some(&crate::timeline::invite_key(room.room_id())),
false,
)
.await;
DaemonResponse::ok(&serde_json::json!({
"joined": true,
"room_id": room.room_id().to_string(),
@ -595,7 +562,12 @@ pub async fn resolve_invite(
};
match room.leave().await {
Ok(()) => {
refresh_invite_loose_ends(client).await;
// Clear the invite todo immediately on reject.
let _ = crate::wake::send_todo_clear(
Some(&crate::timeline::invite_key(room.room_id())),
false,
)
.await;
DaemonResponse::ok(&serde_json::json!({
"rejected": true,
"room_id": room.room_id().to_string(),