Class ChatAccess

java.lang.Object
com.axelor.ai.chat.ChatAccess

public final class ChatAccess extends Object
Membership resolution for shared chats. The invite is the permission boundary: any AiChatParticipant row grants the read column (list, messages, stream resume). Driving — agent turns, tool approvals, the subject — extends to PROMPTER via canDrive(com.axelor.ai.db.AiChat, com.axelor.auth.db.User); administration (rename, delete, invite/remove, mode, the space mark, claims) stays with the owner ( createdBy, whose meaning is never widened).
  • Method Details

    • userRef

      public static Map<String,Object> userRef(User user)
      The {id, code, name} shape used for server-derived attribution everywhere.
    • memberCodes

      public static List<String> memberCodes(String uuid)
      User codes of everyone who can see the chat: the owner plus invited participants.
    • findMemberChat

      public static AiChat findMemberChat(String uuid, User user)
      The chat when user is its owner or an invited member, else null.
    • findMemberChatId

      public static Long findMemberChatId(String uuid, User user)
      Like findMemberChat(java.lang.String, com.axelor.auth.db.User), but returns only the primary key, loading no entity — so a caller can take a pessimistic lock as the row's first read. (Locking an already-loaded entity is a version-checked upgrade that fails when another transaction commits in between.)
    • ownerIdOf

      public static Long ownerIdOf(String uuid)
      The chat owner's id (createdBy), or null when the chat does not exist. A scalar read for per-turn gates that need the ownership answer without loading the row.
    • parseUuid

      public static UUID parseUuid(String uuid)
    • hasForeignUserMessage

      public static boolean hasForeignUserMessage(String chatUuid, User user)
      True when the chat's history contains a USER message authored by someone other than user — i.e. untrusted text sits in the prompt window. Mutation approval (the confused-deputy mitigation) keys on this rather than on the discussion-mode flag: a summoner alone with the agent gets no friction, and once foreign text exists it stays in the replay forever, so the guard stays on.
    • isTurnInitiator

      public static boolean isTurnInitiator(String chatUuid, User user)
      True when user is the member whose run the chat's latest assistant turn is — the row's author. An approval, like an interactive prompt, is that member's consent act: the tool executes with their permissions, so no other driver may answer it (see the streamer's sanitizeApprovedToolMessage, which validates the same thing on the resume path).
    • participantOf

      public static AiChatParticipant participantOf(AiChat chat, User user)
      The user's participant row on the chat, or null (owners have none — see roleOf(com.axelor.ai.db.AiChat, com.axelor.auth.db.User)).
    • roleOf

      public static ChatAccess.Role roleOf(AiChat chat, User user)
      The user's role on the chat, or null for a non-member.
    • canDrive

      public static boolean canDrive(AiChat chat, User user)
      True when user may drive (run an agent turn in) this chat: the owner, always, or a PROMPTER. Mode does not enter into it — mode decides whether a driver's plain message runs a turn, never whether they are allowed to run one. Every write path must agree on this, or a summon admitted by one is silently dropped by another.
    • modeOf

      public static AiChatConversationMode modeOf(AiChat chat)
      The chat's conversation mode; AUTO for rows created before the column existed.
    • isDiscussion

      public static boolean isDiscussion(AiChat chat)
      True when a driver's plain message goes to the members rather than the agent.
    • roleOf

      public static ChatAccess.Role roleOf(AiChatParticipant participant)
      The participant row's role, or null.