Skip to content

Render chat entries on the kind/author axes — system notes in history, agents distinct from owners, moderator indicator #37

Description

@emooreatx

GET /v1/chat/{id}/messages now returns entries on two axes instead of a message list, so one renderer can serve chat rooms, agent conversations, and (later) multi-user channels. Requesting the client side of that.

Server: CIRISServer fd18e8d (0.5.197).

The schema

Every entry in messages carries:

field values viewer-dependent?
kind message | system | error no
author the attester's key_id (empty for system) no
author_kind person | agent | node | other | unknown no
author_role founder | member | operator-defined; absent if not on the roster no
relation self | own_agent | other | none yes
mine bool, exactly relation == "self" yes
message_id localization key; system/error only no
body text, or absent when the row could not be opened no
unopened_reason why it could not be opened no
status live | superseded | withdrawn | recanted no

Why two axes and not one role

The obvious flat enum — self | other_human | my_agent | other_agent | system | error — was considered and rejected. It fuses two independent questions, and a channel breaks it:

  • other_human collapses every other participant into one label, and the client still needs author to draw a name and avatar. In a 50-person channel it says nothing.
  • my_agent vs other_agent is a relationship, so the same row is my_agent to one member and other_agent to another — but these rows are CEG objects replicated byte-identically to every member. A viewer-dependent value cannot be a property of the row.

So kind + author_kind are facts about the entry, relation is the derived convenience, and every combination you asked for is expressible:

you want match on
self relation == "self"
other human kind == "message" && author_kind == "person" && relation == "other"
my agent author_kind == "agent" && relation == "own_agent"
other agent author_kind == "agent" && relation == "other"
system kind == "system"
error kind == "error"

This is the split Matrix makes (sender + event type, with "is it me" computed client-side). The system|user|assistant triple from LLM APIs is what we deliberately did not copy — it cannot say which user or which agent.

What we are asking for

1. Render kind distinctly. message as a bubble; system as a centered inline note in the transcript (not a toast, not a dialog — it belongs in history, in order); error as a note that reads as a problem.

2. Moderators. author_role == "founder" moderates — in a pair room both members are founders. Please show a mod indicator next to the name. Note the vocabulary is open: persist documents it as "founder / member / operator-defined", so treat unknown values as ordinary members rather than failing, and do not hardcode a two-value enum.

3. Agents must be visually distinct from the people who own them. author_kind == "agent" with relation == "own_agent" is your own agent speaking — same owner, not the same speaker. Rendering it as the user's own message would attribute a machine's words to a human.

4. unknown is a real state. Directory resolution stalls while a key body is in flight. Render neutrally and re-resolve later; do not fall back to "person".

5. Localized system notes. New ids, all currently rendering the server's English (see CIRISClient#36 — please fold these in with those):

id English
chat.state.ready End-to-end encrypted.
chat.state.awaiting_peer Waiting for them to join this chat. They will see your invitation when their device next syncs, and your messages will send once they do.
chat.state.join_requested Request to join chat sent. Waiting for them to let you in — this completes on its own once their device answers.
chat.state.no_author_signer This device cannot act as you yet, so it cannot join the chat's key exchange. Create or unlock your federation ID to continue.

Each entry carries both message_id and the English in body, so render the lookup and fall back to body — never a blank line.

6. Do not offer a retry button when converges_on_its_own is true. The response carries it at the top level alongside ready. Both waiting states resolve when the peer's row replicates; a human pressing retry changes nothing, and offering the button says otherwise.

7. total counts what people said — system notes are excluded, so an unstarted conversation reports total: 0 with one entry in messages. Don't drive unread badges off messages.length.

Replaces

503 chat.room_not_keyed_yet is gone. A room that has not finished its key exchange is a 200 with a system entry. If the client has a special case for that 503, it can be deleted.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions