Skip to content

miniapp: add matrix.edit_message service (m.replace) - #91

Open
kkkkikun wants to merge 1 commit into
hagency-org:mainfrom
kkkkikun:feat/matrix-edit-message
Open

kkkkikun wants to merge 1 commit into
hagency-org:mainfrom
kkkkikun:feat/matrix-edit-message

Conversation

@kkkkikun

Copy link
Copy Markdown

What

Adds the matrix.edit_message service (m.replace) to the mini-app surface, closing #90.

Mini-apps could send messages but never fix one — any live-card / coordination app (polls, tally boards, status cards) had to post corrections as new messages. The composer already edits via m.replace internally; this exposes the same semantics as a service:

  • miniapp-core: MatrixServiceCall::EditMessage { event_id, body }, a parse arm (room-attached; trimmed non-empty body; same 4096-char cap as matrix.reply), priced 8.0 with the other send services. Capability gating needs no whitelist change: the manifest names matrix.edit_message and the call-time check in miniapps/package.rs matches by service string.
  • host: dispatch → new send::edit(), which loads the original event (cache first, explicit audited fetch fallback — same helper as matrix.event) and refuses up front when it isn't the user's own message. A forged m.replace is silently dropped by every client, so answering an error here is the honest behavior. The original's mentions are handed to make_replacement so the edit doesn't re-ping. The send goes through the same commit_sensitive_target review + audit_room_operation gates as send_message.
  • Answer shape: {} on success, matching matrix.send_message.

Verification

cargo test -p rinx-miniapp-core           # 31 passed — incl. new parse tests:
                                          #   accept path, both missing-arg errors,
                                          #   4096-char cap, roomless refusal
cargo test -p rinx --lib                  # 357 passed, 0 failed
                                          #   incl. 2 new wire-format tests pinning
                                          #   the exact m.replace shape the room
                                          #   timeline renderer matches, and that
                                          #   edits don't re-ping original mentions

Not done: a live end-to-end run against a real homeserver (needs the full GUI + logged-in session). The new external surface is one room.send() of a ruma-constructed m.replace — the same call shape as the existing send path — and the issue already records palpo's m.replace semantics (acknowledged without /relations aggregation). Happy to add a scripted live check if you'd like one before merge.


Found while building a GOSIM Agentic App Hackathon 2026 entry (a group-coordination mini-app that wanted to update its tally card in place).

Mini-apps could send messages but never fix one, so any live-card /
coordination app had to spam corrections as new messages. The composer
already edits via m.replace internally; this exposes the same semantics
as a mini-app service:

- miniapp-core: EditMessage{event_id, body} variant, parse arm
  (room-attached, trimmed non-empty body, 4096-char cap like reply),
  priced 8.0 with the other send services. Parse tests cover the accept
  path, both missing-arg errors, the length cap and roomless refusal.
- host: dispatch to a new send::edit() which loads the original event,
  refuses up front when it isn't the user's own message (a forged
  m.replace would be silently dropped by every client — answer honestly
  instead), keeps the original mentions so the edit doesn't re-ping, and
  goes through the same sensitive-target review + audit gates as sends.
- The wire shape (fallback '* ' body, m.new_content, m.relates_to) is
  pinned by unit tests against what the room timeline renderer matches.

Closes hagency-org#90
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant