@ai-matrx/messaging 0.10.4 → 0.11.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/dist/index.d.cts CHANGED
@@ -137,6 +137,39 @@ interface Message {
137
137
  /** Set only while this message is in the outbox and its last send failed. */
138
138
  readonly failureReason?: string | undefined;
139
139
  }
140
+ /**
141
+ * THE ARCHIVED-ITEMS LAW's three states (Arman, 2026-09-09 —
142
+ * `common-docs/policies/archived-items.md`):
143
+ *
144
+ * > "everything should have an archive filter, and the default should always
145
+ * > hide archived, but seeing archived items should be one or two clicks
146
+ * > away … this is a system wide decision for every single item everywhere in
147
+ * > our system, for every single table and every single page."
148
+ *
149
+ * | value | what the list shows |
150
+ * |------------|--------------------------|
151
+ * | `active` | only un-archived rows |
152
+ * | `archived` | only archived rows |
153
+ * | `all` | every row |
154
+ *
155
+ * The same three words the rest of the platform uses — `ArchivedFilter` in
156
+ * matrx-frontend's `lib/entity-list`, `p_archived` on the `agx_*`/`wfx_*` and
157
+ * DM list RPCs, `ArchiveFilter` in `@ai-matrx/design-system`. A person meets
158
+ * ONE control, not one per app.
159
+ *
160
+ * 🚨 Three states, never a boolean. A boolean cannot say "archived only",
161
+ * which is exactly the view someone looking for a conversation they archived
162
+ * last month needs.
163
+ */
164
+ type MessagingArchiveFilter = "active" | "archived" | "all";
165
+ /** The platform default: a list hides archived rows until asked. */
166
+ declare const DEFAULT_MESSAGING_ARCHIVE_FILTER: MessagingArchiveFilter;
167
+ /**
168
+ * Narrow an untrusted value (a stored preference, a URL param, a host knob) to
169
+ * the tri-state. Anything unrecognised falls back to the default rather than
170
+ * silently widening a list to archived conversations.
171
+ */
172
+ declare function toMessagingArchiveFilter(value: unknown, fallback?: MessagingArchiveFilter): MessagingArchiveFilter;
140
173
  interface ConversationSummary {
141
174
  readonly conversation: Conversation;
142
175
  readonly participants: readonly UserSummary[];
@@ -770,7 +803,30 @@ interface MessagingRepository {
770
803
  listConversations(args?: {
771
804
  limit?: number;
772
805
  cursor?: ConversationCursor | null;
806
+ /**
807
+ * THE ARCHIVED-ITEMS LAW. Omitted means `active` — archived conversations
808
+ * are hidden until a caller asks for them. This is a REQUEST to the reader
809
+ * (the RPC's `p_archived`), never a client-side sieve, so `hasMore` and the
810
+ * cursor describe the rows the caller actually asked for.
811
+ */
812
+ archived?: MessagingArchiveFilter;
773
813
  }): Promise<Page<ConversationSummary, ConversationCursor>>;
814
+ /**
815
+ * How many ARCHIVED conversations the caller has — the number a reveal
816
+ * control prints ("Archived (12)").
817
+ *
818
+ * 🚨 It is capped, and it says so. A count is read by listing archived rows
819
+ * up to `limit`; if that many come back the result is `exact: false` and the
820
+ * UI must render "12+", never a confident "12". A screen never lies: a cap
821
+ * silently presented as a total is the same defect as a badge counting rows
822
+ * the list hides.
823
+ */
824
+ countArchivedConversations(args?: {
825
+ limit?: number;
826
+ }): Promise<{
827
+ count: number;
828
+ exact: boolean;
829
+ }>;
774
830
  getConversation(id: ConversationId): Promise<Conversation>;
775
831
  listMessages(conversationId: ConversationId, args?: {
776
832
  limit?: number;
@@ -857,11 +913,38 @@ interface MessagingSnapshot {
857
913
  readonly activeConversationId: ConversationId | null;
858
914
  /** Conversations WITH unread, not total unread messages (the origin's semantics). */
859
915
  readonly totalUnreadConversations: number;
916
+ /**
917
+ * THE ARCHIVED-ITEMS LAW's state for this inbox. `conversations` above is
918
+ * ALWAYS the rows this filter asked the server for — never a superset a
919
+ * surface is expected to sieve, so counts and pagination cannot disagree
920
+ * with what renders.
921
+ */
922
+ readonly archiveFilter: MessagingArchiveFilter;
923
+ /**
924
+ * How many archived conversations exist, for the reveal control's label, or
925
+ * `null` before the first count lands. `exact: false` means the count hit its
926
+ * cap and the label must read "N+" — a capped number shown as a total is the
927
+ * same lie as a badge counting hidden rows.
928
+ */
929
+ readonly archivedCount: {
930
+ readonly count: number;
931
+ readonly exact: boolean;
932
+ } | null;
860
933
  }
861
934
  interface MessagingStore {
862
935
  snapshot(): MessagingSnapshot;
863
936
  subscribe(listener: (snapshot: MessagingSnapshot) => void): () => void;
864
937
  setConversations(items: readonly ConversationSummary[], hasMore: boolean): void;
938
+ /**
939
+ * Switch the archive axis. Clears the list and marks it UNLOADED: the rows
940
+ * on screen belong to the old filter, and leaving them there while the new
941
+ * page is in flight shows active conversations under an "Archived" heading.
942
+ */
943
+ setArchiveFilter(next: MessagingArchiveFilter): void;
944
+ setArchivedCount(value: {
945
+ count: number;
946
+ exact: boolean;
947
+ } | null): void;
865
948
  appendConversations(items: readonly ConversationSummary[], hasMore: boolean): void;
866
949
  upsertConversation(item: ConversationSummary): void;
867
950
  removeConversation(id: ConversationId): void;
@@ -936,6 +1019,15 @@ interface MessagingEngineOptions {
936
1019
  onIncoming?: ((message: Message) => void) | undefined;
937
1020
  conversationPageSize?: number | undefined;
938
1021
  messagePageSize?: number | undefined;
1022
+ /**
1023
+ * 🚨 OPINIONS BECOME KNOBS. THE ARCHIVED-ITEMS LAW's clause 6 says the
1024
+ * filter's initial state is an org/user-configurable setting, not code
1025
+ * taste. The PLATFORM default is `active` (hide archived) and a host that
1026
+ * passes nothing gets exactly that; a host wires this to whatever its own
1027
+ * setting resolves to. The person can always change it from the control on
1028
+ * the list.
1029
+ */
1030
+ archiveFilter?: MessagingArchiveFilter | undefined;
939
1031
  }
940
1032
  interface MessagingEngine {
941
1033
  readonly store: MessagingStore;
@@ -944,6 +1036,14 @@ interface MessagingEngine {
944
1036
  /** Load page one of the inbox and start the inbox channel. */
945
1037
  start(): Promise<void>;
946
1038
  loadMoreConversations(): Promise<void>;
1039
+ /**
1040
+ * THE ARCHIVED-ITEMS LAW's control. Re-reads page one under the new state
1041
+ * (the filter is a REQUEST to the server, never a client-side sieve) and
1042
+ * refreshes the archived count the reveal label prints.
1043
+ */
1044
+ setArchiveFilter(next: MessagingArchiveFilter): Promise<void>;
1045
+ /** Re-read how many archived conversations exist, for the reveal's label. */
1046
+ refreshArchivedCount(): Promise<void>;
947
1047
  /** Open a conversation: load its thread and subscribe to its channel. */
948
1048
  openConversation(id: ConversationId): Promise<void>;
949
1049
  closeConversation(id: ConversationId): void;
@@ -1119,4 +1219,4 @@ declare function summarizeText(content: string, maxLength?: number): string;
1119
1219
  /** Serialize picked references into a fence the platform's other readers accept. */
1120
1220
  declare function composeFence(references: readonly MatrxReference[]): string;
1121
1221
 
1122
- export { type ActionChoice, type ActionContext, type ActionHandler, type ActionOutcome, type ActionReceipt, type ActionRegistry, type ActorPresentation, type AiCallArgs, type AiCapability, type AiResult, type Attachment, type ClientMessageId, type Conversation, type ConversationCursor, type ConversationId, type ConversationSummary, type ConversationThread, type ConversationType, type DeliveryState, type DraftMessage, type EngineDiagnostic, type JsonObject, type JsonValue, MESSAGING_EVENTS, MESSAGING_RPC_SCHEMA, MESSAGING_SCHEMA, type MatrxReference, type Message, type MessageAction, type MessageCursor, type MessageGroup, type MessageId, type MessageKind, type MessagingAgentIdentity, type MessagingAgents, type MessagingAi, type MessagingAiOptions, type MessagingEngine, type MessagingEngineOptions, MessagingError, type MessagingErrorCode, type MessagingIdentity, type MessagingRepository, type MessagingSnapshot, type MessagingStore, type MessagingSupabaseClient, type MessagingSupabaseInternal, type OrganizationId, type Outbox, type OutboxEntry, type OutboxOptions, type OutboxStorage, type Page, type Participant, type ParticipantRole, type PostgrestFilterLike, type PostgrestLikeResponse, type PostgrestTableLike, RPCS, type ReadCache, type ReadCacheOptions, type RepositoryOptions, type SchemaLike, type SessionResolver, type SupabaseLike, TABLES, type TextSegment, type UserId, type UserSummary, asClientMessageId, asConversationId, asMessageId, asOrganizationId, asUserId, composeFence, conversationTopic, createActionRegistry, createMemoryOutboxStorage, createMessagingAi, createMessagingEngine, createMessagingRepository, createMessagingStore, createOutbox, createReadCache, createWebOutboxStorage, effectiveActorKey, extractReferences, formatConversationTime, formatDateSeparator, formatLastSeen, formatMessageTime, formatTypists, groupMessages, inboxTopic, invalidResponse, isSameDay, messagingClientId, normalizeMessagingError, optimisticMessage, participantNames, projectConversationSummary, projectMessage, projectMessageAction, projectParticipantRole, projectUserSummary, resolveActor, splitText, summarizeText, unreadCutoff };
1222
+ export { type ActionChoice, type ActionContext, type ActionHandler, type ActionOutcome, type ActionReceipt, type ActionRegistry, type ActorPresentation, type AiCallArgs, type AiCapability, type AiResult, type Attachment, type ClientMessageId, type Conversation, type ConversationCursor, type ConversationId, type ConversationSummary, type ConversationThread, type ConversationType, DEFAULT_MESSAGING_ARCHIVE_FILTER, type DeliveryState, type DraftMessage, type EngineDiagnostic, type JsonObject, type JsonValue, MESSAGING_EVENTS, MESSAGING_RPC_SCHEMA, MESSAGING_SCHEMA, type MatrxReference, type Message, type MessageAction, type MessageCursor, type MessageGroup, type MessageId, type MessageKind, type MessagingAgentIdentity, type MessagingAgents, type MessagingAi, type MessagingAiOptions, type MessagingArchiveFilter, type MessagingEngine, type MessagingEngineOptions, MessagingError, type MessagingErrorCode, type MessagingIdentity, type MessagingRepository, type MessagingSnapshot, type MessagingStore, type MessagingSupabaseClient, type MessagingSupabaseInternal, type OrganizationId, type Outbox, type OutboxEntry, type OutboxOptions, type OutboxStorage, type Page, type Participant, type ParticipantRole, type PostgrestFilterLike, type PostgrestLikeResponse, type PostgrestTableLike, RPCS, type ReadCache, type ReadCacheOptions, type RepositoryOptions, type SchemaLike, type SessionResolver, type SupabaseLike, TABLES, type TextSegment, type UserId, type UserSummary, asClientMessageId, asConversationId, asMessageId, asOrganizationId, asUserId, composeFence, conversationTopic, createActionRegistry, createMemoryOutboxStorage, createMessagingAi, createMessagingEngine, createMessagingRepository, createMessagingStore, createOutbox, createReadCache, createWebOutboxStorage, effectiveActorKey, extractReferences, formatConversationTime, formatDateSeparator, formatLastSeen, formatMessageTime, formatTypists, groupMessages, inboxTopic, invalidResponse, isSameDay, messagingClientId, normalizeMessagingError, optimisticMessage, participantNames, projectConversationSummary, projectMessage, projectMessageAction, projectParticipantRole, projectUserSummary, resolveActor, splitText, summarizeText, toMessagingArchiveFilter, unreadCutoff };
package/dist/index.d.ts CHANGED
@@ -137,6 +137,39 @@ interface Message {
137
137
  /** Set only while this message is in the outbox and its last send failed. */
138
138
  readonly failureReason?: string | undefined;
139
139
  }
140
+ /**
141
+ * THE ARCHIVED-ITEMS LAW's three states (Arman, 2026-09-09 —
142
+ * `common-docs/policies/archived-items.md`):
143
+ *
144
+ * > "everything should have an archive filter, and the default should always
145
+ * > hide archived, but seeing archived items should be one or two clicks
146
+ * > away … this is a system wide decision for every single item everywhere in
147
+ * > our system, for every single table and every single page."
148
+ *
149
+ * | value | what the list shows |
150
+ * |------------|--------------------------|
151
+ * | `active` | only un-archived rows |
152
+ * | `archived` | only archived rows |
153
+ * | `all` | every row |
154
+ *
155
+ * The same three words the rest of the platform uses — `ArchivedFilter` in
156
+ * matrx-frontend's `lib/entity-list`, `p_archived` on the `agx_*`/`wfx_*` and
157
+ * DM list RPCs, `ArchiveFilter` in `@ai-matrx/design-system`. A person meets
158
+ * ONE control, not one per app.
159
+ *
160
+ * 🚨 Three states, never a boolean. A boolean cannot say "archived only",
161
+ * which is exactly the view someone looking for a conversation they archived
162
+ * last month needs.
163
+ */
164
+ type MessagingArchiveFilter = "active" | "archived" | "all";
165
+ /** The platform default: a list hides archived rows until asked. */
166
+ declare const DEFAULT_MESSAGING_ARCHIVE_FILTER: MessagingArchiveFilter;
167
+ /**
168
+ * Narrow an untrusted value (a stored preference, a URL param, a host knob) to
169
+ * the tri-state. Anything unrecognised falls back to the default rather than
170
+ * silently widening a list to archived conversations.
171
+ */
172
+ declare function toMessagingArchiveFilter(value: unknown, fallback?: MessagingArchiveFilter): MessagingArchiveFilter;
140
173
  interface ConversationSummary {
141
174
  readonly conversation: Conversation;
142
175
  readonly participants: readonly UserSummary[];
@@ -770,7 +803,30 @@ interface MessagingRepository {
770
803
  listConversations(args?: {
771
804
  limit?: number;
772
805
  cursor?: ConversationCursor | null;
806
+ /**
807
+ * THE ARCHIVED-ITEMS LAW. Omitted means `active` — archived conversations
808
+ * are hidden until a caller asks for them. This is a REQUEST to the reader
809
+ * (the RPC's `p_archived`), never a client-side sieve, so `hasMore` and the
810
+ * cursor describe the rows the caller actually asked for.
811
+ */
812
+ archived?: MessagingArchiveFilter;
773
813
  }): Promise<Page<ConversationSummary, ConversationCursor>>;
814
+ /**
815
+ * How many ARCHIVED conversations the caller has — the number a reveal
816
+ * control prints ("Archived (12)").
817
+ *
818
+ * 🚨 It is capped, and it says so. A count is read by listing archived rows
819
+ * up to `limit`; if that many come back the result is `exact: false` and the
820
+ * UI must render "12+", never a confident "12". A screen never lies: a cap
821
+ * silently presented as a total is the same defect as a badge counting rows
822
+ * the list hides.
823
+ */
824
+ countArchivedConversations(args?: {
825
+ limit?: number;
826
+ }): Promise<{
827
+ count: number;
828
+ exact: boolean;
829
+ }>;
774
830
  getConversation(id: ConversationId): Promise<Conversation>;
775
831
  listMessages(conversationId: ConversationId, args?: {
776
832
  limit?: number;
@@ -857,11 +913,38 @@ interface MessagingSnapshot {
857
913
  readonly activeConversationId: ConversationId | null;
858
914
  /** Conversations WITH unread, not total unread messages (the origin's semantics). */
859
915
  readonly totalUnreadConversations: number;
916
+ /**
917
+ * THE ARCHIVED-ITEMS LAW's state for this inbox. `conversations` above is
918
+ * ALWAYS the rows this filter asked the server for — never a superset a
919
+ * surface is expected to sieve, so counts and pagination cannot disagree
920
+ * with what renders.
921
+ */
922
+ readonly archiveFilter: MessagingArchiveFilter;
923
+ /**
924
+ * How many archived conversations exist, for the reveal control's label, or
925
+ * `null` before the first count lands. `exact: false` means the count hit its
926
+ * cap and the label must read "N+" — a capped number shown as a total is the
927
+ * same lie as a badge counting hidden rows.
928
+ */
929
+ readonly archivedCount: {
930
+ readonly count: number;
931
+ readonly exact: boolean;
932
+ } | null;
860
933
  }
861
934
  interface MessagingStore {
862
935
  snapshot(): MessagingSnapshot;
863
936
  subscribe(listener: (snapshot: MessagingSnapshot) => void): () => void;
864
937
  setConversations(items: readonly ConversationSummary[], hasMore: boolean): void;
938
+ /**
939
+ * Switch the archive axis. Clears the list and marks it UNLOADED: the rows
940
+ * on screen belong to the old filter, and leaving them there while the new
941
+ * page is in flight shows active conversations under an "Archived" heading.
942
+ */
943
+ setArchiveFilter(next: MessagingArchiveFilter): void;
944
+ setArchivedCount(value: {
945
+ count: number;
946
+ exact: boolean;
947
+ } | null): void;
865
948
  appendConversations(items: readonly ConversationSummary[], hasMore: boolean): void;
866
949
  upsertConversation(item: ConversationSummary): void;
867
950
  removeConversation(id: ConversationId): void;
@@ -936,6 +1019,15 @@ interface MessagingEngineOptions {
936
1019
  onIncoming?: ((message: Message) => void) | undefined;
937
1020
  conversationPageSize?: number | undefined;
938
1021
  messagePageSize?: number | undefined;
1022
+ /**
1023
+ * 🚨 OPINIONS BECOME KNOBS. THE ARCHIVED-ITEMS LAW's clause 6 says the
1024
+ * filter's initial state is an org/user-configurable setting, not code
1025
+ * taste. The PLATFORM default is `active` (hide archived) and a host that
1026
+ * passes nothing gets exactly that; a host wires this to whatever its own
1027
+ * setting resolves to. The person can always change it from the control on
1028
+ * the list.
1029
+ */
1030
+ archiveFilter?: MessagingArchiveFilter | undefined;
939
1031
  }
940
1032
  interface MessagingEngine {
941
1033
  readonly store: MessagingStore;
@@ -944,6 +1036,14 @@ interface MessagingEngine {
944
1036
  /** Load page one of the inbox and start the inbox channel. */
945
1037
  start(): Promise<void>;
946
1038
  loadMoreConversations(): Promise<void>;
1039
+ /**
1040
+ * THE ARCHIVED-ITEMS LAW's control. Re-reads page one under the new state
1041
+ * (the filter is a REQUEST to the server, never a client-side sieve) and
1042
+ * refreshes the archived count the reveal label prints.
1043
+ */
1044
+ setArchiveFilter(next: MessagingArchiveFilter): Promise<void>;
1045
+ /** Re-read how many archived conversations exist, for the reveal's label. */
1046
+ refreshArchivedCount(): Promise<void>;
947
1047
  /** Open a conversation: load its thread and subscribe to its channel. */
948
1048
  openConversation(id: ConversationId): Promise<void>;
949
1049
  closeConversation(id: ConversationId): void;
@@ -1119,4 +1219,4 @@ declare function summarizeText(content: string, maxLength?: number): string;
1119
1219
  /** Serialize picked references into a fence the platform's other readers accept. */
1120
1220
  declare function composeFence(references: readonly MatrxReference[]): string;
1121
1221
 
1122
- export { type ActionChoice, type ActionContext, type ActionHandler, type ActionOutcome, type ActionReceipt, type ActionRegistry, type ActorPresentation, type AiCallArgs, type AiCapability, type AiResult, type Attachment, type ClientMessageId, type Conversation, type ConversationCursor, type ConversationId, type ConversationSummary, type ConversationThread, type ConversationType, type DeliveryState, type DraftMessage, type EngineDiagnostic, type JsonObject, type JsonValue, MESSAGING_EVENTS, MESSAGING_RPC_SCHEMA, MESSAGING_SCHEMA, type MatrxReference, type Message, type MessageAction, type MessageCursor, type MessageGroup, type MessageId, type MessageKind, type MessagingAgentIdentity, type MessagingAgents, type MessagingAi, type MessagingAiOptions, type MessagingEngine, type MessagingEngineOptions, MessagingError, type MessagingErrorCode, type MessagingIdentity, type MessagingRepository, type MessagingSnapshot, type MessagingStore, type MessagingSupabaseClient, type MessagingSupabaseInternal, type OrganizationId, type Outbox, type OutboxEntry, type OutboxOptions, type OutboxStorage, type Page, type Participant, type ParticipantRole, type PostgrestFilterLike, type PostgrestLikeResponse, type PostgrestTableLike, RPCS, type ReadCache, type ReadCacheOptions, type RepositoryOptions, type SchemaLike, type SessionResolver, type SupabaseLike, TABLES, type TextSegment, type UserId, type UserSummary, asClientMessageId, asConversationId, asMessageId, asOrganizationId, asUserId, composeFence, conversationTopic, createActionRegistry, createMemoryOutboxStorage, createMessagingAi, createMessagingEngine, createMessagingRepository, createMessagingStore, createOutbox, createReadCache, createWebOutboxStorage, effectiveActorKey, extractReferences, formatConversationTime, formatDateSeparator, formatLastSeen, formatMessageTime, formatTypists, groupMessages, inboxTopic, invalidResponse, isSameDay, messagingClientId, normalizeMessagingError, optimisticMessage, participantNames, projectConversationSummary, projectMessage, projectMessageAction, projectParticipantRole, projectUserSummary, resolveActor, splitText, summarizeText, unreadCutoff };
1222
+ export { type ActionChoice, type ActionContext, type ActionHandler, type ActionOutcome, type ActionReceipt, type ActionRegistry, type ActorPresentation, type AiCallArgs, type AiCapability, type AiResult, type Attachment, type ClientMessageId, type Conversation, type ConversationCursor, type ConversationId, type ConversationSummary, type ConversationThread, type ConversationType, DEFAULT_MESSAGING_ARCHIVE_FILTER, type DeliveryState, type DraftMessage, type EngineDiagnostic, type JsonObject, type JsonValue, MESSAGING_EVENTS, MESSAGING_RPC_SCHEMA, MESSAGING_SCHEMA, type MatrxReference, type Message, type MessageAction, type MessageCursor, type MessageGroup, type MessageId, type MessageKind, type MessagingAgentIdentity, type MessagingAgents, type MessagingAi, type MessagingAiOptions, type MessagingArchiveFilter, type MessagingEngine, type MessagingEngineOptions, MessagingError, type MessagingErrorCode, type MessagingIdentity, type MessagingRepository, type MessagingSnapshot, type MessagingStore, type MessagingSupabaseClient, type MessagingSupabaseInternal, type OrganizationId, type Outbox, type OutboxEntry, type OutboxOptions, type OutboxStorage, type Page, type Participant, type ParticipantRole, type PostgrestFilterLike, type PostgrestLikeResponse, type PostgrestTableLike, RPCS, type ReadCache, type ReadCacheOptions, type RepositoryOptions, type SchemaLike, type SessionResolver, type SupabaseLike, TABLES, type TextSegment, type UserId, type UserSummary, asClientMessageId, asConversationId, asMessageId, asOrganizationId, asUserId, composeFence, conversationTopic, createActionRegistry, createMemoryOutboxStorage, createMessagingAi, createMessagingEngine, createMessagingRepository, createMessagingStore, createOutbox, createReadCache, createWebOutboxStorage, effectiveActorKey, extractReferences, formatConversationTime, formatDateSeparator, formatLastSeen, formatMessageTime, formatTypists, groupMessages, inboxTopic, invalidResponse, isSameDay, messagingClientId, normalizeMessagingError, optimisticMessage, participantNames, projectConversationSummary, projectMessage, projectMessageAction, projectParticipantRole, projectUserSummary, resolveActor, splitText, summarizeText, toMessagingArchiveFilter, unreadCutoff };
package/dist/index.js CHANGED
@@ -920,6 +920,17 @@ function projectConversationSummary(row, viewerId, fallbackOrganizationId) {
920
920
  };
921
921
  }
922
922
 
923
+ // src/core/types.ts
924
+ var asConversationId = (value) => value;
925
+ var asMessageId = (value) => value;
926
+ var asUserId = (value) => value;
927
+ var asOrganizationId = (value) => value;
928
+ var asClientMessageId = (value) => value;
929
+ var DEFAULT_MESSAGING_ARCHIVE_FILTER = "active";
930
+ function toMessagingArchiveFilter(value, fallback = DEFAULT_MESSAGING_ARCHIVE_FILTER) {
931
+ return value === "active" || value === "archived" || value === "all" ? value : fallback;
932
+ }
933
+
923
934
  // src/core/store.ts
924
935
  function timeOf(message) {
925
936
  const stamp = message.editedAt ?? message.createdAt;
@@ -950,6 +961,8 @@ function createMessagingStore() {
950
961
  let conversations = [];
951
962
  let hasMoreConversations = false;
952
963
  let hasLoadedConversations = false;
964
+ let archiveFilter = DEFAULT_MESSAGING_ARCHIVE_FILTER;
965
+ let archivedCount = null;
953
966
  let threads = /* @__PURE__ */ new Map();
954
967
  let activeConversationId = null;
955
968
  const listeners = /* @__PURE__ */ new Set();
@@ -962,7 +975,9 @@ function createMessagingStore() {
962
975
  hasLoadedConversations,
963
976
  threads,
964
977
  activeConversationId,
965
- totalUnreadConversations: conversations.filter((item) => item.unreadCount > 0).length
978
+ totalUnreadConversations: conversations.filter((item) => item.unreadCount > 0).length,
979
+ archiveFilter,
980
+ archivedCount
966
981
  };
967
982
  return cached;
968
983
  }
@@ -1009,6 +1024,18 @@ function createMessagingStore() {
1009
1024
  hasLoadedConversations = true;
1010
1025
  emit();
1011
1026
  },
1027
+ setArchiveFilter(next) {
1028
+ if (next === archiveFilter) return;
1029
+ archiveFilter = next;
1030
+ conversations = [];
1031
+ hasMoreConversations = false;
1032
+ hasLoadedConversations = false;
1033
+ emit();
1034
+ },
1035
+ setArchivedCount(value) {
1036
+ archivedCount = value;
1037
+ emit();
1038
+ },
1012
1039
  appendConversations(items, hasMore) {
1013
1040
  const byId = new Map(conversations.map((item) => [item.conversation.id, item]));
1014
1041
  items.forEach((item) => byId.set(item.conversation.id, item));
@@ -1214,10 +1241,21 @@ function createMessagingEngine(options) {
1214
1241
  openChannels.get(message.conversationId)?.send(MESSAGING_EVENTS.message, message);
1215
1242
  }
1216
1243
  async function reloadInbox() {
1217
- const page = await repository.listConversations({ limit: conversationPageSize });
1244
+ if (disposed) return;
1245
+ const page = await repository.listConversations({
1246
+ limit: conversationPageSize,
1247
+ archived: store.snapshot().archiveFilter
1248
+ });
1249
+ if (disposed) return;
1218
1250
  conversationCursor = page.nextCursor;
1219
1251
  store.setConversations(page.items, page.hasMore);
1220
1252
  }
1253
+ async function reloadArchivedCount() {
1254
+ if (disposed) return;
1255
+ const value = await repository.countArchivedConversations();
1256
+ if (disposed) return;
1257
+ store.setArchivedCount(value);
1258
+ }
1221
1259
  async function backfillConversation(id) {
1222
1260
  const thread = store.snapshot().threads.get(id);
1223
1261
  const since = thread?.latestAt ?? null;
@@ -1244,7 +1282,13 @@ function createMessagingEngine(options) {
1244
1282
  outbox,
1245
1283
  identity,
1246
1284
  async start() {
1285
+ if (options.archiveFilter !== void 0) {
1286
+ store.setArchiveFilter(options.archiveFilter);
1287
+ }
1247
1288
  await reloadInbox();
1289
+ void reloadArchivedCount().catch(
1290
+ (error) => reportError(error, "refreshArchivedCount")
1291
+ );
1248
1292
  if (disposed || inboxChannel !== null) return;
1249
1293
  inboxChannel = manager.open({
1250
1294
  topic: inboxTopic(identity.userId),
@@ -1259,7 +1303,7 @@ function createMessagingEngine(options) {
1259
1303
  const message = projectMessage(row, identity.organizationId);
1260
1304
  const known = store.snapshot().conversations.some((item) => item.conversation.id === message.conversationId);
1261
1305
  if (!known) {
1262
- void reloadInbox();
1306
+ void reloadInbox().catch((error) => reportError(error, "inboxRefresh"));
1263
1307
  return;
1264
1308
  }
1265
1309
  store.ingest(message);
@@ -1275,7 +1319,7 @@ function createMessagingEngine(options) {
1275
1319
  filter: `user_id=eq.${identity.userId}`,
1276
1320
  rowId: (row) => typeof row["id"] === "string" ? row["id"] : void 0,
1277
1321
  onChange: () => {
1278
- void reloadInbox();
1322
+ void reloadInbox().catch((error) => reportError(error, "inboxRefresh"));
1279
1323
  }
1280
1324
  }
1281
1325
  ],
@@ -1286,12 +1330,33 @@ function createMessagingEngine(options) {
1286
1330
  }
1287
1331
  });
1288
1332
  },
1333
+ async setArchiveFilter(next) {
1334
+ if (next === store.snapshot().archiveFilter) return;
1335
+ store.setArchiveFilter(next);
1336
+ conversationCursor = null;
1337
+ try {
1338
+ await reloadInbox();
1339
+ } catch (error) {
1340
+ reportError(error, "setArchiveFilter");
1341
+ }
1342
+ await reloadArchivedCount().catch(
1343
+ (error) => reportError(error, "refreshArchivedCount")
1344
+ );
1345
+ },
1346
+ async refreshArchivedCount() {
1347
+ try {
1348
+ await reloadArchivedCount();
1349
+ } catch (error) {
1350
+ reportError(error, "refreshArchivedCount");
1351
+ }
1352
+ },
1289
1353
  async loadMoreConversations() {
1290
1354
  if (conversationCursor === null) return;
1291
1355
  try {
1292
1356
  const page = await repository.listConversations({
1293
1357
  limit: conversationPageSize,
1294
- cursor: conversationCursor
1358
+ cursor: conversationCursor,
1359
+ archived: store.snapshot().archiveFilter
1295
1360
  });
1296
1361
  conversationCursor = page.nextCursor;
1297
1362
  store.appendConversations(page.items, page.hasMore);
@@ -1600,6 +1665,7 @@ function createMessagingRepository(options) {
1600
1665
  identity,
1601
1666
  async listConversations(args = {}) {
1602
1667
  const limit = args.limit ?? 30;
1668
+ const archived = args.archived ?? DEFAULT_MESSAGING_ARCHIVE_FILTER;
1603
1669
  const operation = "listConversations";
1604
1670
  const rows = await withSessionRetry(
1605
1671
  operation,
@@ -1609,7 +1675,12 @@ function createMessagingRepository(options) {
1609
1675
  p_user_id: identity.userId,
1610
1676
  p_limit: limit + 1,
1611
1677
  p_before_sort_at: args.cursor?.beforeSortAt ?? null,
1612
- p_before_conversation_id: args.cursor?.beforeConversationId ?? null
1678
+ p_before_conversation_id: args.cursor?.beforeConversationId ?? null,
1679
+ // THE ARCHIVED-ITEMS LAW, SERVER-side. `get_dm_conversations_with_details`
1680
+ // used to hardcode `is_archived IS FALSE` with no parameter at all,
1681
+ // so an archived conversation was not hidden — it was unreachable.
1682
+ // The RPC gained `p_archived` on 2026-09-09 (register row R1).
1683
+ p_archived: archived
1613
1684
  },
1614
1685
  operation
1615
1686
  )
@@ -1629,6 +1700,29 @@ function createMessagingRepository(options) {
1629
1700
  nextCursor: hasMore && last !== void 0 ? { beforeSortAt: last.sortAt, beforeConversationId: last.conversation.id } : null
1630
1701
  };
1631
1702
  },
1703
+ async countArchivedConversations(args = {}) {
1704
+ const limit = args.limit ?? 100;
1705
+ const operation = "countArchivedConversations";
1706
+ const rows = await withSessionRetry(
1707
+ operation,
1708
+ () => rpc(
1709
+ RPCS.conversationsWithDetails,
1710
+ {
1711
+ p_user_id: identity.userId,
1712
+ p_limit: limit + 1,
1713
+ p_before_sort_at: null,
1714
+ p_before_conversation_id: null,
1715
+ p_archived: "archived"
1716
+ },
1717
+ operation
1718
+ )
1719
+ );
1720
+ if (rows !== null && !Array.isArray(rows)) {
1721
+ throw invalidResponse(operation, `${RPCS.conversationsWithDetails} did not return rows`);
1722
+ }
1723
+ const found = (rows ?? []).length;
1724
+ return found > limit ? { count: limit, exact: false } : { count: found, exact: true };
1725
+ },
1632
1726
  async getConversation(id) {
1633
1727
  const operation = "getConversation";
1634
1728
  const { data, error } = await withSessionRetry(
@@ -1862,14 +1956,8 @@ function createMessagingRepository(options) {
1862
1956
  };
1863
1957
  return repository;
1864
1958
  }
1865
-
1866
- // src/core/types.ts
1867
- var asConversationId = (value) => value;
1868
- var asMessageId = (value) => value;
1869
- var asUserId = (value) => value;
1870
- var asOrganizationId = (value) => value;
1871
- var asClientMessageId = (value) => value;
1872
1959
  export {
1960
+ DEFAULT_MESSAGING_ARCHIVE_FILTER,
1873
1961
  MESSAGING_EVENTS,
1874
1962
  MESSAGING_RPC_SCHEMA,
1875
1963
  MESSAGING_SCHEMA,
@@ -1915,6 +2003,7 @@ export {
1915
2003
  resolveActor,
1916
2004
  splitText,
1917
2005
  summarizeText,
2006
+ toMessagingArchiveFilter,
1918
2007
  unreadCutoff
1919
2008
  };
1920
2009
  //# sourceMappingURL=index.js.map