@ai-matrx/messaging 0.15.14 → 0.15.16

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/react.d.cts CHANGED
@@ -449,6 +449,49 @@ interface MessagingAi {
449
449
  declare function unreadCutoff(messages: readonly Message[], unreadCount: number): string | null;
450
450
  declare function createMessagingAi(options: MessagingAiOptions): MessagingAi;
451
451
 
452
+ /**
453
+ * ONE error classification for the whole package (C22).
454
+ *
455
+ * The banned alternative is the host catching a PostgREST error and deciding
456
+ * for itself whether it was an auth blip, a permission denial, or a real bug —
457
+ * which is how the same three-branch `if` ends up copy-pasted into every
458
+ * consumer and drifts. Everything this package throws is a `MessagingError`
459
+ * with a stable `code`, and the two that hosts genuinely treat differently
460
+ * (`session-unavailable`, `forbidden`) say so in the type.
461
+ *
462
+ * The incident behind `session-unavailable`: a DM reader mounted before the
463
+ * access token existed, every RPC came back `42501`, and the app captured 909
464
+ * errors in 0.6 seconds. A missing session is a NORMAL lifecycle moment — it
465
+ * warns and retries; it is never a red error.
466
+ */
467
+ type MessagingErrorCode = "session-unavailable" | "contention" | "forbidden" | "not-found" | "conflict" | "transport" | "invalid-response" | "write-not-applied" | "misconfigured"
468
+ /** A NEW conversation needs an organization to land in and none is selected. */
469
+ | "organization-required" | "unknown";
470
+ declare class MessagingError extends Error {
471
+ readonly code: MessagingErrorCode;
472
+ /** The remedy. Nothing fails silently, and nothing fails without saying what to do. */
473
+ readonly remedy: string;
474
+ readonly cause?: unknown;
475
+ constructor(code: MessagingErrorCode, message: string, remedy: string, cause?: unknown);
476
+ /** True when re-running the same call is the fix. */
477
+ get isRetryable(): boolean;
478
+ }
479
+ interface PostgrestErrorLike {
480
+ message: string;
481
+ code?: string | undefined;
482
+ details?: string | null | undefined;
483
+ }
484
+ /**
485
+ * THE ONE PLACE a raw PostgREST/transport error becomes a typed one. Called at
486
+ * every boundary in `repository.ts`; nothing else in the package inspects a raw
487
+ * error, and no consumer should ever have to.
488
+ */
489
+ declare function normalizeMessagingError(error: PostgrestErrorLike | Error | unknown, operation: string): MessagingError;
490
+ /** A response whose SHAPE is wrong — the ingress boundary, not the wire. */
491
+ declare function invalidResponse(operation: string, detail: string): MessagingError;
492
+ /** A mutation that PostgREST accepted syntactically but applied to no row. */
493
+ declare function writeNotApplied(operation: string, detail: string): MessagingError;
494
+
452
495
  /**
453
496
  * THE OUTBOX — a message you typed is NEVER lost (R2, table-stakes law).
454
497
  *
@@ -537,47 +580,6 @@ interface Outbox {
537
580
  }
538
581
  declare function createOutbox(options: OutboxOptions): Outbox;
539
582
 
540
- /**
541
- * ONE error classification for the whole package (C22).
542
- *
543
- * The banned alternative is the host catching a PostgREST error and deciding
544
- * for itself whether it was an auth blip, a permission denial, or a real bug —
545
- * which is how the same three-branch `if` ends up copy-pasted into every
546
- * consumer and drifts. Everything this package throws is a `MessagingError`
547
- * with a stable `code`, and the two that hosts genuinely treat differently
548
- * (`session-unavailable`, `forbidden`) say so in the type.
549
- *
550
- * The incident behind `session-unavailable`: a DM reader mounted before the
551
- * access token existed, every RPC came back `42501`, and the app captured 909
552
- * errors in 0.6 seconds. A missing session is a NORMAL lifecycle moment — it
553
- * warns and retries; it is never a red error.
554
- */
555
- type MessagingErrorCode = "session-unavailable" | "contention" | "forbidden" | "not-found" | "conflict" | "transport" | "invalid-response" | "misconfigured"
556
- /** A NEW conversation needs an organization to land in and none is selected. */
557
- | "organization-required" | "unknown";
558
- declare class MessagingError extends Error {
559
- readonly code: MessagingErrorCode;
560
- /** The remedy. Nothing fails silently, and nothing fails without saying what to do. */
561
- readonly remedy: string;
562
- readonly cause?: unknown;
563
- constructor(code: MessagingErrorCode, message: string, remedy: string, cause?: unknown);
564
- /** True when re-running the same call is the fix. */
565
- get isRetryable(): boolean;
566
- }
567
- interface PostgrestErrorLike {
568
- message: string;
569
- code?: string | undefined;
570
- details?: string | null | undefined;
571
- }
572
- /**
573
- * THE ONE PLACE a raw PostgREST/transport error becomes a typed one. Called at
574
- * every boundary in `repository.ts`; nothing else in the package inspects a raw
575
- * error, and no consumer should ever have to.
576
- */
577
- declare function normalizeMessagingError(error: PostgrestErrorLike | Error | unknown, operation: string): MessagingError;
578
- /** A response whose SHAPE is wrong — the ingress boundary, not the wire. */
579
- declare function invalidResponse(operation: string, detail: string): MessagingError;
580
-
581
583
  /**
582
584
  * THE STRUCTURAL SUPABASE SHAPE.
583
585
  *
@@ -609,6 +611,7 @@ interface PostgrestLikeResponse<T> {
609
611
  code?: string;
610
612
  details?: string | null;
611
613
  } | null;
614
+ count?: number | null;
612
615
  }
613
616
  interface PostgrestFilterLike<TRow> extends PromiseLike<PostgrestLikeResponse<TRow[]>> {
614
617
  eq(column: string, value: unknown): PostgrestFilterLike<TRow>;
@@ -622,12 +625,17 @@ interface PostgrestFilterLike<TRow> extends PromiseLike<PostgrestLikeResponse<TR
622
625
  ascending?: boolean;
623
626
  }): PostgrestFilterLike<TRow>;
624
627
  limit(count: number): PostgrestFilterLike<TRow>;
625
- select(columns?: string): PostgrestFilterLike<TRow>;
628
+ range(from: number, to: number): PostgrestFilterLike<TRow>;
629
+ select(columns?: string, options?: {
630
+ count?: "exact";
631
+ }): PostgrestFilterLike<TRow>;
626
632
  single(): PromiseLike<PostgrestLikeResponse<TRow>>;
627
633
  maybeSingle(): PromiseLike<PostgrestLikeResponse<TRow>>;
628
634
  }
629
635
  interface PostgrestTableLike<TRow> {
630
- select(columns?: string): PostgrestFilterLike<TRow>;
636
+ select(columns?: string, options?: {
637
+ count?: "exact";
638
+ }): PostgrestFilterLike<TRow>;
631
639
  insert(values: unknown): PostgrestFilterLike<TRow>;
632
640
  update(values: unknown): PostgrestFilterLike<TRow>;
633
641
  upsert(values: unknown, options?: {
@@ -920,6 +928,11 @@ interface MessagingRepository {
920
928
  exact: boolean;
921
929
  }>;
922
930
  getConversation(id: ConversationId): Promise<Conversation>;
931
+ /**
932
+ * One conversation's complete inbox/header row, independent of inbox paging.
933
+ * Deep links use this door when their conversation is not in the loaded page.
934
+ */
935
+ getConversationSummary(id: ConversationId): Promise<ConversationSummary>;
923
936
  listMessages(conversationId: ConversationId, args?: {
924
937
  limit?: number;
925
938
  cursor?: MessageCursor | null;
@@ -934,7 +947,8 @@ interface MessagingRepository {
934
947
  insertMessage(draft: DraftMessage, clientMessageId: ClientMessageId): Promise<Message>;
935
948
  editMessage(id: MessageId, content: string): Promise<Message>;
936
949
  deleteMessage(id: MessageId, forEveryone: boolean): Promise<void>;
937
- markRead(conversationId: ConversationId, at?: string): Promise<void>;
950
+ /** Persists the cutoff, then returns the server's remaining unread count. */
951
+ markRead(conversationId: ConversationId, at: string): Promise<number>;
938
952
  setConversationFlags(conversationId: ConversationId, flags: {
939
953
  isMuted?: boolean;
940
954
  isArchived?: boolean;
@@ -975,10 +989,10 @@ declare function createMessagingRepository(options: RepositoryOptions): Messagin
975
989
  * 4. **Order is `created_at` then `id`.** The unique tiebreaker is not
976
990
  * decoration: two messages in the same millisecond otherwise swap places on
977
991
  * every re-render.
978
- * 5. **The active conversation's unread count is forced to zero.** A stale
979
- * server count must never re-badge a conversation the user is reading — a
980
- * safety net the origin needed in three separate places, so it lives in one
981
- * place here.
992
+ * 5. **Navigation is not a read receipt.** Active-conversation state never
993
+ * clears unread counts. Only a confirmed participant write may do that, and
994
+ * overlapping confirmations apply monotonically so one baseline is never
995
+ * subtracted twice.
982
996
  */
983
997
 
984
998
  /**
@@ -1008,6 +1022,12 @@ interface ConversationThread {
1008
1022
  }
1009
1023
  interface MessagingSnapshot {
1010
1024
  readonly conversations: readonly ConversationSummary[];
1025
+ /**
1026
+ * Summaries known by id, including a directly opened conversation outside
1027
+ * the current filtered inbox page. Header lookup uses this map; inbox
1028
+ * rendering continues to use `conversations` exactly as returned by its page.
1029
+ */
1030
+ readonly conversationSummaries: ReadonlyMap<ConversationId, ConversationSummary>;
1011
1031
  readonly hasMoreConversations: boolean;
1012
1032
  /**
1013
1033
  * True once the inbox has actually been READ at least once.
@@ -1066,6 +1086,7 @@ interface MessagingStore {
1066
1086
  } | null): void;
1067
1087
  appendConversations(items: readonly ConversationSummary[], hasMore: boolean): void;
1068
1088
  upsertConversation(item: ConversationSummary): void;
1089
+ setConversationSummary(item: ConversationSummary): void;
1069
1090
  removeConversation(id: ConversationId): void;
1070
1091
  setActiveConversation(id: ConversationId | null): void;
1071
1092
  /** Record (or clear, with `null`) why a conversation could not be read. */
@@ -1073,12 +1094,19 @@ interface MessagingStore {
1073
1094
  setThread(id: ConversationId, messages: readonly Message[], args?: {
1074
1095
  hasMoreOlder?: boolean;
1075
1096
  }): void;
1097
+ clearThread(id: ConversationId): void;
1076
1098
  prependOlder(id: ConversationId, messages: readonly Message[], hasMoreOlder: boolean): void;
1077
1099
  /** The one door every incoming message uses. Returns what actually happened. */
1078
1100
  ingest(message: Message): "added" | "merged" | "dropped-stale" | "dropped-duplicate";
1079
1101
  ingestMany(messages: readonly Message[]): void;
1080
1102
  removeMessage(conversationId: ConversationId, messageId: MessageId): void;
1081
- markConversationRead(id: ConversationId): void;
1103
+ captureConversationRead(id: ConversationId, at: string): ConversationReadCutoff;
1104
+ /**
1105
+ * Apply a confirmed read through the exact cutoff persisted by the repository.
1106
+ * `unreadCount` is the portion visible when that write began; arrivals after
1107
+ * `at` remain unread when the confirmation comes back.
1108
+ */
1109
+ markConversationRead(id: ConversationId, cutoff: ConversationReadCutoff, canonicalUnreadCount?: number): void;
1082
1110
  /**
1083
1111
  * Move a conversation's preview + sort position to reflect a message, with NO
1084
1112
  * refetch. Sending a message must not cost a conversation-list RPC — at a
@@ -1090,6 +1118,12 @@ interface MessagingStore {
1090
1118
  }): void;
1091
1119
  setUnreadCount(id: ConversationId, count: number): void;
1092
1120
  }
1121
+ interface ConversationReadCutoff {
1122
+ readonly at: string;
1123
+ readonly unreadCount: number;
1124
+ /** Per-conversation unread-state revision observed when this read began. */
1125
+ readonly revision: number;
1126
+ }
1093
1127
  declare function createMessagingStore(): MessagingStore;
1094
1128
  /**
1095
1129
  * The optimistic row a composer shows the instant Enter is pressed. It carries
@@ -1129,6 +1163,12 @@ interface EngineDiagnostic {
1129
1163
  readonly level: "info" | "warn" | "error";
1130
1164
  readonly message: string;
1131
1165
  readonly remedy?: string | undefined;
1166
+ /** Stable machine-readable metadata for host diagnostic adapters. */
1167
+ readonly code?: MessagingErrorCode | undefined;
1168
+ readonly operation?: string | undefined;
1169
+ readonly isRetryable?: boolean | undefined;
1170
+ readonly error?: MessagingError | undefined;
1171
+ readonly conversationId?: ConversationId | undefined;
1132
1172
  }
1133
1173
  interface MessagingEngineOptions {
1134
1174
  repository: MessagingRepository;
@@ -1877,4 +1917,4 @@ declare function summarizeText(content: string, maxLength?: number): string;
1877
1917
  /** Serialize picked references into a fence the platform's other readers accept. */
1878
1918
  declare function composeFence(references: readonly MatrxReference[]): string;
1879
1919
 
1880
- export { type ActionChoice, type ActionContext, type ActionHandler, type ActionOutcome, type ActionReceipt, type ActionRegistry, type ActorPresentation, AgentTag, type AiCallArgs, type AiCapability, type AiResult, AlertIcon, type Attachment, Avatar, BotIcon, CheckIcon, ChevronLeftIcon, type ClientMessageId, ClockIcon, CloseIcon, Composer, type ComposerAttachment, type ComposerInputRenderProps, type Conversation, type ConversationAiContext, type ConversationCursor, type ConversationId, ConversationList, type ConversationListProps, type ConversationReadFailure, type ConversationRowWrapperProps, ConversationSkeleton, type ConversationSummary, type ConversationThread, type ConversationType, ConversationView, type ConversationViewProps, DEFAULT_MESSAGING_ARCHIVE_FILTER, type DeliveryState, DeliveryTick, DoubleCheckIcon, type DraftMessage, EmptyState, type EngineDiagnostic, type IncomingMessageContext, type JsonObject, type JsonValue, LinkIcon, MESSAGING_EVENTS, MESSAGING_RPC_SCHEMA, MESSAGING_SCHEMA, type MatrxReference, type Message, type MessageAction, MessageActionChips, type MessageActionRenderProps, type MessageActionRenderer, MessageBubble, type MessageCursor, type MessageGroup, type MessageId, type MessageKind, type MessageWrapperProps, type MessagingAgentIdentity, type MessagingAgents, type MessagingAi, type MessagingAiOptions, type MessagingArchiveFilter, type MessagingEngine, type MessagingEngineOptions, MessagingError, type MessagingErrorCode, type MessagingHost, type MessagingIdentity, MessagingInbox, type MessagingInboxProps, MessagingProvider, type MessagingProviderProps, type MessagingRepository, type MessagingSnapshot, type MessagingStore, type MessagingSupabaseClient, type MessagingSupabaseInternal, type OrganizationId, type Outbox, type OutboxEntry, type OutboxOptions, type OutboxStorage, type Page, PaperclipIcon, type Participant, type ParticipantRole, PlusIcon, type PostgrestFilterLike, type PostgrestLikeResponse, type PostgrestTableLike, RPCS, type ReadCache, type ReadCacheOptions, ReferenceCard, type ReferenceRenderer, ReplyIcon, type RepositoryOptions, SERVER_SESSION_IDENTITY, type SchemaLike, SearchIcon, SendIcon, type SessionIdentity, type SessionIdentitySnapshot, type SessionResolver, SparklesIcon, type SupabaseLike, TABLES, type TextSegment, TypingDots, type UseComposerResult, type UseConversationResult, type UseConversationsResult, type UseMessageActionResult, type UseMessagingAiResult, type UseTypistsResult, type UserId, type UserSummary, UsersIcon, asClientMessageId, asConversationId, asMessageId, asOrganizationId, asUserId, composeFence, conversationContextVariables, conversationTopic, createActionRegistry, createMemoryOutboxStorage, createMessagingAi, createMessagingEngine, createMessagingRepository, createMessagingStore, createOutbox, createReadCache, createSessionIdentity, 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, useComposer, useConversation, useConversations, useMessageAction, useMessagingAi, useMessagingHost, useMessagingSnapshot, useOnlineUserIds, useRequiredMessagingHost, useTypists };
1920
+ export { type ActionChoice, type ActionContext, type ActionHandler, type ActionOutcome, type ActionReceipt, type ActionRegistry, type ActorPresentation, AgentTag, type AiCallArgs, type AiCapability, type AiResult, AlertIcon, type Attachment, Avatar, BotIcon, CheckIcon, ChevronLeftIcon, type ClientMessageId, ClockIcon, CloseIcon, Composer, type ComposerAttachment, type ComposerInputRenderProps, type Conversation, type ConversationAiContext, type ConversationCursor, type ConversationId, ConversationList, type ConversationListProps, type ConversationReadCutoff, type ConversationReadFailure, type ConversationRowWrapperProps, ConversationSkeleton, type ConversationSummary, type ConversationThread, type ConversationType, ConversationView, type ConversationViewProps, DEFAULT_MESSAGING_ARCHIVE_FILTER, type DeliveryState, DeliveryTick, DoubleCheckIcon, type DraftMessage, EmptyState, type EngineDiagnostic, type IncomingMessageContext, type JsonObject, type JsonValue, LinkIcon, MESSAGING_EVENTS, MESSAGING_RPC_SCHEMA, MESSAGING_SCHEMA, type MatrxReference, type Message, type MessageAction, MessageActionChips, type MessageActionRenderProps, type MessageActionRenderer, MessageBubble, type MessageCursor, type MessageGroup, type MessageId, type MessageKind, type MessageWrapperProps, type MessagingAgentIdentity, type MessagingAgents, type MessagingAi, type MessagingAiOptions, type MessagingArchiveFilter, type MessagingEngine, type MessagingEngineOptions, MessagingError, type MessagingErrorCode, type MessagingHost, type MessagingIdentity, MessagingInbox, type MessagingInboxProps, MessagingProvider, type MessagingProviderProps, type MessagingRepository, type MessagingSnapshot, type MessagingStore, type MessagingSupabaseClient, type MessagingSupabaseInternal, type OrganizationId, type Outbox, type OutboxEntry, type OutboxOptions, type OutboxStorage, type Page, PaperclipIcon, type Participant, type ParticipantRole, PlusIcon, type PostgrestFilterLike, type PostgrestLikeResponse, type PostgrestTableLike, RPCS, type ReadCache, type ReadCacheOptions, ReferenceCard, type ReferenceRenderer, ReplyIcon, type RepositoryOptions, SERVER_SESSION_IDENTITY, type SchemaLike, SearchIcon, SendIcon, type SessionIdentity, type SessionIdentitySnapshot, type SessionResolver, SparklesIcon, type SupabaseLike, TABLES, type TextSegment, TypingDots, type UseComposerResult, type UseConversationResult, type UseConversationsResult, type UseMessageActionResult, type UseMessagingAiResult, type UseTypistsResult, type UserId, type UserSummary, UsersIcon, asClientMessageId, asConversationId, asMessageId, asOrganizationId, asUserId, composeFence, conversationContextVariables, conversationTopic, createActionRegistry, createMemoryOutboxStorage, createMessagingAi, createMessagingEngine, createMessagingRepository, createMessagingStore, createOutbox, createReadCache, createSessionIdentity, 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, useComposer, useConversation, useConversations, useMessageAction, useMessagingAi, useMessagingHost, useMessagingSnapshot, useOnlineUserIds, useRequiredMessagingHost, useTypists, writeNotApplied };
package/dist/react.d.ts CHANGED
@@ -449,6 +449,49 @@ interface MessagingAi {
449
449
  declare function unreadCutoff(messages: readonly Message[], unreadCount: number): string | null;
450
450
  declare function createMessagingAi(options: MessagingAiOptions): MessagingAi;
451
451
 
452
+ /**
453
+ * ONE error classification for the whole package (C22).
454
+ *
455
+ * The banned alternative is the host catching a PostgREST error and deciding
456
+ * for itself whether it was an auth blip, a permission denial, or a real bug —
457
+ * which is how the same three-branch `if` ends up copy-pasted into every
458
+ * consumer and drifts. Everything this package throws is a `MessagingError`
459
+ * with a stable `code`, and the two that hosts genuinely treat differently
460
+ * (`session-unavailable`, `forbidden`) say so in the type.
461
+ *
462
+ * The incident behind `session-unavailable`: a DM reader mounted before the
463
+ * access token existed, every RPC came back `42501`, and the app captured 909
464
+ * errors in 0.6 seconds. A missing session is a NORMAL lifecycle moment — it
465
+ * warns and retries; it is never a red error.
466
+ */
467
+ type MessagingErrorCode = "session-unavailable" | "contention" | "forbidden" | "not-found" | "conflict" | "transport" | "invalid-response" | "write-not-applied" | "misconfigured"
468
+ /** A NEW conversation needs an organization to land in and none is selected. */
469
+ | "organization-required" | "unknown";
470
+ declare class MessagingError extends Error {
471
+ readonly code: MessagingErrorCode;
472
+ /** The remedy. Nothing fails silently, and nothing fails without saying what to do. */
473
+ readonly remedy: string;
474
+ readonly cause?: unknown;
475
+ constructor(code: MessagingErrorCode, message: string, remedy: string, cause?: unknown);
476
+ /** True when re-running the same call is the fix. */
477
+ get isRetryable(): boolean;
478
+ }
479
+ interface PostgrestErrorLike {
480
+ message: string;
481
+ code?: string | undefined;
482
+ details?: string | null | undefined;
483
+ }
484
+ /**
485
+ * THE ONE PLACE a raw PostgREST/transport error becomes a typed one. Called at
486
+ * every boundary in `repository.ts`; nothing else in the package inspects a raw
487
+ * error, and no consumer should ever have to.
488
+ */
489
+ declare function normalizeMessagingError(error: PostgrestErrorLike | Error | unknown, operation: string): MessagingError;
490
+ /** A response whose SHAPE is wrong — the ingress boundary, not the wire. */
491
+ declare function invalidResponse(operation: string, detail: string): MessagingError;
492
+ /** A mutation that PostgREST accepted syntactically but applied to no row. */
493
+ declare function writeNotApplied(operation: string, detail: string): MessagingError;
494
+
452
495
  /**
453
496
  * THE OUTBOX — a message you typed is NEVER lost (R2, table-stakes law).
454
497
  *
@@ -537,47 +580,6 @@ interface Outbox {
537
580
  }
538
581
  declare function createOutbox(options: OutboxOptions): Outbox;
539
582
 
540
- /**
541
- * ONE error classification for the whole package (C22).
542
- *
543
- * The banned alternative is the host catching a PostgREST error and deciding
544
- * for itself whether it was an auth blip, a permission denial, or a real bug —
545
- * which is how the same three-branch `if` ends up copy-pasted into every
546
- * consumer and drifts. Everything this package throws is a `MessagingError`
547
- * with a stable `code`, and the two that hosts genuinely treat differently
548
- * (`session-unavailable`, `forbidden`) say so in the type.
549
- *
550
- * The incident behind `session-unavailable`: a DM reader mounted before the
551
- * access token existed, every RPC came back `42501`, and the app captured 909
552
- * errors in 0.6 seconds. A missing session is a NORMAL lifecycle moment — it
553
- * warns and retries; it is never a red error.
554
- */
555
- type MessagingErrorCode = "session-unavailable" | "contention" | "forbidden" | "not-found" | "conflict" | "transport" | "invalid-response" | "misconfigured"
556
- /** A NEW conversation needs an organization to land in and none is selected. */
557
- | "organization-required" | "unknown";
558
- declare class MessagingError extends Error {
559
- readonly code: MessagingErrorCode;
560
- /** The remedy. Nothing fails silently, and nothing fails without saying what to do. */
561
- readonly remedy: string;
562
- readonly cause?: unknown;
563
- constructor(code: MessagingErrorCode, message: string, remedy: string, cause?: unknown);
564
- /** True when re-running the same call is the fix. */
565
- get isRetryable(): boolean;
566
- }
567
- interface PostgrestErrorLike {
568
- message: string;
569
- code?: string | undefined;
570
- details?: string | null | undefined;
571
- }
572
- /**
573
- * THE ONE PLACE a raw PostgREST/transport error becomes a typed one. Called at
574
- * every boundary in `repository.ts`; nothing else in the package inspects a raw
575
- * error, and no consumer should ever have to.
576
- */
577
- declare function normalizeMessagingError(error: PostgrestErrorLike | Error | unknown, operation: string): MessagingError;
578
- /** A response whose SHAPE is wrong — the ingress boundary, not the wire. */
579
- declare function invalidResponse(operation: string, detail: string): MessagingError;
580
-
581
583
  /**
582
584
  * THE STRUCTURAL SUPABASE SHAPE.
583
585
  *
@@ -609,6 +611,7 @@ interface PostgrestLikeResponse<T> {
609
611
  code?: string;
610
612
  details?: string | null;
611
613
  } | null;
614
+ count?: number | null;
612
615
  }
613
616
  interface PostgrestFilterLike<TRow> extends PromiseLike<PostgrestLikeResponse<TRow[]>> {
614
617
  eq(column: string, value: unknown): PostgrestFilterLike<TRow>;
@@ -622,12 +625,17 @@ interface PostgrestFilterLike<TRow> extends PromiseLike<PostgrestLikeResponse<TR
622
625
  ascending?: boolean;
623
626
  }): PostgrestFilterLike<TRow>;
624
627
  limit(count: number): PostgrestFilterLike<TRow>;
625
- select(columns?: string): PostgrestFilterLike<TRow>;
628
+ range(from: number, to: number): PostgrestFilterLike<TRow>;
629
+ select(columns?: string, options?: {
630
+ count?: "exact";
631
+ }): PostgrestFilterLike<TRow>;
626
632
  single(): PromiseLike<PostgrestLikeResponse<TRow>>;
627
633
  maybeSingle(): PromiseLike<PostgrestLikeResponse<TRow>>;
628
634
  }
629
635
  interface PostgrestTableLike<TRow> {
630
- select(columns?: string): PostgrestFilterLike<TRow>;
636
+ select(columns?: string, options?: {
637
+ count?: "exact";
638
+ }): PostgrestFilterLike<TRow>;
631
639
  insert(values: unknown): PostgrestFilterLike<TRow>;
632
640
  update(values: unknown): PostgrestFilterLike<TRow>;
633
641
  upsert(values: unknown, options?: {
@@ -920,6 +928,11 @@ interface MessagingRepository {
920
928
  exact: boolean;
921
929
  }>;
922
930
  getConversation(id: ConversationId): Promise<Conversation>;
931
+ /**
932
+ * One conversation's complete inbox/header row, independent of inbox paging.
933
+ * Deep links use this door when their conversation is not in the loaded page.
934
+ */
935
+ getConversationSummary(id: ConversationId): Promise<ConversationSummary>;
923
936
  listMessages(conversationId: ConversationId, args?: {
924
937
  limit?: number;
925
938
  cursor?: MessageCursor | null;
@@ -934,7 +947,8 @@ interface MessagingRepository {
934
947
  insertMessage(draft: DraftMessage, clientMessageId: ClientMessageId): Promise<Message>;
935
948
  editMessage(id: MessageId, content: string): Promise<Message>;
936
949
  deleteMessage(id: MessageId, forEveryone: boolean): Promise<void>;
937
- markRead(conversationId: ConversationId, at?: string): Promise<void>;
950
+ /** Persists the cutoff, then returns the server's remaining unread count. */
951
+ markRead(conversationId: ConversationId, at: string): Promise<number>;
938
952
  setConversationFlags(conversationId: ConversationId, flags: {
939
953
  isMuted?: boolean;
940
954
  isArchived?: boolean;
@@ -975,10 +989,10 @@ declare function createMessagingRepository(options: RepositoryOptions): Messagin
975
989
  * 4. **Order is `created_at` then `id`.** The unique tiebreaker is not
976
990
  * decoration: two messages in the same millisecond otherwise swap places on
977
991
  * every re-render.
978
- * 5. **The active conversation's unread count is forced to zero.** A stale
979
- * server count must never re-badge a conversation the user is reading — a
980
- * safety net the origin needed in three separate places, so it lives in one
981
- * place here.
992
+ * 5. **Navigation is not a read receipt.** Active-conversation state never
993
+ * clears unread counts. Only a confirmed participant write may do that, and
994
+ * overlapping confirmations apply monotonically so one baseline is never
995
+ * subtracted twice.
982
996
  */
983
997
 
984
998
  /**
@@ -1008,6 +1022,12 @@ interface ConversationThread {
1008
1022
  }
1009
1023
  interface MessagingSnapshot {
1010
1024
  readonly conversations: readonly ConversationSummary[];
1025
+ /**
1026
+ * Summaries known by id, including a directly opened conversation outside
1027
+ * the current filtered inbox page. Header lookup uses this map; inbox
1028
+ * rendering continues to use `conversations` exactly as returned by its page.
1029
+ */
1030
+ readonly conversationSummaries: ReadonlyMap<ConversationId, ConversationSummary>;
1011
1031
  readonly hasMoreConversations: boolean;
1012
1032
  /**
1013
1033
  * True once the inbox has actually been READ at least once.
@@ -1066,6 +1086,7 @@ interface MessagingStore {
1066
1086
  } | null): void;
1067
1087
  appendConversations(items: readonly ConversationSummary[], hasMore: boolean): void;
1068
1088
  upsertConversation(item: ConversationSummary): void;
1089
+ setConversationSummary(item: ConversationSummary): void;
1069
1090
  removeConversation(id: ConversationId): void;
1070
1091
  setActiveConversation(id: ConversationId | null): void;
1071
1092
  /** Record (or clear, with `null`) why a conversation could not be read. */
@@ -1073,12 +1094,19 @@ interface MessagingStore {
1073
1094
  setThread(id: ConversationId, messages: readonly Message[], args?: {
1074
1095
  hasMoreOlder?: boolean;
1075
1096
  }): void;
1097
+ clearThread(id: ConversationId): void;
1076
1098
  prependOlder(id: ConversationId, messages: readonly Message[], hasMoreOlder: boolean): void;
1077
1099
  /** The one door every incoming message uses. Returns what actually happened. */
1078
1100
  ingest(message: Message): "added" | "merged" | "dropped-stale" | "dropped-duplicate";
1079
1101
  ingestMany(messages: readonly Message[]): void;
1080
1102
  removeMessage(conversationId: ConversationId, messageId: MessageId): void;
1081
- markConversationRead(id: ConversationId): void;
1103
+ captureConversationRead(id: ConversationId, at: string): ConversationReadCutoff;
1104
+ /**
1105
+ * Apply a confirmed read through the exact cutoff persisted by the repository.
1106
+ * `unreadCount` is the portion visible when that write began; arrivals after
1107
+ * `at` remain unread when the confirmation comes back.
1108
+ */
1109
+ markConversationRead(id: ConversationId, cutoff: ConversationReadCutoff, canonicalUnreadCount?: number): void;
1082
1110
  /**
1083
1111
  * Move a conversation's preview + sort position to reflect a message, with NO
1084
1112
  * refetch. Sending a message must not cost a conversation-list RPC — at a
@@ -1090,6 +1118,12 @@ interface MessagingStore {
1090
1118
  }): void;
1091
1119
  setUnreadCount(id: ConversationId, count: number): void;
1092
1120
  }
1121
+ interface ConversationReadCutoff {
1122
+ readonly at: string;
1123
+ readonly unreadCount: number;
1124
+ /** Per-conversation unread-state revision observed when this read began. */
1125
+ readonly revision: number;
1126
+ }
1093
1127
  declare function createMessagingStore(): MessagingStore;
1094
1128
  /**
1095
1129
  * The optimistic row a composer shows the instant Enter is pressed. It carries
@@ -1129,6 +1163,12 @@ interface EngineDiagnostic {
1129
1163
  readonly level: "info" | "warn" | "error";
1130
1164
  readonly message: string;
1131
1165
  readonly remedy?: string | undefined;
1166
+ /** Stable machine-readable metadata for host diagnostic adapters. */
1167
+ readonly code?: MessagingErrorCode | undefined;
1168
+ readonly operation?: string | undefined;
1169
+ readonly isRetryable?: boolean | undefined;
1170
+ readonly error?: MessagingError | undefined;
1171
+ readonly conversationId?: ConversationId | undefined;
1132
1172
  }
1133
1173
  interface MessagingEngineOptions {
1134
1174
  repository: MessagingRepository;
@@ -1877,4 +1917,4 @@ declare function summarizeText(content: string, maxLength?: number): string;
1877
1917
  /** Serialize picked references into a fence the platform's other readers accept. */
1878
1918
  declare function composeFence(references: readonly MatrxReference[]): string;
1879
1919
 
1880
- export { type ActionChoice, type ActionContext, type ActionHandler, type ActionOutcome, type ActionReceipt, type ActionRegistry, type ActorPresentation, AgentTag, type AiCallArgs, type AiCapability, type AiResult, AlertIcon, type Attachment, Avatar, BotIcon, CheckIcon, ChevronLeftIcon, type ClientMessageId, ClockIcon, CloseIcon, Composer, type ComposerAttachment, type ComposerInputRenderProps, type Conversation, type ConversationAiContext, type ConversationCursor, type ConversationId, ConversationList, type ConversationListProps, type ConversationReadFailure, type ConversationRowWrapperProps, ConversationSkeleton, type ConversationSummary, type ConversationThread, type ConversationType, ConversationView, type ConversationViewProps, DEFAULT_MESSAGING_ARCHIVE_FILTER, type DeliveryState, DeliveryTick, DoubleCheckIcon, type DraftMessage, EmptyState, type EngineDiagnostic, type IncomingMessageContext, type JsonObject, type JsonValue, LinkIcon, MESSAGING_EVENTS, MESSAGING_RPC_SCHEMA, MESSAGING_SCHEMA, type MatrxReference, type Message, type MessageAction, MessageActionChips, type MessageActionRenderProps, type MessageActionRenderer, MessageBubble, type MessageCursor, type MessageGroup, type MessageId, type MessageKind, type MessageWrapperProps, type MessagingAgentIdentity, type MessagingAgents, type MessagingAi, type MessagingAiOptions, type MessagingArchiveFilter, type MessagingEngine, type MessagingEngineOptions, MessagingError, type MessagingErrorCode, type MessagingHost, type MessagingIdentity, MessagingInbox, type MessagingInboxProps, MessagingProvider, type MessagingProviderProps, type MessagingRepository, type MessagingSnapshot, type MessagingStore, type MessagingSupabaseClient, type MessagingSupabaseInternal, type OrganizationId, type Outbox, type OutboxEntry, type OutboxOptions, type OutboxStorage, type Page, PaperclipIcon, type Participant, type ParticipantRole, PlusIcon, type PostgrestFilterLike, type PostgrestLikeResponse, type PostgrestTableLike, RPCS, type ReadCache, type ReadCacheOptions, ReferenceCard, type ReferenceRenderer, ReplyIcon, type RepositoryOptions, SERVER_SESSION_IDENTITY, type SchemaLike, SearchIcon, SendIcon, type SessionIdentity, type SessionIdentitySnapshot, type SessionResolver, SparklesIcon, type SupabaseLike, TABLES, type TextSegment, TypingDots, type UseComposerResult, type UseConversationResult, type UseConversationsResult, type UseMessageActionResult, type UseMessagingAiResult, type UseTypistsResult, type UserId, type UserSummary, UsersIcon, asClientMessageId, asConversationId, asMessageId, asOrganizationId, asUserId, composeFence, conversationContextVariables, conversationTopic, createActionRegistry, createMemoryOutboxStorage, createMessagingAi, createMessagingEngine, createMessagingRepository, createMessagingStore, createOutbox, createReadCache, createSessionIdentity, 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, useComposer, useConversation, useConversations, useMessageAction, useMessagingAi, useMessagingHost, useMessagingSnapshot, useOnlineUserIds, useRequiredMessagingHost, useTypists };
1920
+ export { type ActionChoice, type ActionContext, type ActionHandler, type ActionOutcome, type ActionReceipt, type ActionRegistry, type ActorPresentation, AgentTag, type AiCallArgs, type AiCapability, type AiResult, AlertIcon, type Attachment, Avatar, BotIcon, CheckIcon, ChevronLeftIcon, type ClientMessageId, ClockIcon, CloseIcon, Composer, type ComposerAttachment, type ComposerInputRenderProps, type Conversation, type ConversationAiContext, type ConversationCursor, type ConversationId, ConversationList, type ConversationListProps, type ConversationReadCutoff, type ConversationReadFailure, type ConversationRowWrapperProps, ConversationSkeleton, type ConversationSummary, type ConversationThread, type ConversationType, ConversationView, type ConversationViewProps, DEFAULT_MESSAGING_ARCHIVE_FILTER, type DeliveryState, DeliveryTick, DoubleCheckIcon, type DraftMessage, EmptyState, type EngineDiagnostic, type IncomingMessageContext, type JsonObject, type JsonValue, LinkIcon, MESSAGING_EVENTS, MESSAGING_RPC_SCHEMA, MESSAGING_SCHEMA, type MatrxReference, type Message, type MessageAction, MessageActionChips, type MessageActionRenderProps, type MessageActionRenderer, MessageBubble, type MessageCursor, type MessageGroup, type MessageId, type MessageKind, type MessageWrapperProps, type MessagingAgentIdentity, type MessagingAgents, type MessagingAi, type MessagingAiOptions, type MessagingArchiveFilter, type MessagingEngine, type MessagingEngineOptions, MessagingError, type MessagingErrorCode, type MessagingHost, type MessagingIdentity, MessagingInbox, type MessagingInboxProps, MessagingProvider, type MessagingProviderProps, type MessagingRepository, type MessagingSnapshot, type MessagingStore, type MessagingSupabaseClient, type MessagingSupabaseInternal, type OrganizationId, type Outbox, type OutboxEntry, type OutboxOptions, type OutboxStorage, type Page, PaperclipIcon, type Participant, type ParticipantRole, PlusIcon, type PostgrestFilterLike, type PostgrestLikeResponse, type PostgrestTableLike, RPCS, type ReadCache, type ReadCacheOptions, ReferenceCard, type ReferenceRenderer, ReplyIcon, type RepositoryOptions, SERVER_SESSION_IDENTITY, type SchemaLike, SearchIcon, SendIcon, type SessionIdentity, type SessionIdentitySnapshot, type SessionResolver, SparklesIcon, type SupabaseLike, TABLES, type TextSegment, TypingDots, type UseComposerResult, type UseConversationResult, type UseConversationsResult, type UseMessageActionResult, type UseMessagingAiResult, type UseTypistsResult, type UserId, type UserSummary, UsersIcon, asClientMessageId, asConversationId, asMessageId, asOrganizationId, asUserId, composeFence, conversationContextVariables, conversationTopic, createActionRegistry, createMemoryOutboxStorage, createMessagingAi, createMessagingEngine, createMessagingRepository, createMessagingStore, createOutbox, createReadCache, createSessionIdentity, 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, useComposer, useConversation, useConversations, useMessageAction, useMessagingAi, useMessagingHost, useMessagingSnapshot, useOnlineUserIds, useRequiredMessagingHost, useTypists, writeNotApplied };