@ai-matrx/messaging 0.12.5 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog — `@ai-matrx/messaging`
2
2
 
3
+ ## 0.13.0
4
+
5
+ ### Added
6
+
7
+ - **A conversation that cannot be read now says so.** Opening a conversation the
8
+ caller may not read (a refused message read) used to leave the thread absent
9
+ forever — an endless spinner — and a zero-row read of a conversation that does
10
+ not exist or is hidden by RLS rendered "No messages yet". The engine now
11
+ records a per-conversation read failure in the store
12
+ (`snapshot.conversationReadFailures`, type `ConversationReadFailure`); on a
13
+ zero-row read it asks the conversation row itself (`getConversation(id)`)
14
+ before calling the thread empty, and opens no realtime channel for a
15
+ conversation the user cannot read.
16
+ - `useConversation(id)` gains `error: MessagingError | null` and
17
+ `unavailable: boolean` (additive). `isLoading` is now false once the read has
18
+ failed. `<ConversationView>` renders "Conversation unavailable" /
19
+ "Couldn't load this conversation" instead of the spinner or the empty state.
20
+
21
+ ### Consumer action
22
+
23
+ - Hosts with an access surface: when `useConversation(id).unavailable` is true,
24
+ render it instead of the thread. `error.cause` is the raw database error
25
+ (`42501`, `PGRST116`) for hosts that classify access failures themselves
26
+ (matrx-frontend: `<AccessGate token="dm_conversation" id error={error.cause} />`).
27
+ - No action otherwise — existing fields are unchanged.
28
+
3
29
  ## 0.12.5
4
30
 
5
31
  Automatic changed-only republish (docs/metadata drift since the last tag — see
package/dist/index.cjs CHANGED
@@ -1044,6 +1044,7 @@ function createMessagingStore() {
1044
1044
  let archivedCount = null;
1045
1045
  let threads = /* @__PURE__ */ new Map();
1046
1046
  let activeConversationId = null;
1047
+ let conversationReadFailures = /* @__PURE__ */ new Map();
1047
1048
  const listeners = /* @__PURE__ */ new Set();
1048
1049
  let cached = null;
1049
1050
  function snapshot() {
@@ -1056,7 +1057,8 @@ function createMessagingStore() {
1056
1057
  activeConversationId,
1057
1058
  totalUnreadConversations: conversations.filter((item) => item.unreadCount > 0).length,
1058
1059
  archiveFilter,
1059
- archivedCount
1060
+ archivedCount,
1061
+ conversationReadFailures
1060
1062
  };
1061
1063
  return cached;
1062
1064
  }
@@ -1140,6 +1142,14 @@ function createMessagingStore() {
1140
1142
  conversations = normalizeConversations(conversations);
1141
1143
  emit();
1142
1144
  },
1145
+ setConversationReadFailure(id, failure) {
1146
+ if (failure === null && !conversationReadFailures.has(id)) return;
1147
+ const next = new Map(conversationReadFailures);
1148
+ if (failure === null) next.delete(id);
1149
+ else next.set(id, failure);
1150
+ conversationReadFailures = next;
1151
+ emit();
1152
+ },
1143
1153
  setThread(id, messages, args = {}) {
1144
1154
  writeThread({
1145
1155
  conversationId: id,
@@ -1445,11 +1455,26 @@ function createMessagingEngine(options) {
1445
1455
  },
1446
1456
  async openConversation(id) {
1447
1457
  store.setActiveConversation(id);
1458
+ store.setConversationReadFailure(id, null);
1448
1459
  try {
1449
1460
  const page = await repository.listMessages(id, { limit: messagePageSize });
1461
+ if (page.items.length === 0) {
1462
+ await repository.getConversation(id);
1463
+ }
1450
1464
  store.setThread(id, page.items, { hasMoreOlder: page.hasMore });
1451
1465
  } catch (error) {
1452
- reportError(error, "openConversation");
1466
+ const normalized = normalizeMessagingError(error, "openConversation");
1467
+ const unavailable = normalized.code === "not-found" || normalized.code === "forbidden";
1468
+ store.setConversationReadFailure(id, { error: normalized, unavailable });
1469
+ if (unavailable) {
1470
+ report({
1471
+ level: "warn",
1472
+ message: normalized.message,
1473
+ remedy: normalized.remedy
1474
+ });
1475
+ return;
1476
+ }
1477
+ reportError(normalized, "openConversation");
1453
1478
  }
1454
1479
  if (openChannels.has(id) || disposed) return;
1455
1480
  const handle = manager.open({