@ai-matrx/messaging 0.3.0 → 0.4.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/core/actions.ts","../src/core/actor.ts","../src/core/ai.ts","../src/core/errors.ts","../src/core/references.ts","../src/core/cache.ts","../src/core/channels.ts","../src/core/slot.ts","../src/core/engine.ts","../src/core/outbox.ts","../src/core/projection.ts","../src/core/store.ts","../src/core/format.ts","../src/core/repository.ts","../src/core/types.ts"],"sourcesContent":["/**\n * `@ai-matrx/messaging` — enterprise, AI-native in-app messaging for Matrx.\n *\n * This entry is FRAMEWORK-FREE: safe to import from Redux middleware, a plain\n * service module, a worker, or a node test. React lives in\n * `@ai-matrx/messaging/react`.\n *\n * The doctrine — how a message can arrive four ways and still render once, why\n * reconnecting means re-reading, and what the host is and is not allowed to\n * wire — is in the README. Read it before changing anything here.\n */\n\nexport { createActionRegistry } from \"./core/actions\";\nexport type {\n ActionChoice,\n ActionContext,\n ActionHandler,\n ActionOutcome,\n ActionReceipt,\n ActionRegistry,\n} from \"./core/actions\";\n\nexport { resolveActor } from \"./core/actor\";\nexport type { ActorPresentation } from \"./core/actor\";\n\nexport { createMessagingAi } from \"./core/ai\";\nexport type {\n AiCapability,\n AiResult,\n MessagingAgents,\n MessagingAi,\n MessagingAiOptions,\n} from \"./core/ai\";\n\nexport { createReadCache } from \"./core/cache\";\nexport type { ReadCache, ReadCacheOptions } from \"./core/cache\";\n\nexport { conversationTopic, inboxTopic, MESSAGING_EVENTS } from \"./core/channels\";\n\nexport { createMessagingEngine, messagingClientId } from \"./core/engine\";\nexport type {\n EngineDiagnostic,\n MessagingEngine,\n MessagingEngineOptions,\n} from \"./core/engine\";\n\nexport { invalidResponse, MessagingError, normalizeMessagingError } from \"./core/errors\";\nexport type { MessagingErrorCode } from \"./core/errors\";\n\nexport {\n avatarPaletteIndex,\n formatConversationTime,\n formatDateSeparator,\n formatLastSeen,\n formatMessageTime,\n formatTypists,\n getInitials,\n groupMessages,\n isSameDay,\n participantNames,\n} from \"./core/format\";\nexport type { MessageGroup } from \"./core/format\";\n\nexport {\n createMemoryOutboxStorage,\n createOutbox,\n createWebOutboxStorage,\n} from \"./core/outbox\";\nexport type { Outbox, OutboxEntry, OutboxOptions, OutboxStorage } from \"./core/outbox\";\n\nexport {\n projectConversationSummary,\n projectMessage,\n projectMessageAction,\n projectParticipantRole,\n projectUserSummary,\n} from \"./core/projection\";\n\nexport {\n composeFence,\n extractReferences,\n splitText,\n summarizeText,\n} from \"./core/references\";\nexport type { TextSegment } from \"./core/references\";\n\nexport {\n createMessagingRepository,\n MESSAGING_SCHEMA,\n RPCS,\n TABLES,\n} from \"./core/repository\";\nexport type {\n MessagingRepository,\n RepositoryOptions,\n SessionResolver,\n} from \"./core/repository\";\n\nexport { createMessagingStore, optimisticMessage } from \"./core/store\";\nexport type {\n ConversationThread,\n MessagingSnapshot,\n MessagingStore,\n} from \"./core/store\";\n\nexport type {\n PostgrestFilterLike,\n PostgrestLikeResponse,\n PostgrestTableLike,\n SchemaLike,\n SupabaseLike,\n} from \"./core/supabase-shape\";\n\nexport {\n asClientMessageId,\n asConversationId,\n asMessageId,\n asOrganizationId,\n asUserId,\n} from \"./core/types\";\nexport type {\n Attachment,\n ClientMessageId,\n Conversation,\n ConversationCursor,\n ConversationId,\n ConversationSummary,\n ConversationType,\n DeliveryState,\n DraftMessage,\n JsonObject,\n JsonValue,\n MatrxReference,\n Message,\n MessageAction,\n MessageCursor,\n MessageId,\n MessageKind,\n MessagingIdentity,\n OrganizationId,\n Page,\n Participant,\n ParticipantRole,\n UserId,\n UserSummary,\n} from \"./core/types\";\n","/**\n * ACTIONABLE MESSAGES — a message that carries something you can DO.\n *\n * A registry, never a `switch`. Two properties come from that choice, and both\n * are load-bearing:\n *\n * - **Forward compatibility.** An unknown `kind` — or a known kind at a version\n * this build does not understand — renders NOTHING and executes nothing. A\n * `switch` with a `default: throw` means the day a new sender ships, every\n * older reader's thread breaks. Silently rendering an unknown payload with a\n * generic button is worse: it offers an action nobody can honor.\n * - **Extensibility across packages.** `@ai-matrx/meet` registers its call\n * invitation here rather than this package learning about meetings (D1).\n *\n * 🚨 **IDEMPOTENCE IS ENFORCED HERE, NOT PROMISED BY HANDLERS.** Two tabs, a\n * double-click, and a retried tap must apply an action ONCE. The registry\n * de-duplicates by `(kind, messageId, actorId)`: a second execution while the\n * first is in flight AWAITS it and returns the same receipt, and a settled\n * action returns its stored receipt without touching the server.\n *\n * 🚨 **THE PAYLOAD IS NOT AUTHORIZATION.** A handler must re-resolve the\n * durable, caller-authorized request row server-side before it writes. The\n * message's `action_data` says what was ASKED, never what is ALLOWED — trusting\n * it turns any message into a privilege grant. Handlers declare this by\n * construction: they receive the payload and the actor, and must go to the\n * database themselves.\n */\n\nimport type { JsonObject, MessageAction, MessageId, UserId } from \"./types\";\n\nexport type ActionOutcome = \"applied\" | \"already\" | \"declined\" | \"unavailable\";\n\nexport interface ActionReceipt {\n readonly kind: string;\n readonly messageId: MessageId;\n readonly actorId: UserId;\n readonly outcome: ActionOutcome;\n readonly label: string;\n readonly settledAt: string;\n readonly detail?: string | undefined;\n}\n\nexport interface ActionContext {\n readonly messageId: MessageId;\n readonly actorId: UserId;\n readonly organizationId: string;\n /** Which of the choices the user picked, e.g. `approve` / `decline`. */\n readonly choice: string;\n}\n\nexport interface ActionChoice {\n readonly id: string;\n readonly label: string;\n readonly tone: \"primary\" | \"neutral\" | \"danger\";\n}\n\nexport interface ActionHandler<TPayload = JsonObject> {\n readonly kind: string;\n /** Versions this build understands. An unlisted version renders nothing. */\n readonly versions: readonly number[];\n /** What the chips say. Return `[]` to render the action as read-only. */\n choices: (payload: TPayload) => readonly ActionChoice[];\n /** One-line summary for the inbox preview and notifications. */\n summarize?: (payload: TPayload) => string;\n /**\n * Do the thing. MUST re-resolve the authorizing row server-side; the payload\n * is the request, not the permission. Returning `already` is how a handler\n * reports that the server had settled it — the registry treats it as success.\n */\n execute: (payload: TPayload, context: ActionContext) => Promise<ActionReceipt>;\n}\n\nexport interface ActionRegistry {\n register<TPayload = JsonObject>(handler: ActionHandler<TPayload>): void;\n /** The handler for an action, or null when this build cannot honor it. */\n resolve(action: MessageAction): ActionHandler | null;\n choicesFor(action: MessageAction): readonly ActionChoice[];\n summarize(action: MessageAction): string | null;\n /** Idempotent execution. Concurrent callers share one in-flight result. */\n execute(action: MessageAction, context: ActionContext): Promise<ActionReceipt>;\n /** The stored receipt for a settled action, if there is one. */\n receiptFor(kind: string, messageId: MessageId, actorId: UserId): ActionReceipt | null;\n /** Record a receipt observed from another client's broadcast. */\n observeReceipt(receipt: ActionReceipt): void;\n known(): readonly string[];\n}\n\nfunction receiptKey(kind: string, messageId: MessageId, actorId: UserId): string {\n return `${kind}::${messageId}::${actorId}`;\n}\n\nexport function createActionRegistry(): ActionRegistry {\n const handlers = new Map<string, ActionHandler>();\n const receipts = new Map<string, ActionReceipt>();\n const inFlight = new Map<string, Promise<ActionReceipt>>();\n\n const registry: ActionRegistry = {\n register(handler) {\n handlers.set(handler.kind, handler as unknown as ActionHandler);\n },\n resolve(action) {\n const handler = handlers.get(action.kind);\n if (handler === undefined) return null;\n // A version this build does not list is as unknown as an unknown kind.\n return handler.versions.includes(action.version) ? handler : null;\n },\n choicesFor(action) {\n const handler = registry.resolve(action);\n return handler === null ? [] : handler.choices(action.payload as JsonObject);\n },\n summarize(action) {\n const handler = registry.resolve(action);\n if (handler?.summarize === undefined) return null;\n return handler.summarize(action.payload as JsonObject);\n },\n execute(action, context) {\n const key = receiptKey(action.kind, context.messageId, context.actorId);\n\n const settled = receipts.get(key);\n if (settled !== undefined) return Promise.resolve(settled);\n\n const running = inFlight.get(key);\n // THE DOUBLE-CLICK GUARD. The second caller awaits the first rather than\n // starting a second write.\n if (running !== undefined) return running;\n\n const handler = registry.resolve(action);\n if (handler === null) {\n const receipt: ActionReceipt = {\n kind: action.kind,\n messageId: context.messageId,\n actorId: context.actorId,\n outcome: \"unavailable\",\n label: \"Not available in this app version\",\n settledAt: new Date().toISOString(),\n detail:\n `No handler registered for \"${action.kind}\" v${action.version}. Update the app, ` +\n `or register a handler on <MessagingProvider actions={...}>.`,\n };\n return Promise.resolve(receipt);\n }\n\n const started = handler\n .execute(action.payload as JsonObject, context)\n .then((receipt) => {\n // `applied` and `already` are both terminal: the server has the\n // effect either way, so a later click must not re-attempt.\n if (receipt.outcome === \"applied\" || receipt.outcome === \"already\") {\n receipts.set(key, receipt);\n }\n return receipt;\n })\n .finally(() => {\n inFlight.delete(key);\n });\n\n inFlight.set(key, started);\n return started;\n },\n receiptFor(kind, messageId, actorId) {\n return receipts.get(receiptKey(kind, messageId, actorId)) ?? null;\n },\n observeReceipt(receipt) {\n if (receipt.outcome === \"applied\" || receipt.outcome === \"already\") {\n receipts.set(receiptKey(receipt.kind, receipt.messageId, receipt.actorId), receipt);\n }\n },\n known() {\n return [...handlers.keys()];\n },\n };\n\n return registry;\n}\n","/**\n * EFFECTIVE-ACTOR RESOLUTION — who a message is FROM, versus who wrote the row.\n *\n * `sender_id` is the AUDIT PRINCIPAL: the session the write happened under. It\n * must never change, and it must never be what the UI renders when an agent\n * acted through a human's session. Otherwise an automated message wears a\n * colleague's face and name — the failure this module exists to prevent (R4\n * puts agents in conversations, which makes it the normal case, not an edge).\n *\n * The effective actor is declared in the message's own metadata by whoever sent\n * it. Nothing here infers an agent from heuristics on the content.\n */\n\nimport type { JsonObject, Message, UserSummary } from \"./types\";\n\nexport interface ActorPresentation {\n readonly displayName: string;\n readonly avatarUrl: string | null;\n /** True when the message was authored by an agent, not the human principal. */\n readonly isAgent: boolean;\n /** Set only for an agent: the human whose session it acted under. */\n readonly onBehalfOfName: string | null;\n /** The audit principal. Always the row's `sender_id`. Never rewritten. */\n readonly principalUserId: Message[\"senderId\"];\n}\n\ninterface ActorHint {\n readonly agentId?: string;\n readonly agentName?: string;\n readonly agentAvatarUrl?: string;\n}\n\nfunction readActorHint(metadata: JsonObject): ActorHint | null {\n const raw = (metadata as Record<string, unknown>)[\"actor\"];\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) return null;\n const record = raw as Record<string, unknown>;\n const agentId = record[\"agentId\"] ?? record[\"agent_id\"];\n if (typeof agentId !== \"string\" || agentId.length === 0) return null;\n const name = record[\"agentName\"] ?? record[\"agent_name\"];\n const avatar = record[\"agentAvatarUrl\"] ?? record[\"agent_avatar_url\"];\n return {\n agentId,\n ...(typeof name === \"string\" && name.length > 0 ? { agentName: name } : {}),\n ...(typeof avatar === \"string\" && avatar.length > 0 ? { agentAvatarUrl: avatar } : {}),\n };\n}\n\nexport function resolveActor(\n message: Message,\n sender: UserSummary | null,\n): ActorPresentation {\n const hint = readActorHint(message.metadata);\n const humanName = sender?.displayName ?? message.senderId;\n\n if (hint !== null) {\n return {\n // An agent NEVER inherits the human's name or avatar. When the agent did\n // not name itself, it is labeled generically — an honest \"Agent\" beats a\n // colleague's face on a message they did not write.\n displayName: hint.agentName ?? \"Agent\",\n avatarUrl: hint.agentAvatarUrl ?? null,\n isAgent: true,\n onBehalfOfName: humanName,\n principalUserId: message.senderId,\n };\n }\n\n if (sender?.isAgent === true) {\n return {\n displayName: sender.displayName,\n avatarUrl: sender.avatarUrl,\n isAgent: true,\n onBehalfOfName: null,\n principalUserId: message.senderId,\n };\n }\n\n return {\n displayName: humanName,\n avatarUrl: sender?.avatarUrl ?? null,\n isAgent: false,\n onBehalfOfName: null,\n principalUserId: message.senderId,\n };\n}\n","/**\n * AI-NATIVE, OUT OF THE BOX (R4).\n *\n * Four conversation intelligences — catch me up, summarize, extract action\n * items, draft a reply — plus agents as participants. All of it executes on the\n * platform's agent system through `@ai-matrx/agents`; this package never talks\n * to a model, never holds a prompt, and never carries an agent definition.\n *\n * 🚨 **THE USER-INPUT LAW.** `user_input` is what a HUMAN typed. Machine\n * content — a transcript, a participant roster, a cutoff timestamp — travels as\n * NAMED VARIABLES. This is not style: the server treats `user_input` as the\n * turn's human utterance (it is stored as such, shown as such, and shapes the\n * agent's framing), so smuggling a transcript through it silently corrupts\n * every conversation it touches. Every call below sends `variables` and either\n * a short human-shaped `user_input` or none.\n *\n * 🚨 **AGENT DEFINITIONS LIVE IN THE DATABASE.** The package takes agent IDs as\n * injected identity. When an id is not configured the capability reports\n * `unavailable` WITH the remedy — it never silently no-ops, and the UI hides\n * the action rather than offering a dead button (no dead ends).\n */\n\nimport {\n newEphemeralConversationStart,\n runAgentToCompletion,\n type MatrxJsonObject,\n type MatrxTransport,\n} from \"@ai-matrx/agents/matrx\";\nimport { MessagingError } from \"./errors\";\nimport { summarizeText } from \"./references\";\nimport type { ConversationId, Message, UserSummary } from \"./types\";\n\n/**\n * Which platform agent backs each capability. Every field is a DATABASE row id\n * supplied by the host; an omitted one disables exactly that capability.\n */\nexport interface MessagingAgents {\n readonly catchUp?: string | undefined;\n readonly summarize?: string | undefined;\n readonly actionItems?: string | undefined;\n readonly draftReply?: string | undefined;\n}\n\nexport interface MessagingAiOptions {\n transport: MatrxTransport;\n organizationId: string;\n agents: MessagingAgents;\n /** Stable source slugs so server-side analytics can see where a run came from. */\n sourceApp?: string | undefined;\n sourceFeature?: string | undefined;\n /** Cap on how much transcript is sent. Default 200 messages. */\n maxTranscriptMessages?: number | undefined;\n}\n\nexport type AiCapability = keyof MessagingAgents;\n\nexport interface AiResult {\n readonly capability: AiCapability;\n readonly text: string;\n readonly conversationId: string | null;\n}\n\n/** A message reduced to what an agent needs. Never the raw DB row. */\ninterface TranscriptEntry {\n readonly at: string;\n readonly author: string;\n readonly text: string;\n}\n\nexport interface MessagingAi {\n /** Which capabilities this host actually configured. Drives what the UI offers. */\n available(): readonly AiCapability[];\n isAvailable(capability: AiCapability): boolean;\n catchMeUp(args: {\n conversationId: ConversationId;\n messages: readonly Message[];\n participants: readonly UserSummary[];\n since: string | null;\n signal?: AbortSignal;\n }): Promise<AiResult>;\n summarize(args: {\n conversationId: ConversationId;\n messages: readonly Message[];\n participants: readonly UserSummary[];\n signal?: AbortSignal;\n }): Promise<AiResult>;\n extractActionItems(args: {\n conversationId: ConversationId;\n messages: readonly Message[];\n participants: readonly UserSummary[];\n signal?: AbortSignal;\n }): Promise<AiResult>;\n draftReply(args: {\n conversationId: ConversationId;\n messages: readonly Message[];\n participants: readonly UserSummary[];\n /** What the human asked for, if anything — this IS a human utterance. */\n instruction?: string | undefined;\n signal?: AbortSignal;\n }): Promise<AiResult>;\n}\n\nfunction nameOf(\n participants: readonly UserSummary[],\n senderId: Message[\"senderId\"],\n): string {\n return participants.find((p) => p.userId === senderId)?.displayName ?? senderId;\n}\n\nfunction buildTranscript(\n messages: readonly Message[],\n participants: readonly UserSummary[],\n limit: number,\n since: string | null,\n): readonly TranscriptEntry[] {\n const relevant = messages.filter((message) => {\n if (message.deletedAt !== null) return false;\n if (since === null) return true;\n return message.createdAt > since;\n });\n // Keep the MOST RECENT window when the conversation is long: the tail is what\n // \"what did I miss\" is about, and silently truncating the head is the honest\n // cut. The cap itself is reported to the agent so it can say so.\n const windowed = relevant.slice(-limit);\n return windowed.map((message) => ({\n at: message.createdAt,\n author: nameOf(participants, message.senderId),\n // The transcript is TEXT. A reference fence becomes its label, never JSON —\n // the same collapse the inbox preview uses.\n text: summarizeText(message.content, 2_000),\n }));\n}\n\nexport function createMessagingAi(options: MessagingAiOptions): MessagingAi {\n const limit = options.maxTranscriptMessages ?? 200;\n\n function agentFor(capability: AiCapability): string {\n const agentId = options.agents[capability];\n if (typeof agentId !== \"string\" || agentId.length === 0) {\n throw new MessagingError(\n \"misconfigured\",\n `Messaging AI capability \"${capability}\" has no agent configured`,\n `Pass agents={{ ${capability}: \"<agent-id>\" }} to <MessagingProvider>. Agent ` +\n `definitions live in the database, never in this package — the id is the only ` +\n `part a host injects. Until then the UI hides this action rather than ` +\n `offering a button that cannot work.`,\n );\n }\n return agentId;\n }\n\n async function run(\n capability: AiCapability,\n variables: MatrxJsonObject,\n userInput: string | null,\n signal: AbortSignal | undefined,\n ): Promise<AiResult> {\n const agentId = agentFor(capability);\n const completed = await runAgentToCompletion(\n options.transport,\n agentId,\n {\n ...newEphemeralConversationStart(),\n organization_id: options.organizationId,\n source_app: options.sourceApp ?? \"ai-matrx\",\n source_feature: options.sourceFeature ?? `messaging.${capability}`,\n initiation: \"user\",\n // THE USER-INPUT LAW: structured content is NEVER here.\n ...(userInput !== null ? { user_input: userInput } : {}),\n variables,\n },\n signal !== undefined ? { signal } : {},\n );\n return {\n capability,\n text: completed.text.trim(),\n conversationId: completed.conversationId,\n };\n }\n\n function variablesFor(args: {\n conversationId: ConversationId;\n messages: readonly Message[];\n participants: readonly UserSummary[];\n since?: string | null;\n }): MatrxJsonObject {\n const transcript = buildTranscript(\n args.messages,\n args.participants,\n limit,\n args.since ?? null,\n );\n return {\n conversation_id: args.conversationId,\n participants: args.participants.map((participant) => ({\n user_id: participant.userId,\n display_name: participant.displayName,\n is_agent: participant.isAgent,\n })),\n transcript: transcript.map((entry) => ({\n at: entry.at,\n author: entry.author,\n text: entry.text,\n })),\n transcript_message_count: transcript.length,\n // Honest about the cut, so an agent can say \"showing the last 200\".\n transcript_truncated: args.messages.length > transcript.length,\n ...(args.since != null ? { unread_since: args.since } : {}),\n };\n }\n\n return {\n available: () =>\n ([\"catchUp\", \"summarize\", \"actionItems\", \"draftReply\"] as const).filter(\n (capability) => {\n const id = options.agents[capability];\n return typeof id === \"string\" && id.length > 0;\n },\n ),\n isAvailable(capability) {\n const id = options.agents[capability];\n return typeof id === \"string\" && id.length > 0;\n },\n catchMeUp: (args) =>\n run(\"catchUp\", variablesFor(args), null, args.signal),\n summarize: (args) => run(\"summarize\", variablesFor(args), null, args.signal),\n extractActionItems: (args) =>\n run(\"actionItems\", variablesFor(args), null, args.signal),\n draftReply: (args) =>\n run(\n \"draftReply\",\n variablesFor(args),\n // The ONE genuine human utterance in this module: what the user asked\n // the drafter for. Everything else rode `variables`.\n args.instruction !== undefined && args.instruction.trim().length > 0\n ? args.instruction.trim()\n : null,\n args.signal,\n ),\n };\n}\n","/**\n * ONE error classification for the whole package (C22).\n *\n * The banned alternative is the host catching a PostgREST error and deciding\n * for itself whether it was an auth blip, a permission denial, or a real bug —\n * which is how the same three-branch `if` ends up copy-pasted into every\n * consumer and drifts. Everything this package throws is a `MessagingError`\n * with a stable `code`, and the two that hosts genuinely treat differently\n * (`session-unavailable`, `forbidden`) say so in the type.\n *\n * The incident behind `session-unavailable`: a DM reader mounted before the\n * access token existed, every RPC came back `42501`, and the app captured 909\n * errors in 0.6 seconds. A missing session is a NORMAL lifecycle moment — it\n * warns and retries; it is never a red error.\n */\n\nexport type MessagingErrorCode =\n | \"session-unavailable\"\n | \"forbidden\"\n | \"not-found\"\n | \"conflict\"\n | \"transport\"\n | \"invalid-response\"\n | \"misconfigured\"\n | \"unknown\";\n\nexport class MessagingError extends Error {\n readonly code: MessagingErrorCode;\n /** The remedy. Nothing fails silently, and nothing fails without saying what to do. */\n readonly remedy: string;\n override readonly cause?: unknown;\n\n constructor(\n code: MessagingErrorCode,\n message: string,\n remedy: string,\n cause?: unknown,\n ) {\n super(message);\n this.name = \"MessagingError\";\n this.code = code;\n this.remedy = remedy;\n if (cause !== undefined) this.cause = cause;\n }\n\n /** True when retrying after the host re-establishes a session is the fix. */\n get isRetryable(): boolean {\n return this.code === \"session-unavailable\" || this.code === \"transport\";\n }\n}\n\ninterface PostgrestErrorLike {\n message: string;\n code?: string | undefined;\n details?: string | null | undefined;\n}\n\n/** Postgres/PostgREST codes that mean \"you are not allowed\", not \"you are broken\". */\nconst FORBIDDEN_CODES = new Set([\"42501\", \"PGRST301\", \"PGRST302\"]);\nconst NOT_FOUND_CODES = new Set([\"PGRST116\", \"PGRST205\"]);\nconst CONFLICT_CODES = new Set([\"23505\"]);\n\nconst SESSION_MARKERS = [\n \"auth session missing\",\n \"jwt expired\",\n \"no api key found\",\n \"refresh_token_not_found\",\n \"invalid claim: missing sub claim\",\n];\n\n/**\n * THE ONE PLACE a raw PostgREST/transport error becomes a typed one. Called at\n * every boundary in `repository.ts`; nothing else in the package inspects a raw\n * error, and no consumer should ever have to.\n */\nexport function normalizeMessagingError(\n error: PostgrestErrorLike | Error | unknown,\n operation: string,\n): MessagingError {\n if (error instanceof MessagingError) return error;\n\n const raw = error as Partial<PostgrestErrorLike> & { name?: string };\n const message = typeof raw?.message === \"string\" ? raw.message : String(error);\n const code = typeof raw?.code === \"string\" ? raw.code : undefined;\n const lowered = message.toLowerCase();\n\n if (\n raw?.name === \"SessionUnavailableError\" ||\n SESSION_MARKERS.some((marker) => lowered.includes(marker))\n ) {\n return new MessagingError(\n \"session-unavailable\",\n `${operation}: no Supabase session available (${message})`,\n \"Normal during sign-in and token refresh. The package retries once after the \" +\n \"host's session source resolves; log it as a warning, never as an error.\",\n error,\n );\n }\n if (code !== undefined && FORBIDDEN_CODES.has(code)) {\n return new MessagingError(\n \"forbidden\",\n `${operation}: denied by the database (${code}: ${message})`,\n \"Authorization is RLS + auth-checked RPCs (R5). Fix the policy or the caller's \" +\n \"membership — never work around it with a service-role client in a browser.\",\n error,\n );\n }\n if (code !== undefined && NOT_FOUND_CODES.has(code)) {\n return new MessagingError(\n \"not-found\",\n `${operation}: not found (${code}: ${message})`,\n \"The row is gone, or RLS hides it from this user. Re-read the conversation list.\",\n error,\n );\n }\n if (code !== undefined && CONFLICT_CODES.has(code)) {\n return new MessagingError(\n \"conflict\",\n `${operation}: unique violation (${code}: ${message})`,\n \"A concurrent writer won. For messages this is the client_message_id idempotency \" +\n \"key doing its job — re-read the row rather than retrying the insert.\",\n error,\n );\n }\n return new MessagingError(\n \"transport\",\n `${operation}: ${message}`,\n \"Transient transport failure. The package retries reads; a write is surfaced to the \" +\n \"outbox so the typed message is never lost.\",\n error,\n );\n}\n\n/** A response whose SHAPE is wrong — the ingress boundary, not the wire. */\nexport function invalidResponse(operation: string, detail: string): MessagingError {\n return new MessagingError(\n \"invalid-response\",\n `${operation}: ${detail}`,\n \"The database returned a shape this package does not accept. Rendering it would put \" +\n \"a lie on the screen, so it is refused here. Fix the RPC's return contract.\",\n );\n}\n","/**\n * MATRX REFERENCES — a message can name a platform entity, and everything it\n * names must OPEN. No dead ends.\n *\n * Two transports, both supported because both already exist in the wild:\n *\n * 1. **Structured**, in `metadata.references` — what this package writes.\n * 2. **Fenced**, as a ```matrx block in the content — the platform's\n * reference-fence protocol, which arrives from senders outside messaging.\n *\n * Two failures this module exists to prevent:\n *\n * - **A raw fence rendered as a code block.** The reader sees JSON. `splitText`\n * is what lets the renderer draw a card instead.\n * - **A fence leaking into a preview.** The inbox row and the desktop\n * notification are plain text; pasting the JSON there is the same defect one\n * layer down. `summarizeText` is the one collapse, used by both.\n */\n\nimport type { MatrxReference } from \"./types\";\n\nconst FENCE = /```matrx\\s*\\n([\\s\\S]*?)\\n?```/g;\n\nexport type TextSegment =\n | { readonly type: \"text\"; readonly value: string }\n | { readonly type: \"reference\"; readonly reference: MatrxReference };\n\nfunction parseFenceBody(body: string): readonly MatrxReference[] {\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n return [];\n }\n const entries = Array.isArray(parsed) ? parsed : [parsed];\n const references: MatrxReference[] = [];\n for (const entry of entries) {\n if (typeof entry !== \"object\" || entry === null) continue;\n const record = entry as Record<string, unknown>;\n const entityType = record[\"entityType\"] ?? record[\"entity_type\"] ?? record[\"type\"];\n const entityId = record[\"entityId\"] ?? record[\"entity_id\"] ?? record[\"id\"];\n if (typeof entityType !== \"string\" || typeof entityId !== \"string\") continue;\n if (entityType.length === 0 || entityId.length === 0) continue;\n const label = record[\"label\"] ?? record[\"title\"] ?? record[\"name\"];\n const href = record[\"href\"] ?? record[\"url\"];\n references.push({\n entityType,\n entityId,\n label: typeof label === \"string\" && label.length > 0 ? label : entityId,\n ...(typeof href === \"string\" && href.length > 0 ? { href } : {}),\n });\n }\n return references;\n}\n\n/** Every reference a message carries, from both transports, de-duplicated. */\nexport function extractReferences(\n content: string,\n structured: readonly MatrxReference[] = [],\n): readonly MatrxReference[] {\n const seen = new Map<string, MatrxReference>();\n const add = (reference: MatrxReference): void => {\n seen.set(`${reference.entityType}:${reference.entityId}`, reference);\n };\n structured.forEach(add);\n for (const match of content.matchAll(FENCE)) {\n parseFenceBody(match[1] ?? \"\").forEach(add);\n }\n return [...seen.values()];\n}\n\n/**\n * Split content into renderable segments. The renderer draws text for `text`\n * and a live card for `reference`; a fence therefore CANNOT reach the screen as\n * a code block.\n */\nexport function splitText(content: string): readonly TextSegment[] {\n const segments: TextSegment[] = [];\n let cursor = 0;\n for (const match of content.matchAll(FENCE)) {\n const start = match.index ?? 0;\n if (start > cursor) {\n segments.push({ type: \"text\", value: content.slice(cursor, start) });\n }\n parseFenceBody(match[1] ?? \"\").forEach((reference) => {\n segments.push({ type: \"reference\", reference });\n });\n cursor = start + match[0].length;\n }\n if (cursor < content.length) {\n segments.push({ type: \"text\", value: content.slice(cursor) });\n }\n return segments.filter(\n (segment) => segment.type === \"reference\" || segment.value.trim().length > 0,\n );\n}\n\n/**\n * ONE plain-text collapse, used by the inbox preview AND the notification body.\n * A fence becomes its human label; it never reaches either as raw JSON.\n */\nexport function summarizeText(content: string, maxLength = 140): string {\n const parts = splitText(content).map((segment) =>\n segment.type === \"text\" ? segment.value : segment.reference.label,\n );\n const flattened = parts.join(\" \").replace(/\\s+/g, \" \").trim();\n if (flattened.length <= maxLength) return flattened;\n return `${flattened.slice(0, maxLength - 1).trimEnd()}…`;\n}\n\n/** Serialize picked references into a fence the platform's other readers accept. */\nexport function composeFence(references: readonly MatrxReference[]): string {\n if (references.length === 0) return \"\";\n return `\\`\\`\\`matrx\\n${JSON.stringify(references, null, 2)}\\n\\`\\`\\``;\n}\n","/**\n * IN-FLIGHT DEDUP + TTL CACHE — the cure for the request storm.\n *\n * The incident (matrx-frontend, 2026-08-21): every conversation row rendered\n * called the profile lookup for its participants, every one of those calls hit\n * the network independently, the transport wobbled, and the app captured\n * **909 errors in 0.6 seconds**. The bug was never the transport — it was that\n * N identical concurrent reads were N requests.\n *\n * Two guarantees, and both are load-bearing:\n *\n * 1. **Concurrent callers for the same key share ONE promise.** The second\n * caller does not start a second request; it awaits the first.\n * 2. **A failure does not poison the cache.** The rejected promise is evicted\n * the moment it settles, so the next caller retries instead of inheriting a\n * permanent error. (The naive version caches the rejection and the surface\n * stays broken until reload — the reason this has a named test.)\n *\n * A resolved value is held for `ttlMs` and then re-read. This is a READ cache\n * only: writes go straight through and invalidate.\n */\n\nexport interface ReadCacheOptions {\n /** How long a resolved value is served without a re-read. */\n ttlMs: number;\n /** Injected clock — the tests must not sleep. */\n now?: () => number;\n /** Bound on retained entries; the oldest is evicted first. */\n maxEntries?: number;\n}\n\ninterface Entry<T> {\n value: T;\n expiresAt: number;\n}\n\nexport interface ReadCache<T> {\n /** Read through the cache. Identical concurrent keys share one call. */\n read(key: string, load: () => Promise<T>): Promise<T>;\n /** Drop one key (after a write that changes it). */\n invalidate(key: string): void;\n /** Drop everything (sign-out, org switch). */\n clear(): void;\n /** Diagnostics: how many resolved entries are held. */\n size(): number;\n}\n\nexport function createReadCache<T>(options: ReadCacheOptions): ReadCache<T> {\n const now = options.now ?? (() => Date.now());\n const maxEntries = options.maxEntries ?? 500;\n const resolved = new Map<string, Entry<T>>();\n const inFlight = new Map<string, Promise<T>>();\n\n function evictIfNeeded(): void {\n while (resolved.size > maxEntries) {\n const oldest = resolved.keys().next();\n if (oldest.done === true) return;\n resolved.delete(oldest.value);\n }\n }\n\n return {\n read(key, load) {\n const hit = resolved.get(key);\n if (hit !== undefined && hit.expiresAt > now()) {\n return Promise.resolve(hit.value);\n }\n if (hit !== undefined) resolved.delete(key);\n\n const pending = inFlight.get(key);\n if (pending !== undefined) return pending;\n\n const started = load()\n .then((value) => {\n resolved.set(key, { value, expiresAt: now() + options.ttlMs });\n evictIfNeeded();\n return value;\n })\n .finally(() => {\n // ALWAYS drop the in-flight entry — on success the value now lives in\n // `resolved`, and on failure the next caller must be free to retry.\n // Caching the rejection is the \"surface stays broken until reload\" bug.\n inFlight.delete(key);\n });\n\n inFlight.set(key, started);\n return started;\n },\n invalidate(key) {\n resolved.delete(key);\n inFlight.delete(key);\n },\n clear() {\n resolved.clear();\n inFlight.clear();\n },\n size() {\n return resolved.size;\n },\n };\n}\n","/**\n * THE MESSAGING CHANNEL NAMESPACES.\n *\n * D5: zero hand-rolled `.channel(` in this package. Every topic is declared\n * through `@ai-matrx/realtime`'s registry, which refuses a colliding\n * re-declaration and hands every connection attempt a unique instance topic.\n *\n * TWO channels, deliberately — not four. The origin ran `messages:`, `typing:`,\n * `presence:` and a global list channel as four independent subscriptions, and\n * three of the four hardening items were about them fighting each other. Here:\n *\n * - `messaging-inbox` (per user): the conversation list's own feed.\n * - `messaging-conversation` (per conversation): messages, presence, AND\n * typing on ONE channel, because they are one room. Sharing the channel is\n * what makes \"the person typing is also present\" true by construction\n * instead of by two subscriptions agreeing.\n *\n * `defineChannelNamespace` throws on a conflicting re-declaration, so these are\n * created lazily through a `globalThis` slot: with dual ESM/CJS output the\n * module can be evaluated twice in one process, and a second identical\n * declaration must be a no-op rather than a crash.\n */\n\nimport { defineChannelNamespace, type ChannelNamespace } from \"@ai-matrx/realtime\";\nimport { globalSlot } from \"./slot\";\nimport type { ConversationId, UserId } from \"./types\";\n\ninterface Namespaces {\n inbox: ChannelNamespace;\n conversation: ChannelNamespace;\n}\n\nfunction namespaces(): Namespaces {\n return globalSlot<Namespaces>(\"channel-namespaces\", () => ({\n inbox: defineChannelNamespace({\n namespace: \"messaging-inbox\",\n parts: [\"userId\"],\n description:\n \"One user's conversation list: new messages anywhere they participate, membership \" +\n \"changes, and read-state updates.\",\n }),\n conversation: defineChannelNamespace({\n namespace: \"messaging-conversation\",\n parts: [\"conversationId\"],\n description:\n \"One conversation: message inserts/updates, presence, and typing — deliberately one \" +\n \"channel, because they are one room.\",\n }),\n }));\n}\n\nexport function inboxTopic(userId: UserId): string {\n return namespaces().inbox.topic({ userId });\n}\n\nexport function conversationTopic(conversationId: ConversationId): string {\n return namespaces().conversation.topic({ conversationId });\n}\n\n/** Broadcast event names. One place, so a sender and a receiver cannot drift. */\nexport const MESSAGING_EVENTS = {\n /** A freshly sent message, broadcast beside the Postgres Changes row so a\n * receiver gets it on whichever path arrives first (both are deduped). */\n message: \"mx.message\",\n /** A message edited or soft-deleted. */\n messageUpdated: \"mx.message.updated\",\n /** An action receipt, so every viewer's chip settles at once. */\n actionSettled: \"mx.action.settled\",\n} as const;\n","/**\n * PROCESS-GLOBAL STATE LIVES HERE, NEVER IN A MODULE `let`.\n *\n * With `splitting: false` dual ESM/CJS output, one module can be instantiated\n * TWICE in a single process (a Next.js app that imports ESM while its Jest\n * suite requires CJS is the ordinary case). A module-level `let` therefore\n * silently splits into two independent values — which is how a read cache\n * \"randomly\" stops deduping and a registry \"randomly\" forgets a kind.\n *\n * Everything process-global goes through `globalThis` + `Symbol.for`, and the\n * packed-tarball canary proves the slots are shared across module graphs.\n */\nconst NAMESPACE = \"ai-matrx.messaging\";\n\nexport function globalSlot<T>(name: string, create: () => T): T {\n const key = Symbol.for(`${NAMESPACE}.${name}`);\n const host = globalThis as Record<symbol, unknown>;\n const existing = host[key];\n if (existing !== undefined) return existing as T;\n const created = create();\n host[key] = created;\n return created;\n}\n\n/** Test-only: drop a slot so a suite can start from a clean registry. */\nexport function resetGlobalSlotForTests(name: string): void {\n const key = Symbol.for(`${NAMESPACE}.${name}`);\n delete (globalThis as Record<symbol, unknown>)[key];\n}\n","/**\n * THE MESSAGING ENGINE — the object a host mounts once and never reasons about.\n *\n * It owns the wiring that every chat implementation gets wrong: which channel\n * carries what, what happens on reconnect, which of four arrival paths wins,\n * when a read receipt is written, and what a failed send does. All of it is\n * here so a consumer's job is `engine.send(draft)` and rendering a snapshot.\n *\n * THE RECONNECT RULE, inherited from `@ai-matrx/realtime` and honored here:\n * realtime has NO REPLAY. Every channel this engine opens declares an\n * `onBackfill` door, and those doors re-read from the database. A reconnect\n * without a re-read leaves a permanently wrong thread that looks perfectly\n * healthy — the single most expensive bug class in messaging.\n */\n\nimport {\n clientSessionId,\n type ChannelHandle,\n type RealtimeManager,\n} from \"@ai-matrx/realtime\";\nimport { conversationTopic, inboxTopic, MESSAGING_EVENTS } from \"./channels\";\nimport { MessagingError, normalizeMessagingError } from \"./errors\";\nimport { createOutbox, type Outbox, type OutboxEntry, type OutboxStorage } from \"./outbox\";\nimport { projectMessage } from \"./projection\";\nimport type { MessagingRepository } from \"./repository\";\nimport { createMessagingStore, optimisticMessage, type MessagingStore } from \"./store\";\nimport type {\n ClientMessageId,\n ConversationCursor,\n ConversationId,\n DraftMessage,\n Message,\n MessageId,\n MessagingIdentity,\n UserId,\n} from \"./types\";\n\nexport interface EngineDiagnostic {\n readonly level: \"info\" | \"warn\" | \"error\";\n readonly message: string;\n readonly remedy?: string | undefined;\n}\n\nexport interface MessagingEngineOptions {\n repository: MessagingRepository;\n manager: RealtimeManager;\n identity: MessagingIdentity;\n outboxStorage?: OutboxStorage | undefined;\n onDiagnostic?: ((event: EngineDiagnostic) => void) | undefined;\n /** Fired for a message from someone else, for notification sinks. */\n onIncoming?: ((message: Message) => void) | undefined;\n conversationPageSize?: number | undefined;\n messagePageSize?: number | undefined;\n}\n\nexport interface MessagingEngine {\n readonly store: MessagingStore;\n readonly outbox: Outbox;\n readonly identity: MessagingIdentity;\n /** Load page one of the inbox and start the inbox channel. */\n start(): Promise<void>;\n loadMoreConversations(): Promise<void>;\n /** Open a conversation: load its thread and subscribe to its channel. */\n openConversation(id: ConversationId): Promise<void>;\n closeConversation(id: ConversationId): void;\n loadOlderMessages(id: ConversationId): Promise<void>;\n /** Queue a message. Returns immediately with the optimistic row on screen. */\n send(draft: DraftMessage): ClientMessageId;\n retry(entryId: string): void;\n discard(entryId: string): void;\n editMessage(id: MessageId, conversationId: ConversationId, content: string): Promise<void>;\n deleteMessage(\n id: MessageId,\n conversationId: ConversationId,\n forEveryone: boolean,\n ): Promise<void>;\n markRead(id: ConversationId): Promise<void>;\n startDirectConversation(otherUserId: UserId): Promise<ConversationId>;\n dispose(): void;\n}\n\nexport function createMessagingEngine(\n options: MessagingEngineOptions,\n): MessagingEngine {\n const { repository, manager, identity } = options;\n const store = createMessagingStore();\n const conversationPageSize = options.conversationPageSize ?? 30;\n const messagePageSize = options.messagePageSize ?? 50;\n\n const openChannels = new Map<ConversationId, ChannelHandle>();\n let inboxChannel: ChannelHandle | null = null;\n let conversationCursor: ConversationCursor | null = null;\n let disposed = false;\n\n function report(event: EngineDiagnostic): void {\n options.onDiagnostic?.(event);\n }\n\n function reportError(error: unknown, operation: string): void {\n const normalized = normalizeMessagingError(error, operation);\n report({\n // A missing session is a NORMAL lifecycle moment, not a red error. This\n // one line is the cure for \"909 captured errors in 0.6s\".\n level: normalized.code === \"session-unavailable\" ? \"warn\" : \"error\",\n message: normalized.message,\n remedy: normalized.remedy,\n });\n }\n\n const outbox = createOutbox({\n ...(options.outboxStorage !== undefined ? { storage: options.outboxStorage } : {}),\n send: (draft, clientMessageId) => repository.insertMessage(draft, clientMessageId),\n onSent: (entry: OutboxEntry, message) => {\n // The confirmed row carries the same client key as the optimistic bubble,\n // so this MERGES rather than appending a second copy.\n store.ingest(message);\n broadcastMessage(message);\n // NO REFETCH. The row's new preview and sort position are derivable from\n // the message we already hold; a conversation-list RPC per sent message\n // is a full page read per burst at a busy org.\n store.applyLastMessage(message);\n void entry;\n },\n onChange: (entries) => {\n // Reflect every queued/failed entry as an optimistic row so a message the\n // user typed is VISIBLE while it waits — an invisible queue is the same\n // as a lost message from the user's side.\n entries.forEach((entry) => {\n store.ingest(\n optimisticMessage({\n conversationId: entry.draft.conversationId,\n senderId: identity.userId,\n organizationId: identity.organizationId,\n content: entry.draft.content,\n clientMessageId: entry.clientMessageId,\n ...(entry.draft.kind !== undefined ? { kind: entry.draft.kind } : {}),\n ...(entry.draft.replyToId !== undefined\n ? { replyToId: entry.draft.replyToId }\n : {}),\n ...(entry.draft.action !== undefined ? { action: entry.draft.action } : {}),\n ...(entry.draft.attachments !== undefined\n ? { attachments: [...entry.draft.attachments] }\n : {}),\n ...(entry.draft.references !== undefined\n ? { references: [...entry.draft.references] }\n : {}),\n }),\n );\n });\n },\n onDiagnostic: (message) => report({ level: \"warn\", message }),\n });\n\n /**\n * Broadcast beside the Postgres Changes row: whichever arrives first shows\n * the message, and the store's dedup makes the second one free. Receivers\n * suppress our own echo through the realtime envelope.\n *\n * 🚨 It sends ONLY on an ALREADY-OPEN handle. Opening a channel here to send\n * one message would open a channel per send and never close any of them —\n * the leak the realtime manager's enforced lifecycle otherwise makes\n * impossible. No open conversation channel simply means nobody is watching\n * this thread live, and Postgres Changes still carries the row.\n */\n function broadcastMessage(message: Message): void {\n openChannels.get(message.conversationId)?.send(MESSAGING_EVENTS.message, message);\n }\n\n async function reloadInbox(): Promise<void> {\n const page = await repository.listConversations({ limit: conversationPageSize });\n conversationCursor = page.nextCursor;\n store.setConversations(page.items, page.hasMore);\n }\n\n async function backfillConversation(id: ConversationId): Promise<void> {\n const thread = store.snapshot().threads.get(id);\n const since = thread?.latestAt ?? null;\n try {\n if (since === null) {\n const page = await repository.listMessages(id, { limit: messagePageSize });\n store.setThread(id, page.items, { hasMoreOlder: page.hasMore });\n return;\n }\n const missed = await repository.messagesSince(id, since);\n store.ingestMany(missed);\n if (missed.length > 0) {\n report({\n level: \"info\",\n message: `Recovered ${missed.length} message(s) missed while disconnected.`,\n });\n }\n } catch (error) {\n reportError(error, \"backfillConversation\");\n }\n }\n\n const engine: MessagingEngine = {\n store,\n outbox,\n identity,\n\n async start() {\n await reloadInbox();\n if (disposed || inboxChannel !== null) return;\n inboxChannel = manager.open({\n topic: inboxTopic(identity.userId),\n postgresChanges: [\n {\n event: \"INSERT\",\n schema: \"communication\",\n table: \"dm_messages\",\n rowId: (row) => (typeof row[\"id\"] === \"string\" ? row[\"id\"] : undefined),\n onChange: ({ row }) => {\n if (row === null) return;\n const message = projectMessage(row, identity.organizationId);\n // Realtime has no per-user filter on this table, so rows for\n // conversations this user is not in can arrive. Dropping unknown\n // conversations here is why the inbox does not flicker with\n // strangers' traffic; the row is authorized by RLS anyway.\n const known = store\n .snapshot()\n .conversations.some((item) => item.conversation.id === message.conversationId);\n if (!known) {\n void reloadInbox();\n return;\n }\n store.ingest(message);\n const isMine = message.senderId === identity.userId;\n // Same rule as the send path: update the row in place rather\n // than re-reading the list for every message in the org.\n store.applyLastMessage(message, { incrementUnread: !isMine });\n if (!isMine) options.onIncoming?.(message);\n },\n },\n {\n event: \"*\",\n schema: \"communication\",\n table: \"dm_conversation_participants\",\n filter: `user_id=eq.${identity.userId}`,\n rowId: (row) => (typeof row[\"id\"] === \"string\" ? row[\"id\"] : undefined),\n onChange: () => {\n // Membership or read-state changed: the list's unread counts and\n // membership come from the RPC, so re-read rather than guess.\n void reloadInbox();\n },\n },\n ],\n // THE BACKFILL DOOR for the inbox.\n onBackfill: () => {\n void reloadInbox().catch((error: unknown) => reportError(error, \"inboxBackfill\"));\n outbox.flush();\n },\n });\n },\n\n async loadMoreConversations() {\n if (conversationCursor === null) return;\n try {\n const page = await repository.listConversations({\n limit: conversationPageSize,\n cursor: conversationCursor,\n });\n conversationCursor = page.nextCursor;\n store.appendConversations(page.items, page.hasMore);\n } catch (error) {\n reportError(error, \"loadMoreConversations\");\n }\n },\n\n async openConversation(id) {\n store.setActiveConversation(id);\n try {\n const page = await repository.listMessages(id, { limit: messagePageSize });\n store.setThread(id, page.items, { hasMoreOlder: page.hasMore });\n } catch (error) {\n reportError(error, \"openConversation\");\n }\n\n if (openChannels.has(id) || disposed) return;\n const handle = manager.open({\n topic: conversationTopic(id),\n postgresChanges: [\n {\n event: \"*\",\n schema: \"communication\",\n table: \"dm_messages\",\n filter: `conversation_id=eq.${id}`,\n rowId: (row) => (typeof row[\"id\"] === \"string\" ? row[\"id\"] : undefined),\n fingerprint: (row) =>\n typeof row[\"content\"] === \"string\" ? row[\"content\"] : undefined,\n updatedAtField: \"updated_at\",\n updatedByField: \"updated_by\",\n onChange: ({ row }) => {\n if (row === null) return;\n store.ingest(projectMessage(row, identity.organizationId));\n },\n },\n ],\n broadcast: [\n {\n event: MESSAGING_EVENTS.message,\n onMessage: ({ data }) => {\n if (typeof data !== \"object\" || data === null) return;\n // Broadcast payloads are already domain-shaped (we sent them), so\n // they go through the same one door every message uses.\n store.ingest(data as Message);\n },\n },\n ],\n onBackfill: () => {\n void backfillConversation(id);\n outbox.flush();\n },\n });\n openChannels.set(id, handle);\n },\n\n closeConversation(id) {\n openChannels.get(id)?.close();\n openChannels.delete(id);\n if (store.snapshot().activeConversationId === id) {\n store.setActiveConversation(null);\n }\n },\n\n async loadOlderMessages(id) {\n const thread = store.snapshot().threads.get(id);\n const oldest = thread?.messages[0];\n if (thread === undefined || oldest === undefined || !thread.hasMoreOlder) return;\n try {\n const page = await repository.listMessages(id, {\n limit: messagePageSize,\n cursor: { beforeCreatedAt: oldest.createdAt, beforeMessageId: oldest.id },\n });\n store.prependOlder(id, page.items, page.hasMore);\n } catch (error) {\n reportError(error, \"loadOlderMessages\");\n }\n },\n\n send(draft) {\n if (draft.content.trim().length === 0 && (draft.attachments ?? []).length === 0) {\n throw new MessagingError(\n \"misconfigured\",\n \"send: empty draft\",\n \"A message needs text or at least one attachment. The composer disables Send \" +\n \"for an empty draft rather than queueing nothing.\",\n );\n }\n return outbox.enqueue(draft);\n },\n\n retry: (entryId) => outbox.retry(entryId),\n discard: (entryId) => outbox.discard(entryId),\n\n async editMessage(id, conversationId, content) {\n try {\n const message = await repository.editMessage(id, content);\n store.ingest(message);\n broadcastMessage(message);\n } catch (error) {\n reportError(error, \"editMessage\");\n throw normalizeMessagingError(error, \"editMessage\");\n }\n void conversationId;\n },\n\n async deleteMessage(id, conversationId, forEveryone) {\n try {\n await repository.deleteMessage(id, forEveryone);\n store.removeMessage(conversationId, id);\n } catch (error) {\n reportError(error, \"deleteMessage\");\n throw normalizeMessagingError(error, \"deleteMessage\");\n }\n },\n\n async markRead(id) {\n // Optimistic first: the badge must clear the instant the user opens the\n // thread, not one round trip later.\n store.markConversationRead(id);\n try {\n await repository.markRead(id);\n } catch (error) {\n reportError(error, \"markRead\");\n }\n },\n\n async startDirectConversation(otherUserId) {\n const id = await repository.getOrCreateDirectConversation(otherUserId);\n await reloadInbox();\n return id;\n },\n\n dispose() {\n disposed = true;\n outbox.dispose();\n openChannels.forEach((handle) => handle.close());\n openChannels.clear();\n inboxChannel?.close();\n inboxChannel = null;\n },\n };\n\n return engine;\n}\n\n/** The realtime client session id, exposed for diagnostics surfaces. */\nexport function messagingClientId(): string {\n return clientSessionId();\n}\n","/**\n * THE OUTBOX — a message you typed is NEVER lost (R2, table-stakes law).\n *\n * This is the piece hosts always get wrong, so it is entirely in the package\n * (C22). What \"never lost\" actually requires, and what each part defends:\n *\n * - **Durability across a reload.** The draft is persisted BEFORE the network\n * call, not after it succeeds. A tab closed mid-send comes back with the\n * message still queued. The storage port has a working default; a host that\n * injects nothing still gets in-memory queuing, and it SAYS so rather than\n * pretending to be durable.\n * - **Exactly-once on retry.** Every entry carries a client-minted\n * `clientMessageId` that is generated ONCE and reused by every attempt. A\n * retry after a response that was actually delivered is deduped by the\n * database's idempotency key instead of posting the message twice.\n * - **Order.** Entries for one conversation send strictly in order; message 2\n * never overtakes a retrying message 1.\n * - **Bounded, honest failure.** Attempts back off; after `maxAttempts` the\n * entry stays in the queue marked `failed` WITH a reason and a retry door.\n * It is never silently dropped, and it never retries forever.\n */\n\nimport { randomId } from \"@ai-matrx/realtime\";\nimport { MessagingError } from \"./errors\";\nimport type { ClientMessageId, ConversationId, DraftMessage, Message } from \"./types\";\n\nexport interface OutboxEntry {\n readonly id: string;\n readonly clientMessageId: ClientMessageId;\n readonly draft: DraftMessage;\n readonly queuedAt: number;\n readonly attempts: number;\n readonly state: \"queued\" | \"sending\" | \"failed\";\n readonly failureReason: string | null;\n}\n\n/**\n * Persistence for pending sends. A default is always supplied — the package\n * never leaves a port empty (THE ALL-INCLUSIVE LAW).\n */\nexport interface OutboxStorage {\n /** Human-readable, shown in diagnostics so \"durable\" is never assumed. */\n readonly name: string;\n readonly durable: boolean;\n load(): readonly OutboxEntry[];\n save(entries: readonly OutboxEntry[]): void;\n}\n\nexport function createMemoryOutboxStorage(): OutboxStorage {\n let held: readonly OutboxEntry[] = [];\n return {\n name: \"memory\",\n durable: false,\n load: () => held,\n save: (entries) => {\n held = entries;\n },\n };\n}\n\ninterface WebStorageLike {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n}\n\n/**\n * The real default in a browser. Falls back to memory — announcing itself —\n * when storage is unavailable (private mode, a disabled-cookies browser, SSR).\n */\nexport function createWebOutboxStorage(args: {\n key?: string;\n storage?: WebStorageLike | null;\n onFallback?: (reason: string) => void;\n}): OutboxStorage {\n const key = args.key ?? \"ai-matrx.messaging.outbox\";\n let storage: WebStorageLike | null = args.storage ?? null;\n if (storage === null) {\n try {\n const candidate = (globalThis as { localStorage?: WebStorageLike }).localStorage;\n storage = candidate ?? null;\n } catch {\n storage = null;\n }\n }\n if (storage === null) {\n args.onFallback?.(\n \"localStorage is unavailable, so queued messages will NOT survive a reload. \" +\n \"Inject an OutboxStorage on <MessagingProvider> to restore durability.\",\n );\n return createMemoryOutboxStorage();\n }\n const backing = storage;\n return {\n name: \"web-storage\",\n durable: true,\n load() {\n try {\n const raw = backing.getItem(key);\n if (raw === null) return [];\n const parsed: unknown = JSON.parse(raw);\n return Array.isArray(parsed) ? (parsed as OutboxEntry[]) : [];\n } catch {\n // Corrupt storage must not brick the composer. Start empty and say so\n // through the next save.\n return [];\n }\n },\n save(entries) {\n try {\n backing.setItem(key, JSON.stringify(entries));\n } catch {\n args.onFallback?.(\n \"Writing the outbox to localStorage failed (quota or private mode); queued \" +\n \"messages are in memory only for this session.\",\n );\n }\n },\n };\n}\n\nexport interface OutboxOptions {\n /** Actually send. Returns the persisted message. */\n send: (draft: DraftMessage, clientMessageId: ClientMessageId) => Promise<Message>;\n /** Called when an entry lands. The store collapses it with the optimistic row. */\n onSent: (entry: OutboxEntry, message: Message) => void;\n onChange: (entries: readonly OutboxEntry[]) => void;\n onDiagnostic?: (message: string) => void;\n storage?: OutboxStorage;\n maxAttempts?: number;\n /** Backoff schedule per attempt, ms. The last value repeats. */\n backoffMs?: readonly number[];\n timers?: {\n setTimeout: (run: () => void, ms: number) => unknown;\n clearTimeout: (handle: unknown) => void;\n now: () => number;\n };\n}\n\nexport interface Outbox {\n entries(): readonly OutboxEntry[];\n /** Queue a draft. Returns the id the optimistic message must carry. */\n enqueue(draft: DraftMessage): ClientMessageId;\n /** Retry one failed entry, on the user's explicit ask. */\n retry(entryId: string): void;\n /** Drop one entry — the only sanctioned way a typed message leaves the queue. */\n discard(entryId: string): void;\n /** Network came back / tab woke: try everything that is waiting. */\n flush(): void;\n pendingFor(conversationId: ConversationId): readonly OutboxEntry[];\n dispose(): void;\n}\n\nconst DEFAULT_BACKOFF = [0, 1_000, 3_000, 8_000, 20_000] as const;\n\nexport function createOutbox(options: OutboxOptions): Outbox {\n const timers = options.timers ?? {\n setTimeout: (run, ms) => setTimeout(run, ms),\n clearTimeout: (handle) => clearTimeout(handle as ReturnType<typeof setTimeout>),\n now: () => Date.now(),\n };\n const storage = options.storage ?? createMemoryOutboxStorage();\n const maxAttempts = options.maxAttempts ?? DEFAULT_BACKOFF.length;\n const backoff = options.backoffMs ?? DEFAULT_BACKOFF;\n\n let entries: OutboxEntry[] = [...storage.load()].map((entry) => ({\n ...entry,\n // Anything found mid-`sending` after a reload is genuinely unknown: it may\n // or may not have reached the database. It goes back to `queued` and the\n // idempotency key makes the re-send safe — that is exactly what the key is\n // for. Marking it failed instead would strand a message that was typed.\n state: entry.state === \"sending\" ? \"queued\" : entry.state,\n }));\n let timer: unknown = null;\n let disposed = false;\n\n function persist(): void {\n storage.save(entries);\n options.onChange(entries);\n }\n\n function delayFor(attempts: number): number {\n return backoff[Math.min(attempts, backoff.length - 1)] ?? 0;\n }\n\n function schedule(ms: number): void {\n if (disposed || timer !== null) return;\n timer = timers.setTimeout(() => {\n timer = null;\n void pump();\n }, ms);\n }\n\n async function pump(): Promise<void> {\n if (disposed) return;\n // ORDER: one entry at a time, oldest first. Message 2 must never overtake a\n // retrying message 1 in the same conversation, and the simplest guarantee\n // of that is a single-file queue.\n const next = entries.find((entry) => entry.state === \"queued\");\n if (next === undefined) return;\n\n entries = entries.map((entry) =>\n entry.id === next.id ? { ...entry, state: \"sending\" as const } : entry,\n );\n persist();\n\n try {\n const message = await options.send(next.draft, next.clientMessageId);\n entries = entries.filter((entry) => entry.id !== next.id);\n persist();\n options.onSent(next, message);\n schedule(0);\n } catch (error) {\n const attempts = next.attempts + 1;\n const reason =\n error instanceof MessagingError ? error.message : String((error as Error)?.message ?? error);\n const exhausted = attempts >= maxAttempts;\n entries = entries.map((entry) =>\n entry.id === next.id\n ? {\n ...entry,\n attempts,\n state: exhausted ? (\"failed\" as const) : (\"queued\" as const),\n failureReason: reason,\n }\n : entry,\n );\n persist();\n if (exhausted) {\n options.onDiagnostic?.(\n `Message could not be sent after ${attempts} attempts: ${reason}. It is still in ` +\n `the outbox — offer the user Retry or Discard; never drop it.`,\n );\n // Keep going: a later entry may be for a healthy conversation.\n schedule(0);\n } else {\n schedule(delayFor(attempts));\n }\n }\n }\n\n const outbox: Outbox = {\n entries: () => entries,\n enqueue(draft) {\n const clientMessageId = `mx-${randomId()}` as ClientMessageId;\n entries = [\n ...entries,\n {\n id: randomId(),\n clientMessageId,\n draft,\n queuedAt: timers.now(),\n attempts: 0,\n state: \"queued\",\n failureReason: null,\n },\n ];\n // PERSIST FIRST, send second. The window between \"user pressed enter\" and\n // \"the request left\" is exactly where messages get lost.\n persist();\n schedule(0);\n return clientMessageId;\n },\n retry(entryId) {\n entries = entries.map((entry) =>\n entry.id === entryId\n ? { ...entry, state: \"queued\" as const, attempts: 0, failureReason: null }\n : entry,\n );\n persist();\n schedule(0);\n },\n discard(entryId) {\n entries = entries.filter((entry) => entry.id !== entryId);\n persist();\n },\n flush() {\n entries = entries.map((entry) =>\n entry.state === \"failed\"\n ? { ...entry, state: \"queued\" as const, attempts: 0 }\n : entry,\n );\n persist();\n schedule(0);\n },\n pendingFor(conversationId) {\n return entries.filter((entry) => entry.draft.conversationId === conversationId);\n },\n dispose() {\n disposed = true;\n if (timer !== null) timers.clearTimeout(timer);\n timer = null;\n },\n };\n\n if (entries.length > 0) {\n options.onDiagnostic?.(\n `Restored ${entries.length} unsent message(s) from the ${storage.name} outbox.`,\n );\n schedule(0);\n }\n\n return outbox;\n}\n","/**\n * THE INGRESS BOUNDARY — where a database row becomes a domain object.\n *\n * Everything crossing this line is validated, because the alternative is a\n * screen that lies. A conversation row whose `participants` aggregate came back\n * as a string instead of an array used to render an inbox entry named\n * `undefined` with a working click target; refusing the row here turns a silent\n * wrong screen into a loud, remediable error (nothing fails silently).\n *\n * The rules:\n * - A missing REQUIRED field is a refusal, never a `?? \"\"` that renders blank.\n * - A missing OPTIONAL field is a null, never an invented default.\n * - `metadata` / `action_data` are host-and-DB-owned JSON: typed over `unknown`\n * and narrowed by shape, never cast (the data 0.2.1 `Json = unknown` lesson).\n */\n\nimport { invalidResponse } from \"./errors\";\nimport type {\n Attachment,\n ClientMessageId,\n ConversationId,\n ConversationSummary,\n ConversationType,\n DeliveryState,\n JsonObject,\n MatrxReference,\n Message,\n MessageAction,\n MessageId,\n MessageKind,\n OrganizationId,\n ParticipantRole,\n UserId,\n UserSummary,\n} from \"./types\";\n\nfunction str(row: Record<string, unknown>, key: string): string | null {\n const value = row[key];\n return typeof value === \"string\" && value.length > 0 ? value : null;\n}\n\nfunction requiredStr(\n row: Record<string, unknown>,\n key: string,\n operation: string,\n): string {\n const value = str(row, key);\n if (value === null) {\n throw invalidResponse(operation, `required field \"${key}\" was ${JSON.stringify(row[key])}`);\n }\n return value;\n}\n\nfunction bool(row: Record<string, unknown>, key: string, fallback: boolean): boolean {\n const value = row[key];\n return typeof value === \"boolean\" ? value : fallback;\n}\n\nfunction jsonObject(value: unknown): JsonObject {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as JsonObject)\n : {};\n}\n\nconst CONVERSATION_TYPES: ReadonlySet<string> = new Set([\"direct\", \"group\", \"org\"]);\nconst MESSAGE_KINDS: ReadonlySet<string> = new Set([\n \"text\",\n \"image\",\n \"video\",\n \"audio\",\n \"file\",\n \"system\",\n \"action\",\n]);\nconst ROLES: ReadonlySet<string> = new Set([\"owner\", \"admin\", \"member\"]);\nconst DELIVERY_STATES: ReadonlySet<string> = new Set([\n \"sending\",\n \"sent\",\n \"delivered\",\n \"read\",\n \"failed\",\n]);\n\nexport function projectUserSummary(row: Record<string, unknown>): UserSummary {\n const userId = requiredStr(row, \"user_id\", \"projectUserSummary\") as UserId;\n const displayName = str(row, \"display_name\") ?? str(row, \"email\") ?? userId;\n return {\n userId,\n displayName,\n email: str(row, \"email\"),\n avatarUrl: str(row, \"avatar_url\"),\n isAgent: bool(row, \"is_agent\", false),\n };\n}\n\n/** The `participants` aggregate on the conversation-list RPC row. */\nfunction projectParticipants(value: unknown, operation: string): readonly UserSummary[] {\n if (value === null || value === undefined) return [];\n if (!Array.isArray(value)) {\n throw invalidResponse(\n operation,\n `\"participants\" was ${typeof value}, not an array — the RPC's jsonb aggregate is malformed`,\n );\n }\n const summaries: UserSummary[] = [];\n for (const entry of value) {\n if (typeof entry !== \"object\" || entry === null) continue;\n const record = entry as Record<string, unknown>;\n // A participant aggregate that carries `id` instead of `user_id` is the\n // real historical shape drift; accept both, refuse neither-present.\n const id = str(record, \"user_id\") ?? str(record, \"id\");\n if (id === null) continue;\n summaries.push(\n projectUserSummary({\n ...record,\n user_id: id,\n }),\n );\n }\n return summaries;\n}\n\nexport function projectMessageAction(value: unknown): MessageAction | null {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return null;\n const record = value as Record<string, unknown>;\n const kind = record[\"kind\"];\n if (typeof kind !== \"string\" || kind.length === 0) return null;\n const version = record[\"version\"];\n return {\n kind,\n // A payload without a version is version 1 — the shape that predates the\n // envelope. Refusing it would silently blank every message sent before the\n // envelope existed.\n version: typeof version === \"number\" && Number.isFinite(version) ? version : 1,\n payload: jsonObject(record[\"payload\"]),\n };\n}\n\nfunction projectAttachments(metadata: JsonObject): readonly Attachment[] {\n const raw = (metadata as Record<string, unknown>)[\"attachments\"];\n if (!Array.isArray(raw)) return [];\n const attachments: Attachment[] = [];\n for (const entry of raw) {\n if (typeof entry !== \"object\" || entry === null) continue;\n const record = entry as Record<string, unknown>;\n // A DURABLE file id is the identity. An entry that only has a URL is a\n // signed-URL identity — the thing that breaks the moment the link expires —\n // so it is dropped rather than rendered as an attachment that will 403.\n const fileId = str(record, \"fileId\") ?? str(record, \"file_id\");\n if (fileId === null) continue;\n const size = record[\"sizeBytes\"] ?? record[\"size_bytes\"];\n const width = record[\"width\"];\n const height = record[\"height\"];\n attachments.push({\n fileId,\n fileName: str(record, \"fileName\") ?? str(record, \"file_name\") ?? fileId,\n mimeType: str(record, \"mimeType\") ?? str(record, \"mime_type\"),\n sizeBytes: typeof size === \"number\" ? size : null,\n width: typeof width === \"number\" ? width : null,\n height: typeof height === \"number\" ? height : null,\n });\n }\n return attachments;\n}\n\nfunction projectReferences(metadata: JsonObject): readonly MatrxReference[] {\n const raw = (metadata as Record<string, unknown>)[\"references\"];\n if (!Array.isArray(raw)) return [];\n const references: MatrxReference[] = [];\n for (const entry of raw) {\n if (typeof entry !== \"object\" || entry === null) continue;\n const record = entry as Record<string, unknown>;\n const entityType = str(record, \"entityType\") ?? str(record, \"entity_type\");\n const entityId = str(record, \"entityId\") ?? str(record, \"entity_id\");\n // NO DEAD ENDS: a reference without a resolvable identity cannot be opened,\n // so it is never rendered as an openable card.\n if (entityType === null || entityId === null) continue;\n const href = str(record, \"href\");\n references.push({\n entityType,\n entityId,\n label: str(record, \"label\") ?? entityId,\n ...(href !== null ? { href } : {}),\n });\n }\n return references;\n}\n\nexport function projectMessage(\n row: Record<string, unknown>,\n fallbackOrganizationId: OrganizationId,\n): Message {\n const operation = \"projectMessage\";\n const metadata = jsonObject(row[\"metadata\"]);\n const kindRaw = str(row, \"message_type\") ?? \"text\";\n const stateRaw = str(row, \"status\") ?? \"sent\";\n const replyTo = str(row, \"reply_to_id\");\n const clientMessageId = str(row, \"client_message_id\");\n const action = projectMessageAction(row[\"action_data\"]);\n return {\n id: requiredStr(row, \"id\", operation) as MessageId,\n conversationId: requiredStr(row, \"conversation_id\", operation) as ConversationId,\n senderId: requiredStr(row, \"sender_id\", operation) as UserId,\n organizationId: (str(row, \"organization_id\") ?? fallbackOrganizationId) as OrganizationId,\n content: typeof row[\"content\"] === \"string\" ? (row[\"content\"] as string) : \"\",\n kind: (MESSAGE_KINDS.has(kindRaw) ? kindRaw : \"text\") as MessageKind,\n // A row read from the DB is at least `sent`. `sending`/`failed` are outbox\n // states and can never be projected from a persisted row.\n deliveryState: (DELIVERY_STATES.has(stateRaw) && stateRaw !== \"sending\" && stateRaw !== \"failed\"\n ? stateRaw\n : \"sent\") as DeliveryState,\n replyToId: replyTo === null ? null : (replyTo as MessageId),\n clientMessageId: clientMessageId === null ? null : (clientMessageId as ClientMessageId),\n createdAt: requiredStr(row, \"created_at\", operation),\n editedAt: str(row, \"edited_at\"),\n deletedAt: str(row, \"deleted_at\"),\n deletedForEveryone: bool(row, \"deleted_for_everyone\", false),\n action,\n attachments: projectAttachments(metadata),\n references: projectReferences(metadata),\n metadata,\n };\n}\n\nexport function projectParticipantRole(value: unknown): ParticipantRole {\n return (typeof value === \"string\" && ROLES.has(value) ? value : \"member\") as ParticipantRole;\n}\n\nexport function projectConversationSummary(\n row: Record<string, unknown>,\n viewerId: UserId,\n fallbackOrganizationId: OrganizationId,\n): ConversationSummary {\n const operation = \"projectConversationSummary\";\n const id = requiredStr(row, \"conversation_id\", operation) as ConversationId;\n const typeRaw = str(row, \"conversation_type\") ?? \"direct\";\n const type = (CONVERSATION_TYPES.has(typeRaw) ? typeRaw : \"direct\") as ConversationType;\n const participants = projectParticipants(row[\"participants\"], operation);\n const groupName = str(row, \"group_name\");\n const groupImageUrl = str(row, \"group_image_url\");\n const createdBy = str(row, \"created_by\");\n const updatedAt = str(row, \"conversation_updated_at\") ?? str(row, \"updated_at\");\n const createdAt = str(row, \"conversation_created_at\") ?? str(row, \"created_at\") ?? updatedAt;\n const lastMessageAt = str(row, \"last_message_at\");\n const lastSender = str(row, \"last_message_sender_id\");\n const unreadRaw = row[\"unread_count\"];\n\n if (createdAt === null || updatedAt === null) {\n throw invalidResponse(operation, `conversation ${id} carried no timestamps`);\n }\n\n const others = participants.filter((participant) => participant.userId !== viewerId);\n const displayName =\n type === \"direct\"\n ? (others[0]?.displayName ?? \"Direct message\")\n : (groupName ?? \"Group conversation\");\n const displayImageUrl = type === \"direct\" ? (others[0]?.avatarUrl ?? null) : groupImageUrl;\n\n return {\n conversation: {\n id,\n type,\n groupName,\n groupImageUrl,\n createdBy: createdBy === null ? null : (createdBy as UserId),\n organizationId: (str(row, \"organization_id\") ?? fallbackOrganizationId) as OrganizationId,\n createdAt,\n updatedAt,\n metadata: jsonObject(row[\"metadata\"]),\n },\n participants,\n lastMessageContent: str(row, \"last_message_content\"),\n lastMessageSenderId: lastSender === null ? null : (lastSender as UserId),\n lastMessageAt,\n unreadCount:\n typeof unreadRaw === \"number\" && Number.isFinite(unreadRaw) && unreadRaw > 0\n ? Math.floor(unreadRaw)\n : 0,\n isMuted: bool(row, \"is_muted\", false),\n isArchived: bool(row, \"is_archived\", false),\n displayName,\n displayImageUrl,\n // The keyset sort value: the last message if there is one, else the\n // conversation's own update stamp. Empty conversations must still sort.\n sortAt: lastMessageAt ?? updatedAt,\n };\n}\n","/**\n * THE MESSAGE STORE — where \"the same message twice\" is made impossible.\n *\n * A message can reach this store by FOUR different paths, often in any order:\n * the optimistic bubble, the insert's own response, the broadcast, and the\n * Postgres Changes row. Every duplicate bug in every chat product is one of\n * those four arriving out of order. The rules here, each with a regression test:\n *\n * 1. **Identity is `id` OR `clientMessageId`.** The confirmed row and the\n * optimistic bubble are the SAME message; the client-minted key is what\n * proves it. Matching on `(sender, content)` — the tempting shortcut — merges\n * two genuinely different \"ok\" messages into one.\n * 2. **Merge collapses; it never appends.** A confirmed row that finds an\n * optimistic twin REPLACES it in place, keeping the twin's position so the\n * bubble does not jump.\n * 3. **Older never overwrites newer.** An out-of-order UPDATE carrying an older\n * `edited_at`/`created_at` than the held copy is DROPPED. Without this an\n * edit visibly reverts itself a second later.\n * 4. **Order is `created_at` then `id`.** The unique tiebreaker is not\n * decoration: two messages in the same millisecond otherwise swap places on\n * every re-render.\n * 5. **The active conversation's unread count is forced to zero.** A stale\n * server count must never re-badge a conversation the user is reading — a\n * safety net the origin needed in three separate places, so it lives in one\n * place here.\n */\n\nimport type {\n ClientMessageId,\n ConversationId,\n ConversationSummary,\n Message,\n MessageId,\n UserId,\n} from \"./types\";\n\nexport interface ConversationThread {\n readonly conversationId: ConversationId;\n readonly messages: readonly Message[];\n readonly hasMoreOlder: boolean;\n /** The newest `created_at` held — the reconnect backfill's high-water mark. */\n readonly latestAt: string | null;\n}\n\nexport interface MessagingSnapshot {\n readonly conversations: readonly ConversationSummary[];\n readonly hasMoreConversations: boolean;\n /**\n * True once the inbox has actually been READ at least once.\n *\n * An empty list means two completely different things — \"you have no\n * conversations\" and \"we have not looked yet\" — and a UI that cannot tell\n * them apart shows a confident \"No conversations yet\" to someone who has\n * hundreds. That is a screen telling a lie, so the distinction is a fact the\n * store carries rather than something each surface guesses from a timer.\n */\n readonly hasLoadedConversations: boolean;\n readonly threads: ReadonlyMap<ConversationId, ConversationThread>;\n readonly activeConversationId: ConversationId | null;\n /** Conversations WITH unread, not total unread messages (the origin's semantics). */\n readonly totalUnreadConversations: number;\n}\n\nexport interface MessagingStore {\n snapshot(): MessagingSnapshot;\n subscribe(listener: (snapshot: MessagingSnapshot) => void): () => void;\n setConversations(items: readonly ConversationSummary[], hasMore: boolean): void;\n appendConversations(items: readonly ConversationSummary[], hasMore: boolean): void;\n upsertConversation(item: ConversationSummary): void;\n removeConversation(id: ConversationId): void;\n setActiveConversation(id: ConversationId | null): void;\n setThread(\n id: ConversationId,\n messages: readonly Message[],\n args?: { hasMoreOlder?: boolean },\n ): void;\n prependOlder(id: ConversationId, messages: readonly Message[], hasMoreOlder: boolean): void;\n /** The one door every incoming message uses. Returns what actually happened. */\n ingest(message: Message): \"added\" | \"merged\" | \"dropped-stale\" | \"dropped-duplicate\";\n ingestMany(messages: readonly Message[]): void;\n removeMessage(conversationId: ConversationId, messageId: MessageId): void;\n markConversationRead(id: ConversationId): void;\n /**\n * Move a conversation's preview + sort position to reflect a message, with NO\n * refetch. Sending a message must not cost a conversation-list RPC — at a\n * busy org that is one full page read per keystroke-burst, and the row's new\n * state is entirely derivable from the message we already hold.\n */\n applyLastMessage(message: Message, args?: { incrementUnread?: boolean }): void;\n setUnreadCount(id: ConversationId, count: number): void;\n}\n\nfunction timeOf(message: Message): number {\n const stamp = message.editedAt ?? message.createdAt;\n const parsed = Date.parse(stamp);\n return Number.isFinite(parsed) ? parsed : 0;\n}\n\n/** `created_at` then `id` — the unique tiebreaker keeps the order stable. */\nfunction compare(a: Message, b: Message): number {\n if (a.createdAt !== b.createdAt) return a.createdAt < b.createdAt ? -1 : 1;\n return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;\n}\n\nfunction sameMessage(a: Message, b: Message): boolean {\n if (a.id === b.id) return true;\n const key: ClientMessageId | null = b.clientMessageId;\n return key !== null && a.clientMessageId === key;\n}\n\nfunction insertOrdered(messages: readonly Message[], message: Message): Message[] {\n const next = [...messages];\n // Newest-last is the overwhelmingly common case, so scan from the end.\n let index = next.length;\n while (index > 0) {\n const candidate = next[index - 1];\n if (candidate === undefined || compare(candidate, message) <= 0) break;\n index -= 1;\n }\n next.splice(index, 0, message);\n return next;\n}\n\nexport function createMessagingStore(): MessagingStore {\n let conversations: readonly ConversationSummary[] = [];\n let hasMoreConversations = false;\n let hasLoadedConversations = false;\n let threads = new Map<ConversationId, ConversationThread>();\n let activeConversationId: ConversationId | null = null;\n const listeners = new Set<(snapshot: MessagingSnapshot) => void>();\n let cached: MessagingSnapshot | null = null;\n\n function snapshot(): MessagingSnapshot {\n if (cached !== null) return cached;\n cached = {\n conversations,\n hasMoreConversations,\n hasLoadedConversations,\n threads,\n activeConversationId,\n totalUnreadConversations: conversations.filter((item) => item.unreadCount > 0).length,\n };\n return cached;\n }\n\n function emit(): void {\n cached = null;\n const next = snapshot();\n listeners.forEach((listener) => listener(next));\n }\n\n /**\n * RULE 5, in one place. Any path that writes the conversation list runs\n * through here, so a stale server count can never re-badge the conversation\n * the user is currently looking at.\n */\n function normalizeConversations(items: readonly ConversationSummary[]): readonly ConversationSummary[] {\n const active = activeConversationId;\n const zeroed =\n active === null\n ? items\n : items.map((item) =>\n item.conversation.id === active && item.unreadCount !== 0\n ? { ...item, unreadCount: 0 }\n : item,\n );\n return [...zeroed].sort((a, b) => (a.sortAt < b.sortAt ? 1 : a.sortAt > b.sortAt ? -1 : 0));\n }\n\n function threadFor(id: ConversationId): ConversationThread {\n return (\n threads.get(id) ?? {\n conversationId: id,\n messages: [],\n hasMoreOlder: false,\n latestAt: null,\n }\n );\n }\n\n function writeThread(thread: ConversationThread): void {\n const next = new Map(threads);\n const latest = thread.messages.at(-1);\n next.set(thread.conversationId, {\n ...thread,\n latestAt: latest?.createdAt ?? thread.latestAt,\n });\n threads = next;\n }\n\n const store: MessagingStore = {\n snapshot,\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n setConversations(items, hasMore) {\n conversations = normalizeConversations(items);\n hasMoreConversations = hasMore;\n hasLoadedConversations = true;\n emit();\n },\n appendConversations(items, hasMore) {\n // Merge by id: a realtime insert may already have put a row here, and a\n // \"load more\" page that appends it again is the duplicate-row bug.\n const byId = new Map(conversations.map((item) => [item.conversation.id, item]));\n items.forEach((item) => byId.set(item.conversation.id, item));\n conversations = normalizeConversations([...byId.values()]);\n hasMoreConversations = hasMore;\n emit();\n },\n upsertConversation(item) {\n const byId = new Map(conversations.map((entry) => [entry.conversation.id, entry]));\n byId.set(item.conversation.id, item);\n conversations = normalizeConversations([...byId.values()]);\n emit();\n },\n removeConversation(id) {\n conversations = conversations.filter((item) => item.conversation.id !== id);\n const next = new Map(threads);\n next.delete(id);\n threads = next;\n emit();\n },\n setActiveConversation(id) {\n activeConversationId = id;\n conversations = normalizeConversations(conversations);\n emit();\n },\n setThread(id, messages, args = {}) {\n writeThread({\n conversationId: id,\n messages: [...messages].sort(compare),\n hasMoreOlder: args.hasMoreOlder ?? false,\n latestAt: null,\n });\n emit();\n },\n prependOlder(id, messages, hasMoreOlder) {\n const thread = threadFor(id);\n const known = new Set(thread.messages.map((message) => message.id));\n const fresh = messages.filter((message) => !known.has(message.id));\n writeThread({\n ...thread,\n messages: [...fresh, ...thread.messages].sort(compare),\n hasMoreOlder,\n });\n emit();\n },\n ingest(message) {\n const thread = threadFor(message.conversationId);\n const index = thread.messages.findIndex((held) => sameMessage(held, message));\n\n if (index === -1) {\n writeThread({ ...thread, messages: insertOrdered(thread.messages, message) });\n emit();\n return \"added\";\n }\n\n const held = thread.messages[index];\n if (held === undefined) return \"dropped-duplicate\";\n\n // RULE 3. An optimistic row is ALWAYS superseded by a persisted one, even\n // though its clock may read later — the optimistic timestamp is a guess.\n const heldIsOptimistic =\n held.deliveryState === \"sending\" || held.deliveryState === \"failed\";\n if (!heldIsOptimistic && timeOf(message) < timeOf(held)) return \"dropped-stale\";\n if (!heldIsOptimistic && held.id === message.id && timeOf(message) === timeOf(held)) {\n return \"dropped-duplicate\";\n }\n\n // RULE 2: collapse in place, keeping the position.\n const merged: Message = {\n ...held,\n ...message,\n // Never lose the client key — it is what future echoes match on.\n clientMessageId: message.clientMessageId ?? held.clientMessageId,\n };\n const messages = [...thread.messages];\n messages[index] = merged;\n writeThread({ ...thread, messages: messages.sort(compare) });\n emit();\n return \"merged\";\n },\n ingestMany(messages) {\n messages.forEach((message) => {\n store.ingest(message);\n });\n },\n removeMessage(conversationId, messageId) {\n const thread = threadFor(conversationId);\n writeThread({\n ...thread,\n messages: thread.messages.filter((message) => message.id !== messageId),\n });\n emit();\n },\n applyLastMessage(message, args = {}) {\n const existing = conversations.find(\n (item) => item.conversation.id === message.conversationId,\n );\n // A message for a conversation the list has never seen cannot be\n // synthesized honestly (no participants, no display name). The caller\n // re-reads instead; that is the one case worth a request.\n if (existing === undefined) return;\n if (message.deliveryState === \"sending\" || message.deletedAt !== null) return;\n // Never move a row BACKWARDS: an out-of-order arrival must not make the\n // inbox show an older preview than it already has.\n if (existing.lastMessageAt !== null && message.createdAt < existing.lastMessageAt) return;\n\n // The caller decides whether this counts as unread (it knows who \"I\" am);\n // the store only refuses to badge the conversation being read.\n const isActive = activeConversationId === message.conversationId;\n const shouldCount = args.incrementUnread === true && !isActive;\n conversations = normalizeConversations(\n conversations.map((item) =>\n item.conversation.id === message.conversationId\n ? {\n ...item,\n lastMessageContent: message.content,\n lastMessageSenderId: message.senderId,\n lastMessageAt: message.createdAt,\n sortAt: message.createdAt,\n unreadCount: shouldCount ? item.unreadCount + 1 : item.unreadCount,\n }\n : item,\n ),\n );\n emit();\n },\n markConversationRead(id) {\n conversations = conversations.map((item) =>\n item.conversation.id === id ? { ...item, unreadCount: 0 } : item,\n );\n emit();\n },\n setUnreadCount(id, count) {\n conversations = normalizeConversations(\n conversations.map((item) =>\n item.conversation.id === id ? { ...item, unreadCount: Math.max(0, count) } : item,\n ),\n );\n emit();\n },\n };\n\n return store;\n}\n\n/**\n * The optimistic row a composer shows the instant Enter is pressed. It carries\n * the outbox's client key, which is the whole reason the confirmed row can find\n * and replace it rather than appearing beside it.\n */\nexport function optimisticMessage(args: {\n conversationId: ConversationId;\n senderId: UserId;\n organizationId: Message[\"organizationId\"];\n content: string;\n clientMessageId: ClientMessageId;\n kind?: Message[\"kind\"];\n replyToId?: MessageId | null;\n action?: Message[\"action\"];\n attachments?: readonly Message[\"attachments\"][number][];\n references?: readonly Message[\"references\"][number][];\n now?: () => number;\n}): Message {\n const at = new Date(args.now?.() ?? Date.now()).toISOString();\n return {\n // A temporary id that can never collide with a uuid from the database.\n id: `optimistic:${args.clientMessageId}` as MessageId,\n conversationId: args.conversationId,\n senderId: args.senderId,\n organizationId: args.organizationId,\n content: args.content,\n kind: args.kind ?? \"text\",\n deliveryState: \"sending\",\n replyToId: args.replyToId ?? null,\n clientMessageId: args.clientMessageId,\n createdAt: at,\n editedAt: null,\n deletedAt: null,\n deletedForEveryone: false,\n action: args.action ?? null,\n attachments: args.attachments ?? [],\n references: args.references ?? [],\n metadata: {},\n };\n}\n","/**\n * Display formatting. In the package because every consumer otherwise rebuilds\n * it slightly differently and the inbox and the thread disagree about what\n * \"yesterday\" means.\n *\n * Every function takes an explicit `now` so the tests do not depend on the\n * clock, and none of them touches `Intl` with an implicit locale — a package\n * that silently formats in the build machine's locale is a bug that only shows\n * up for users in other timezones.\n */\n\nimport type { Message, UserId, UserSummary } from \"./types\";\n\nconst MINUTE = 60_000;\nconst HOUR = 60 * MINUTE;\nconst DAY = 24 * HOUR;\n\nexport function isSameDay(a: Date, b: Date): boolean {\n return (\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n );\n}\n\n/** Compact stamp for a conversation row: `9:41 AM`, `Yesterday`, `Mar 4`. */\nexport function formatConversationTime(\n isoString: string | null,\n now: number = Date.now(),\n locale?: string,\n): string {\n if (isoString === null) return \"\";\n const parsed = Date.parse(isoString);\n if (!Number.isFinite(parsed)) return \"\";\n const then = new Date(parsed);\n const today = new Date(now);\n if (isSameDay(then, today)) {\n return then.toLocaleTimeString(locale, { hour: \"numeric\", minute: \"2-digit\" });\n }\n const yesterday = new Date(now - DAY);\n if (isSameDay(then, yesterday)) return \"Yesterday\";\n if (now - parsed < 7 * DAY) return then.toLocaleDateString(locale, { weekday: \"short\" });\n return then.toLocaleDateString(locale, { month: \"short\", day: \"numeric\" });\n}\n\n/** The stamp under a bubble. */\nexport function formatMessageTime(\n isoString: string,\n locale?: string,\n): string {\n const parsed = Date.parse(isoString);\n if (!Number.isFinite(parsed)) return \"\";\n return new Date(parsed).toLocaleTimeString(locale, {\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n}\n\n/** The separator between day groups in a thread. */\nexport function formatDateSeparator(\n isoString: string,\n now: number = Date.now(),\n locale?: string,\n): string {\n const parsed = Date.parse(isoString);\n if (!Number.isFinite(parsed)) return \"\";\n const then = new Date(parsed);\n const today = new Date(now);\n if (isSameDay(then, today)) return \"Today\";\n if (isSameDay(then, new Date(now - DAY))) return \"Yesterday\";\n if (then.getFullYear() === today.getFullYear()) {\n return then.toLocaleDateString(locale, { month: \"long\", day: \"numeric\" });\n }\n return then.toLocaleDateString(locale, {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n });\n}\n\nexport function getInitials(name: string): string {\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return \"?\";\n const first = parts[0]?.[0] ?? \"\";\n const last = parts.length > 1 ? (parts.at(-1)?.[0] ?? \"\") : \"\";\n return `${first}${last}`.toUpperCase() || \"?\";\n}\n\n/**\n * A stable palette index for an avatar with no image. Deterministic on the id,\n * so the same person is the same color on every device and every reload — a\n * random color per render is a surprisingly loud bug.\n */\nexport function avatarPaletteIndex(seed: string, buckets = 8): number {\n let hash = 0;\n for (let index = 0; index < seed.length; index += 1) {\n hash = (hash * 31 + seed.charCodeAt(index)) | 0;\n }\n return Math.abs(hash) % buckets;\n}\n\n/**\n * Group consecutive messages by the same sender within a window, the way every\n * good chat UI does — one avatar and one name per burst.\n */\nexport interface MessageGroup {\n readonly senderId: UserId;\n readonly messages: readonly Message[];\n readonly dateSeparator: string | null;\n}\n\nexport function groupMessages(\n messages: readonly Message[],\n args: { now?: number; windowMs?: number; locale?: string } = {},\n): readonly MessageGroup[] {\n const windowMs = args.windowMs ?? 5 * MINUTE;\n const now = args.now ?? Date.now();\n const groups: MessageGroup[] = [];\n let current: { senderId: UserId; messages: Message[]; dateSeparator: string | null } | null =\n null;\n let previousDay: string | null = null;\n\n for (const message of messages) {\n const day = message.createdAt.slice(0, 10);\n const startsNewDay = day !== previousDay;\n previousDay = day;\n\n const last = current?.messages.at(-1);\n const withinWindow =\n last !== undefined &&\n Math.abs(Date.parse(message.createdAt) - Date.parse(last.createdAt)) <= windowMs;\n\n if (\n current !== null &&\n current.senderId === message.senderId &&\n withinWindow &&\n !startsNewDay\n ) {\n current.messages.push(message);\n continue;\n }\n if (current !== null) groups.push(current);\n current = {\n senderId: message.senderId,\n messages: [message],\n dateSeparator: startsNewDay\n ? formatDateSeparator(message.createdAt, now, args.locale)\n : null,\n };\n }\n if (current !== null) groups.push(current);\n return groups;\n}\n\n/** \"Ana is typing…\" / \"Ana and Bo are typing…\" / \"3 people are typing…\" */\nexport function formatTypists(names: readonly string[]): string | null {\n if (names.length === 0) return null;\n if (names.length === 1) return `${names[0]} is typing…`;\n if (names.length === 2) return `${names[0]} and ${names[1]} are typing…`;\n return `${names.length} people are typing…`;\n}\n\nexport function participantNames(\n participants: readonly UserSummary[],\n excluding: UserId,\n): readonly string[] {\n return participants\n .filter((participant) => participant.userId !== excluding)\n .map((participant) => participant.displayName);\n}\n\n/** \"Active now\" / \"Active 5m ago\" — presence, said honestly. */\nexport function formatLastSeen(lastSeenMs: number | null, now: number = Date.now()): string {\n if (lastSeenMs === null) return \"\";\n const elapsed = now - lastSeenMs;\n if (elapsed < 2 * MINUTE) return \"Active now\";\n if (elapsed < HOUR) return `Active ${Math.round(elapsed / MINUTE)}m ago`;\n if (elapsed < DAY) return `Active ${Math.round(elapsed / HOUR)}h ago`;\n return `Active ${Math.round(elapsed / DAY)}d ago`;\n}\n","/**\n * THE DATA CONTRACT — the ONE place a messaging table or RPC name exists.\n *\n * Every name below was verified against Matrx Main (`https://db.matrxserver.com`)\n * on 2026-08-31, not guessed from a client. `matrx-dm` grew a parallel schema in\n * its own project (`public.conversations` / `messages` / `message_reactions`);\n * per R8 exactly ONE canonical schema survives and it is this one — the\n * platform-conventional `communication.dm_*` tables with explicit\n * `organization_id`, `version`, and soft-delete columns, behind auth-checked\n * SECURITY DEFINER RPCs.\n *\n * Four rules hold this file together:\n *\n * 1. **Org is explicit on every write (R5).** Not defaulted by a trigger we\n * hope fires, not inherited — passed, and refused in-package when absent.\n * 2. **Authorization is the database's job.** There is no permission branch in\n * this file. An RPC that says no is a `forbidden` MessagingError, never a\n * client-side re-decision.\n * 3. **Direct conversations are created ATOMICALLY, by RPC.** The banned\n * pattern — read `find_dm_direct_conversation`, then insert — races two\n * tabs into two conversations for the same pair. The RPC advisory-locks the\n * unordered pair. Nothing here may reintroduce the read-then-insert shape.\n * 4. **Pagination is keyset and terminated by a unique column.** Ordering by a\n * timestamp alone duplicates or skips rows whenever two share a millisecond\n * — which in a large org is every page.\n */\n\nimport { createReadCache, type ReadCache } from \"./cache\";\nimport { invalidResponse, MessagingError, normalizeMessagingError } from \"./errors\";\nimport { projectConversationSummary, projectMessage, projectUserSummary } from \"./projection\";\nimport type { SchemaLike, SupabaseLike } from \"./supabase-shape\";\nimport type {\n ClientMessageId,\n Conversation,\n ConversationCursor,\n ConversationId,\n ConversationSummary,\n DraftMessage,\n Message,\n MessageCursor,\n MessageId,\n MessagingIdentity,\n OrganizationId,\n Page,\n ParticipantRole,\n UserId,\n UserSummary,\n} from \"./types\";\n\n/** The schema. Messaging is never in `public`. */\nexport const MESSAGING_SCHEMA = \"communication\";\n\nexport const TABLES = {\n conversations: \"dm_conversations\",\n participants: \"dm_conversation_participants\",\n messages: \"dm_messages\",\n} as const;\n\nexport const RPCS = {\n /** Atomic direct-conversation creation. Advisory-locks the unordered pair. */\n getOrCreateDirect: \"dm_get_or_create_direct_conversation\",\n /** Conversation list + participants + last message + unread, keyset paged. */\n conversationsWithDetails: \"get_dm_conversations_with_details\",\n unreadCount: \"get_dm_unread_count\",\n userInfo: \"get_dm_user_info\",\n isParticipant: \"is_dm_participant\",\n} as const;\n\n/** Columns selected for a message. Explicit — `select(\"*\")` silently drifts. */\nconst MESSAGE_COLUMNS =\n \"id,conversation_id,sender_id,organization_id,content,message_type,status,\" +\n \"reply_to_id,client_message_id,action_data,media_url,media_thumbnail_url,\" +\n \"media_metadata,created_at,edited_at,deleted_at,deleted_for_everyone,metadata\";\n\nconst PARTICIPANT_COLUMNS =\n \"conversation_id,user_id,role,joined_at,last_read_at,is_muted,is_archived\";\n\nconst CONVERSATION_COLUMNS =\n \"id,type,group_name,group_image_url,created_by,organization_id,created_at,updated_at,metadata\";\n\n/**\n * The host's session source. A messaging read that lands between sign-in and\n * token arrival must not become a red error — it retries ONCE after the host\n * resolves a session, then reports `session-unavailable`.\n */\nexport type SessionResolver = () => Promise<unknown>;\n\nexport interface RepositoryOptions {\n client: SupabaseLike;\n identity: MessagingIdentity;\n /** Called once before a retry when a read fails with a missing session. */\n resolveSession?: SessionResolver | undefined;\n /** Profile-lookup cache TTL. Default 5 minutes (the frontend's proven value). */\n userTtlMs?: number | undefined;\n now?: (() => number) | undefined;\n}\n\nexport interface MessagingRepository {\n readonly identity: MessagingIdentity;\n listConversations(args?: {\n limit?: number;\n cursor?: ConversationCursor | null;\n }): Promise<Page<ConversationSummary, ConversationCursor>>;\n getConversation(id: ConversationId): Promise<Conversation>;\n listMessages(\n conversationId: ConversationId,\n args?: { limit?: number; cursor?: MessageCursor | null },\n ): Promise<Page<Message, MessageCursor>>;\n /** Messages created strictly after `since` — the reconnect backfill read. */\n messagesSince(conversationId: ConversationId, since: string): Promise<readonly Message[]>;\n getOrCreateDirectConversation(otherUserId: UserId): Promise<ConversationId>;\n createGroupConversation(args: {\n name: string;\n memberIds: readonly UserId[];\n }): Promise<ConversationId>;\n insertMessage(draft: DraftMessage, clientMessageId: ClientMessageId): Promise<Message>;\n editMessage(id: MessageId, content: string): Promise<Message>;\n deleteMessage(id: MessageId, forEveryone: boolean): Promise<void>;\n markRead(conversationId: ConversationId, at?: string): Promise<void>;\n setConversationFlags(\n conversationId: ConversationId,\n flags: { isMuted?: boolean; isArchived?: boolean },\n ): Promise<void>;\n addMembers(conversationId: ConversationId, memberIds: readonly UserId[]): Promise<void>;\n removeMember(conversationId: ConversationId, memberId: UserId): Promise<void>;\n setMemberRole(\n conversationId: ConversationId,\n memberId: UserId,\n role: ParticipantRole,\n ): Promise<void>;\n /** Cached + in-flight-deduped. N callers for one id make ONE request. */\n getUser(userId: UserId): Promise<UserSummary | null>;\n getUsers(userIds: readonly UserId[]): Promise<ReadonlyMap<UserId, UserSummary>>;\n searchMessages(args: {\n query: string;\n conversationId?: ConversationId | null;\n limit?: number;\n }): Promise<readonly Message[]>;\n invalidateUser(userId: UserId): void;\n}\n\nfunction requireOrg(organizationId: OrganizationId | undefined, operation: string): OrganizationId {\n if (typeof organizationId !== \"string\" || organizationId.length === 0) {\n throw new MessagingError(\n \"misconfigured\",\n `${operation}: no organization_id`,\n \"Every conversation and message write carries an explicit organization_id (R5). \" +\n \"Pass a real org on <MessagingProvider>; the package refuses the write rather \" +\n \"than letting an unscoped row reach the database.\",\n );\n }\n return organizationId;\n}\n\nexport function createMessagingRepository(\n options: RepositoryOptions,\n): MessagingRepository {\n const { client, identity } = options;\n const org = requireOrg(identity.organizationId, \"createMessagingRepository\");\n const userCache: ReadCache<UserSummary | null> = createReadCache({\n ttlMs: options.userTtlMs ?? 5 * 60_000,\n ...(options.now !== undefined ? { now: options.now } : {}),\n });\n\n const db = (): SchemaLike => client.schema(MESSAGING_SCHEMA);\n\n /**\n * THE ONE RETRY. A read that failed only because the session had not arrived\n * yet is retried once after the host resolves one. Everything else is thrown\n * as-is: retrying a `forbidden` is how a request storm is built.\n */\n async function withSessionRetry<T>(operation: string, run: () => Promise<T>): Promise<T> {\n try {\n return await run();\n } catch (error) {\n const normalized = normalizeMessagingError(error, operation);\n if (normalized.code !== \"session-unavailable\" || options.resolveSession === undefined) {\n throw normalized;\n }\n await options.resolveSession();\n try {\n return await run();\n } catch (retryError) {\n throw normalizeMessagingError(retryError, operation);\n }\n }\n }\n\n async function rpc<T>(fn: string, args: Record<string, unknown>, operation: string): Promise<T> {\n const { data, error } = await db().rpc<T>(fn, args);\n if (error !== null) throw normalizeMessagingError(error, operation);\n return data as T;\n }\n\n async function getUsers(\n userIds: readonly UserId[],\n ): Promise<ReadonlyMap<UserId, UserSummary>> {\n const unique = [...new Set(userIds)];\n const found = new Map<UserId, UserSummary>();\n // One failed profile must NOT fail the batch — a conversation row with one\n // unresolvable participant still renders; it just shows that participant by\n // id. Failing the whole list here is how one bad row blanks an inbox.\n const results = await Promise.all(\n unique.map(async (id) => {\n try {\n return await repository.getUser(id);\n } catch {\n return null;\n }\n }),\n );\n results.forEach((summary) => {\n if (summary !== null) found.set(summary.userId, summary);\n });\n return found;\n }\n\n const repository: MessagingRepository = {\n identity,\n\n async listConversations(args = {}) {\n const limit = args.limit ?? 30;\n const operation = \"listConversations\";\n // Ask for one MORE than requested: `hasMore` is then a fact about the\n // database, not a guess from \"we got a full page\" (which is wrong exactly\n // when the last page is exactly full).\n const rows = await withSessionRetry(operation, () =>\n rpc<readonly Record<string, unknown>[] | null>(\n RPCS.conversationsWithDetails,\n {\n p_user_id: identity.userId,\n p_limit: limit + 1,\n p_before_sort_at: args.cursor?.beforeSortAt ?? null,\n p_before_conversation_id: args.cursor?.beforeConversationId ?? null,\n },\n operation,\n ),\n );\n if (rows !== null && !Array.isArray(rows)) {\n throw invalidResponse(operation, `${RPCS.conversationsWithDetails} did not return rows`);\n }\n const projected = (rows ?? []).map((row) =>\n projectConversationSummary(row, identity.userId, org),\n );\n const hasMore = projected.length > limit;\n const items = hasMore ? projected.slice(0, limit) : projected;\n const last = items.at(-1);\n return {\n items,\n hasMore,\n nextCursor:\n hasMore && last !== undefined\n ? { beforeSortAt: last.sortAt, beforeConversationId: last.conversation.id }\n : null,\n };\n },\n\n async getConversation(id) {\n const operation = \"getConversation\";\n const { data, error } = await withSessionRetry(operation, async () =>\n db()\n .from<Record<string, unknown>>(TABLES.conversations)\n .select(CONVERSATION_COLUMNS)\n .eq(\"id\", id)\n .single(),\n );\n if (error !== null) throw normalizeMessagingError(error, operation);\n if (data === null) throw invalidResponse(operation, `conversation ${id} returned no row`);\n const summary = projectConversationSummary(\n {\n conversation_id: data[\"id\"],\n conversation_type: data[\"type\"],\n group_name: data[\"group_name\"],\n group_image_url: data[\"group_image_url\"],\n conversation_created_at: data[\"created_at\"],\n conversation_updated_at: data[\"updated_at\"],\n organization_id: data[\"organization_id\"],\n created_by: data[\"created_by\"],\n metadata: data[\"metadata\"],\n participants: [],\n unread_count: 0,\n },\n identity.userId,\n org,\n );\n return summary.conversation;\n },\n\n async listMessages(conversationId, args = {}) {\n const limit = args.limit ?? 50;\n const operation = \"listMessages\";\n // Newest-first with a UNIQUE tiebreaker, then reversed for display.\n // `or(...)` expresses the keyset predicate PostgREST has no tuple syntax\n // for: (created_at < c) OR (created_at = c AND id < i).\n const rows = await withSessionRetry(operation, async () => {\n let query = db()\n .from<Record<string, unknown>>(TABLES.messages)\n .select(MESSAGE_COLUMNS)\n .eq(\"conversation_id\", conversationId);\n const cursor = args.cursor;\n if (cursor != null) {\n query = query.or(\n `created_at.lt.${cursor.beforeCreatedAt},` +\n `and(created_at.eq.${cursor.beforeCreatedAt},id.lt.${cursor.beforeMessageId})`,\n );\n }\n const { data, error } = await query\n .order(\"created_at\", { ascending: false })\n .order(\"id\", { ascending: false })\n .limit(limit + 1);\n if (error !== null) throw normalizeMessagingError(error, operation);\n return data ?? [];\n });\n\n const hasMore = rows.length > limit;\n const page = hasMore ? rows.slice(0, limit) : rows;\n const oldest = page.at(-1);\n const items = page.map((row) => projectMessage(row, org)).reverse();\n return {\n items,\n hasMore,\n nextCursor:\n hasMore && oldest !== undefined\n ? {\n beforeCreatedAt: String(oldest[\"created_at\"]),\n beforeMessageId: String(oldest[\"id\"]) as MessageId,\n }\n : null,\n };\n },\n\n async messagesSince(conversationId, since) {\n const operation = \"messagesSince\";\n const rows = await withSessionRetry(operation, async () => {\n const { data, error } = await db()\n .from<Record<string, unknown>>(TABLES.messages)\n .select(MESSAGE_COLUMNS)\n .eq(\"conversation_id\", conversationId)\n .gt(\"created_at\", since)\n .order(\"created_at\", { ascending: true })\n .limit(500);\n if (error !== null) throw normalizeMessagingError(error, operation);\n return data ?? [];\n });\n return rows.map((row) => projectMessage(row, org));\n },\n\n async getOrCreateDirectConversation(otherUserId) {\n const operation = \"getOrCreateDirectConversation\";\n const id = await withSessionRetry(operation, () =>\n rpc<string | null>(\n RPCS.getOrCreateDirect,\n {\n // The RPC's own guard requires an `authenticated` caller to pass\n // THEMSELVES as user1; passing them in the other slot is a denial,\n // not a preference.\n p_user1_id: identity.userId,\n p_user2_id: otherUserId,\n p_organization_id: org,\n },\n operation,\n ),\n );\n if (typeof id !== \"string\" || id.length === 0) {\n throw invalidResponse(operation, `${RPCS.getOrCreateDirect} returned no conversation id`);\n }\n return id as ConversationId;\n },\n\n async createGroupConversation({ name, memberIds }) {\n const operation = \"createGroupConversation\";\n const { data, error } = await withSessionRetry(operation, async () =>\n db()\n .from<Record<string, unknown>>(TABLES.conversations)\n .insert({\n type: \"group\",\n group_name: name,\n organization_id: org,\n created_by: identity.userId,\n })\n .select(\"id\")\n .single(),\n );\n if (error !== null) throw normalizeMessagingError(error, operation);\n const conversationId = data?.[\"id\"];\n if (typeof conversationId !== \"string\") {\n throw invalidResponse(operation, \"insert returned no conversation id\");\n }\n const members = [...new Set<UserId>([identity.userId, ...memberIds])];\n const { error: memberError } = await db()\n .from(TABLES.participants)\n .insert(\n members.map((userId) => ({\n conversation_id: conversationId,\n user_id: userId,\n role: userId === identity.userId ? \"owner\" : \"member\",\n organization_id: org,\n created_by: identity.userId,\n })),\n );\n if (memberError !== null) throw normalizeMessagingError(memberError, operation);\n return conversationId as ConversationId;\n },\n\n async insertMessage(draft, clientMessageId) {\n const operation = \"insertMessage\";\n const { data, error } = await db()\n .from<Record<string, unknown>>(TABLES.messages)\n .insert({\n conversation_id: draft.conversationId,\n sender_id: identity.userId,\n organization_id: org,\n created_by: identity.userId,\n content: draft.content,\n message_type: draft.kind ?? \"text\",\n status: \"sent\",\n reply_to_id: draft.replyToId ?? null,\n // THE IDEMPOTENCY KEY. It is what makes a retried send exactly-once\n // and what lets a receiver collapse the optimistic bubble with the\n // confirmed row instead of showing the message twice.\n client_message_id: clientMessageId,\n action_data: draft.action ?? null,\n metadata: {\n ...(draft.metadata ?? {}),\n ...(draft.attachments !== undefined && draft.attachments.length > 0\n ? { attachments: draft.attachments }\n : {}),\n ...(draft.references !== undefined && draft.references.length > 0\n ? { references: draft.references }\n : {}),\n },\n })\n .select(MESSAGE_COLUMNS)\n .single();\n if (error !== null) throw normalizeMessagingError(error, operation);\n if (data === null) throw invalidResponse(operation, \"insert returned no row\");\n return projectMessage(data, org);\n },\n\n async editMessage(id, content) {\n const operation = \"editMessage\";\n const { data, error } = await db()\n .from<Record<string, unknown>>(TABLES.messages)\n .update({\n content,\n edited_at: new Date().toISOString(),\n updated_by: identity.userId,\n })\n .eq(\"id\", id)\n .select(MESSAGE_COLUMNS)\n .single();\n if (error !== null) throw normalizeMessagingError(error, operation);\n if (data === null) throw invalidResponse(operation, \"update returned no row\");\n return projectMessage(data, org);\n },\n\n async deleteMessage(id, forEveryone) {\n const operation = \"deleteMessage\";\n // SOFT delete, always. A hard delete loses the audit row and breaks every\n // reply that points at it.\n const { error } = await db()\n .from(TABLES.messages)\n .update({\n deleted_at: new Date().toISOString(),\n deleted_for_everyone: forEveryone,\n updated_by: identity.userId,\n })\n .eq(\"id\", id);\n if (error !== null) throw normalizeMessagingError(error, operation);\n },\n\n async markRead(conversationId, at) {\n const operation = \"markRead\";\n const { error } = await db()\n .from(TABLES.participants)\n .update({ last_read_at: at ?? new Date().toISOString(), updated_by: identity.userId })\n .eq(\"conversation_id\", conversationId)\n .eq(\"user_id\", identity.userId);\n if (error !== null) throw normalizeMessagingError(error, operation);\n },\n\n async setConversationFlags(conversationId, flags) {\n const operation = \"setConversationFlags\";\n const patch: Record<string, unknown> = { updated_by: identity.userId };\n if (flags.isMuted !== undefined) patch[\"is_muted\"] = flags.isMuted;\n if (flags.isArchived !== undefined) patch[\"is_archived\"] = flags.isArchived;\n const { error } = await db()\n .from(TABLES.participants)\n .update(patch)\n .eq(\"conversation_id\", conversationId)\n .eq(\"user_id\", identity.userId);\n if (error !== null) throw normalizeMessagingError(error, operation);\n },\n\n async addMembers(conversationId, memberIds) {\n const operation = \"addMembers\";\n const { error } = await db()\n .from(TABLES.participants)\n .insert(\n memberIds.map((userId) => ({\n conversation_id: conversationId,\n user_id: userId,\n role: \"member\",\n organization_id: org,\n created_by: identity.userId,\n })),\n );\n if (error !== null) throw normalizeMessagingError(error, operation);\n },\n\n async removeMember(conversationId, memberId) {\n const operation = \"removeMember\";\n const { error } = await db()\n .from(TABLES.participants)\n .update({ deleted_at: new Date().toISOString(), updated_by: identity.userId })\n .eq(\"conversation_id\", conversationId)\n .eq(\"user_id\", memberId);\n if (error !== null) throw normalizeMessagingError(error, operation);\n },\n\n async setMemberRole(conversationId, memberId, role) {\n const operation = \"setMemberRole\";\n const { error } = await db()\n .from(TABLES.participants)\n .update({ role, updated_by: identity.userId })\n .eq(\"conversation_id\", conversationId)\n .eq(\"user_id\", memberId);\n if (error !== null) throw normalizeMessagingError(error, operation);\n },\n\n getUser(userId) {\n const operation = \"getUser\";\n return userCache.read(userId, async () => {\n const rows = await withSessionRetry(operation, () =>\n rpc<readonly Record<string, unknown>[] | null>(\n RPCS.userInfo,\n { p_user_id: userId },\n operation,\n ),\n );\n const row = Array.isArray(rows) ? rows[0] : null;\n return row === undefined || row === null ? null : projectUserSummary(row);\n });\n },\n\n getUsers,\n\n async searchMessages({ query, conversationId = null, limit = 50 }) {\n const operation = \"searchMessages\";\n const trimmed = query.trim();\n if (trimmed.length === 0) return [];\n const rows = await withSessionRetry(operation, async () => {\n let base = db()\n .from<Record<string, unknown>>(TABLES.messages)\n .select(MESSAGE_COLUMNS)\n .is(\"deleted_at\", null);\n if (conversationId !== null) base = base.eq(\"conversation_id\", conversationId);\n // PostgREST pattern matching: `%`, `,`, `(`, `)` and `*` are filter\n // GRAMMAR, not text. They are stripped rather than escaped — a search\n // box must never be able to author a filter.\n const safe = trimmed.replace(/[%,()*]/g, \" \").trim();\n const { data, error } = await base\n .or(`content.ilike.*${safe}*`)\n .order(\"created_at\", { ascending: false })\n .limit(limit);\n if (error !== null) throw normalizeMessagingError(error, operation);\n return data ?? [];\n });\n return rows.map((row) => projectMessage(row, org));\n },\n\n invalidateUser(userId) {\n userCache.invalidate(userId);\n },\n };\n\n return repository;\n}\n","/**\n * THE MESSAGING DOMAIN TYPES.\n *\n * Ported from `matrx-frontend/features/messaging/types.ts` with the coupling\n * seams inverted: no imported host types, no app singletons. The DB-shaped\n * types mirror `communication.dm_*` exactly (snake_case, nullable where the\n * column is nullable) so a row read from PostgREST IS one of these without a\n * mapping step that can silently drop a column.\n *\n * Strictness posture (C22): identity-bearing values are BRANDED, invalid states\n * are unrepresentable (a `sending` message has a `clientMessageId`; a `failed`\n * one carries a reason), and no surface here is `any`. The one honest `unknown`\n * is the JSON boundary — `metadata` and `action_data` are host/DB-generated\n * shapes this package does not own (the data 0.2.1 `Json = unknown` lesson).\n */\n\n/** Nominal id types. A conversation id can never be passed where a user id goes. */\ndeclare const brand: unique symbol;\ntype Brand<T, B extends string> = T & { readonly [brand]: B };\n\nexport type ConversationId = Brand<string, \"ConversationId\">;\nexport type MessageId = Brand<string, \"MessageId\">;\nexport type UserId = Brand<string, \"UserId\">;\nexport type OrganizationId = Brand<string, \"OrganizationId\">;\n/** The client-minted idempotency key that makes a send exactly-once end to end. */\nexport type ClientMessageId = Brand<string, \"ClientMessageId\">;\n\nexport const asConversationId = (value: string): ConversationId => value as ConversationId;\nexport const asMessageId = (value: string): MessageId => value as MessageId;\nexport const asUserId = (value: string): UserId => value as UserId;\nexport const asOrganizationId = (value: string): OrganizationId => value as OrganizationId;\nexport const asClientMessageId = (value: string): ClientMessageId =>\n value as ClientMessageId;\n\nexport type JsonValue = unknown;\nexport type JsonObject = Readonly<Record<string, JsonValue>>;\n\nexport type ConversationType = \"direct\" | \"group\" | \"org\";\nexport type ParticipantRole = \"owner\" | \"admin\" | \"member\";\nexport type MessageKind = \"text\" | \"image\" | \"video\" | \"audio\" | \"file\" | \"system\" | \"action\";\n\n/**\n * The delivery ladder. `sending` and `failed` exist only client-side: they are\n * the outbox's states, and the DB's `status` column never holds them.\n */\nexport type DeliveryState = \"sending\" | \"sent\" | \"delivered\" | \"read\" | \"failed\";\n\nexport interface Conversation {\n readonly id: ConversationId;\n readonly type: ConversationType;\n readonly groupName: string | null;\n readonly groupImageUrl: string | null;\n readonly createdBy: UserId | null;\n readonly organizationId: OrganizationId;\n readonly createdAt: string;\n readonly updatedAt: string;\n readonly metadata: JsonObject;\n}\n\nexport interface Participant {\n readonly conversationId: ConversationId;\n readonly userId: UserId;\n readonly role: ParticipantRole;\n readonly joinedAt: string | null;\n readonly lastReadAt: string | null;\n readonly isMuted: boolean;\n readonly isArchived: boolean;\n}\n\nexport interface UserSummary {\n readonly userId: UserId;\n readonly displayName: string;\n readonly email: string | null;\n readonly avatarUrl: string | null;\n /** True when this participant is an AI agent rather than a person (R4). */\n readonly isAgent: boolean;\n}\n\n/**\n * A structured action carried by a message. THE SEAM other packages extend —\n * `@ai-matrx/meet` puts a call invitation here rather than inventing a message\n * type (D1).\n *\n * `version` is not decoration: a renderer that does not understand a version\n * renders NOTHING rather than guessing, which is what lets a new sender ship\n * before every reader has caught up.\n */\nexport interface MessageAction<TKind extends string = string, TPayload = JsonObject> {\n readonly kind: TKind;\n readonly version: number;\n readonly payload: TPayload;\n}\n\n/**\n * A typed reference to a platform entity, rendered as a live openable card.\n * NO DEAD ENDS: everything a message names must open, so a reference always\n * carries enough to resolve — never a bare label.\n */\nexport interface MatrxReference {\n readonly entityType: string;\n readonly entityId: string;\n readonly label: string;\n readonly href?: string | undefined;\n}\n\nexport interface Attachment {\n /** A DURABLE file ref, never a signed URL. Signed URLs expire; identities do not. */\n readonly fileId: string;\n readonly fileName: string;\n readonly mimeType: string | null;\n readonly sizeBytes: number | null;\n readonly width: number | null;\n readonly height: number | null;\n}\n\nexport interface Message {\n readonly id: MessageId;\n readonly conversationId: ConversationId;\n readonly senderId: UserId;\n readonly organizationId: OrganizationId;\n readonly content: string;\n readonly kind: MessageKind;\n readonly deliveryState: DeliveryState;\n readonly replyToId: MessageId | null;\n readonly clientMessageId: ClientMessageId | null;\n readonly createdAt: string;\n readonly editedAt: string | null;\n readonly deletedAt: string | null;\n readonly deletedForEveryone: boolean;\n readonly action: MessageAction | null;\n readonly attachments: readonly Attachment[];\n readonly references: readonly MatrxReference[];\n readonly metadata: JsonObject;\n /** Set only while this message is in the outbox and its last send failed. */\n readonly failureReason?: string | undefined;\n}\n\nexport interface ConversationSummary {\n readonly conversation: Conversation;\n readonly participants: readonly UserSummary[];\n readonly lastMessageContent: string | null;\n readonly lastMessageSenderId: UserId | null;\n readonly lastMessageAt: string | null;\n readonly unreadCount: number;\n readonly isMuted: boolean;\n readonly isArchived: boolean;\n /** Resolved for display: the group name, or the other participant's name. */\n readonly displayName: string;\n readonly displayImageUrl: string | null;\n /** The keyset sort value. Pagination cursors are built from this + `id`. */\n readonly sortAt: string;\n}\n\n/**\n * THE PAGINATION CURSOR. Stable ordering is terminated by a UNIQUE column\n * (R5 scale honesty): ordering by a timestamp alone silently duplicates or\n * skips rows whenever two rows share a millisecond, which in a large org is\n * every page.\n */\nexport interface ConversationCursor {\n readonly beforeSortAt: string;\n readonly beforeConversationId: ConversationId;\n}\n\nexport interface MessageCursor {\n readonly beforeCreatedAt: string;\n readonly beforeMessageId: MessageId;\n}\n\nexport interface Page<TItem, TCursor> {\n readonly items: readonly TItem[];\n readonly nextCursor: TCursor | null;\n readonly hasMore: boolean;\n}\n\n/** What the host injects. Identity ONLY — every hard part is in the package. */\nexport interface MessagingIdentity {\n readonly userId: UserId;\n readonly organizationId: OrganizationId;\n}\n\nexport interface DraftMessage {\n readonly conversationId: ConversationId;\n readonly content: string;\n readonly kind?: MessageKind | undefined;\n readonly replyToId?: MessageId | undefined;\n readonly action?: MessageAction | undefined;\n readonly attachments?: readonly Attachment[] | undefined;\n readonly references?: readonly MatrxReference[] | undefined;\n readonly metadata?: JsonObject | undefined;\n}\n"],"mappings":"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","names":["import_realtime","import_realtime","held"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/core/actions.ts","../src/core/actor.ts","../src/core/ai.ts","../src/core/errors.ts","../src/core/references.ts","../src/core/cache.ts","../src/core/channels.ts","../src/core/slot.ts","../src/core/engine.ts","../src/core/outbox.ts","../src/core/projection.ts","../src/core/store.ts","../src/core/format.ts","../src/core/repository.ts","../src/core/types.ts"],"sourcesContent":["/**\n * `@ai-matrx/messaging` — enterprise, AI-native in-app messaging for Matrx.\n *\n * This entry is FRAMEWORK-FREE: safe to import from Redux middleware, a plain\n * service module, a worker, or a node test. React lives in\n * `@ai-matrx/messaging/react`.\n *\n * The doctrine — how a message can arrive four ways and still render once, why\n * reconnecting means re-reading, and what the host is and is not allowed to\n * wire — is in the README. Read it before changing anything here.\n */\n\nexport { createActionRegistry } from \"./core/actions\";\nexport type {\n ActionChoice,\n ActionContext,\n ActionHandler,\n ActionOutcome,\n ActionReceipt,\n ActionRegistry,\n} from \"./core/actions\";\n\nexport { resolveActor } from \"./core/actor\";\nexport type { ActorPresentation } from \"./core/actor\";\n\nexport { createMessagingAi } from \"./core/ai\";\nexport type {\n AiCapability,\n AiResult,\n MessagingAgents,\n MessagingAi,\n MessagingAiOptions,\n} from \"./core/ai\";\n\nexport { createReadCache } from \"./core/cache\";\nexport type { ReadCache, ReadCacheOptions } from \"./core/cache\";\n\nexport { conversationTopic, inboxTopic, MESSAGING_EVENTS } from \"./core/channels\";\n\nexport { createMessagingEngine, messagingClientId } from \"./core/engine\";\nexport type {\n EngineDiagnostic,\n MessagingEngine,\n MessagingEngineOptions,\n} from \"./core/engine\";\n\nexport { invalidResponse, MessagingError, normalizeMessagingError } from \"./core/errors\";\nexport type { MessagingErrorCode } from \"./core/errors\";\n\nexport {\n avatarPaletteIndex,\n formatConversationTime,\n formatDateSeparator,\n formatLastSeen,\n formatMessageTime,\n formatTypists,\n getInitials,\n groupMessages,\n isSameDay,\n participantNames,\n} from \"./core/format\";\nexport type { MessageGroup } from \"./core/format\";\n\nexport {\n createMemoryOutboxStorage,\n createOutbox,\n createWebOutboxStorage,\n} from \"./core/outbox\";\nexport type { Outbox, OutboxEntry, OutboxOptions, OutboxStorage } from \"./core/outbox\";\n\nexport {\n projectConversationSummary,\n projectMessage,\n projectMessageAction,\n projectParticipantRole,\n projectUserSummary,\n} from \"./core/projection\";\n\nexport {\n composeFence,\n extractReferences,\n splitText,\n summarizeText,\n} from \"./core/references\";\nexport type { TextSegment } from \"./core/references\";\n\nexport {\n createMessagingRepository,\n MESSAGING_SCHEMA,\n RPCS,\n TABLES,\n} from \"./core/repository\";\nexport type {\n MessagingRepository,\n RepositoryOptions,\n SessionResolver,\n} from \"./core/repository\";\n\nexport { createMessagingStore, optimisticMessage } from \"./core/store\";\nexport type {\n ConversationThread,\n MessagingSnapshot,\n MessagingStore,\n} from \"./core/store\";\n\nexport type {\n PostgrestFilterLike,\n PostgrestLikeResponse,\n PostgrestTableLike,\n SchemaLike,\n SupabaseLike,\n} from \"./core/supabase-shape\";\n\nexport {\n asClientMessageId,\n asConversationId,\n asMessageId,\n asOrganizationId,\n asUserId,\n} from \"./core/types\";\nexport type {\n Attachment,\n ClientMessageId,\n Conversation,\n ConversationCursor,\n ConversationId,\n ConversationSummary,\n ConversationType,\n DeliveryState,\n DraftMessage,\n JsonObject,\n JsonValue,\n MatrxReference,\n Message,\n MessageAction,\n MessageCursor,\n MessageId,\n MessageKind,\n MessagingIdentity,\n OrganizationId,\n Page,\n Participant,\n ParticipantRole,\n UserId,\n UserSummary,\n} from \"./core/types\";\n","/**\n * ACTIONABLE MESSAGES — a message that carries something you can DO.\n *\n * A registry, never a `switch`. Two properties come from that choice, and both\n * are load-bearing:\n *\n * - **Forward compatibility.** An unknown `kind` — or a known kind at a version\n * this build does not understand — renders NOTHING and executes nothing. A\n * `switch` with a `default: throw` means the day a new sender ships, every\n * older reader's thread breaks. Silently rendering an unknown payload with a\n * generic button is worse: it offers an action nobody can honor.\n * - **Extensibility across packages.** `@ai-matrx/meet` registers its call\n * invitation here rather than this package learning about meetings (D1).\n *\n * 🚨 **IDEMPOTENCE IS ENFORCED HERE, NOT PROMISED BY HANDLERS.** Two tabs, a\n * double-click, and a retried tap must apply an action ONCE. The registry\n * de-duplicates by `(kind, messageId, actorId)`: a second execution while the\n * first is in flight AWAITS it and returns the same receipt, and a settled\n * action returns its stored receipt without touching the server.\n *\n * 🚨 **THE PAYLOAD IS NOT AUTHORIZATION.** A handler must re-resolve the\n * durable, caller-authorized request row server-side before it writes. The\n * message's `action_data` says what was ASKED, never what is ALLOWED — trusting\n * it turns any message into a privilege grant. Handlers declare this by\n * construction: they receive the payload and the actor, and must go to the\n * database themselves.\n */\n\nimport type { JsonObject, MessageAction, MessageId, UserId } from \"./types\";\n\nexport type ActionOutcome = \"applied\" | \"already\" | \"declined\" | \"unavailable\";\n\nexport interface ActionReceipt {\n readonly kind: string;\n readonly messageId: MessageId;\n readonly actorId: UserId;\n readonly outcome: ActionOutcome;\n readonly label: string;\n readonly settledAt: string;\n readonly detail?: string | undefined;\n}\n\nexport interface ActionContext {\n readonly messageId: MessageId;\n readonly actorId: UserId;\n readonly organizationId: string;\n /** Which of the choices the user picked, e.g. `approve` / `decline`. */\n readonly choice: string;\n}\n\nexport interface ActionChoice {\n readonly id: string;\n readonly label: string;\n readonly tone: \"primary\" | \"neutral\" | \"danger\";\n}\n\nexport interface ActionHandler<TPayload = JsonObject> {\n readonly kind: string;\n /** Versions this build understands. An unlisted version renders nothing. */\n readonly versions: readonly number[];\n /** What the chips say. Return `[]` to render the action as read-only. */\n choices: (payload: TPayload) => readonly ActionChoice[];\n /** One-line summary for the inbox preview and notifications. */\n summarize?: (payload: TPayload) => string;\n /**\n * Do the thing. MUST re-resolve the authorizing row server-side; the payload\n * is the request, not the permission. Returning `already` is how a handler\n * reports that the server had settled it — the registry treats it as success.\n */\n execute: (payload: TPayload, context: ActionContext) => Promise<ActionReceipt>;\n}\n\nexport interface ActionRegistry {\n register<TPayload = JsonObject>(handler: ActionHandler<TPayload>): void;\n /** The handler for an action, or null when this build cannot honor it. */\n resolve(action: MessageAction): ActionHandler | null;\n choicesFor(action: MessageAction): readonly ActionChoice[];\n summarize(action: MessageAction): string | null;\n /** Idempotent execution. Concurrent callers share one in-flight result. */\n execute(action: MessageAction, context: ActionContext): Promise<ActionReceipt>;\n /** The stored receipt for a settled action, if there is one. */\n receiptFor(kind: string, messageId: MessageId, actorId: UserId): ActionReceipt | null;\n /** Record a receipt observed from another client's broadcast. */\n observeReceipt(receipt: ActionReceipt): void;\n known(): readonly string[];\n}\n\nfunction receiptKey(kind: string, messageId: MessageId, actorId: UserId): string {\n return `${kind}::${messageId}::${actorId}`;\n}\n\nexport function createActionRegistry(): ActionRegistry {\n const handlers = new Map<string, ActionHandler>();\n const receipts = new Map<string, ActionReceipt>();\n const inFlight = new Map<string, Promise<ActionReceipt>>();\n\n const registry: ActionRegistry = {\n register(handler) {\n handlers.set(handler.kind, handler as unknown as ActionHandler);\n },\n resolve(action) {\n const handler = handlers.get(action.kind);\n if (handler === undefined) return null;\n // A version this build does not list is as unknown as an unknown kind.\n return handler.versions.includes(action.version) ? handler : null;\n },\n choicesFor(action) {\n const handler = registry.resolve(action);\n return handler === null ? [] : handler.choices(action.payload as JsonObject);\n },\n summarize(action) {\n const handler = registry.resolve(action);\n if (handler?.summarize === undefined) return null;\n return handler.summarize(action.payload as JsonObject);\n },\n execute(action, context) {\n const key = receiptKey(action.kind, context.messageId, context.actorId);\n\n const settled = receipts.get(key);\n if (settled !== undefined) return Promise.resolve(settled);\n\n const running = inFlight.get(key);\n // THE DOUBLE-CLICK GUARD. The second caller awaits the first rather than\n // starting a second write.\n if (running !== undefined) return running;\n\n const handler = registry.resolve(action);\n if (handler === null) {\n const receipt: ActionReceipt = {\n kind: action.kind,\n messageId: context.messageId,\n actorId: context.actorId,\n outcome: \"unavailable\",\n label: \"Not available in this app version\",\n settledAt: new Date().toISOString(),\n detail:\n `No handler registered for \"${action.kind}\" v${action.version}. Update the app, ` +\n `or register a handler on <MessagingProvider actions={...}>.`,\n };\n return Promise.resolve(receipt);\n }\n\n const started = handler\n .execute(action.payload as JsonObject, context)\n .then((receipt) => {\n // `applied` and `already` are both terminal: the server has the\n // effect either way, so a later click must not re-attempt.\n if (receipt.outcome === \"applied\" || receipt.outcome === \"already\") {\n receipts.set(key, receipt);\n }\n return receipt;\n })\n .finally(() => {\n inFlight.delete(key);\n });\n\n inFlight.set(key, started);\n return started;\n },\n receiptFor(kind, messageId, actorId) {\n return receipts.get(receiptKey(kind, messageId, actorId)) ?? null;\n },\n observeReceipt(receipt) {\n if (receipt.outcome === \"applied\" || receipt.outcome === \"already\") {\n receipts.set(receiptKey(receipt.kind, receipt.messageId, receipt.actorId), receipt);\n }\n },\n known() {\n return [...handlers.keys()];\n },\n };\n\n return registry;\n}\n","/**\n * EFFECTIVE-ACTOR RESOLUTION — who a message is FROM, versus who wrote the row.\n *\n * `sender_id` is the AUDIT PRINCIPAL: the session the write happened under. It\n * must never change, and it must never be what the UI renders when an agent\n * acted through a human's session. Otherwise an automated message wears a\n * colleague's face and name — the failure this module exists to prevent (R4\n * puts agents in conversations, which makes it the normal case, not an edge).\n *\n * The effective actor is declared in the message's own metadata by whoever sent\n * it. Nothing here infers an agent from heuristics on the content.\n */\n\nimport type { JsonObject, Message, UserSummary } from \"./types\";\n\nexport interface ActorPresentation {\n readonly displayName: string;\n readonly avatarUrl: string | null;\n /** True when the message was authored by an agent, not the human principal. */\n readonly isAgent: boolean;\n /** Set only for an agent: the human whose session it acted under. */\n readonly onBehalfOfName: string | null;\n /** The audit principal. Always the row's `sender_id`. Never rewritten. */\n readonly principalUserId: Message[\"senderId\"];\n}\n\ninterface ActorHint {\n readonly agentId?: string;\n readonly agentName?: string;\n readonly agentAvatarUrl?: string;\n}\n\nfunction readActorHint(metadata: JsonObject): ActorHint | null {\n const raw = (metadata as Record<string, unknown>)[\"actor\"];\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) return null;\n const record = raw as Record<string, unknown>;\n const agentId = record[\"agentId\"] ?? record[\"agent_id\"];\n if (typeof agentId !== \"string\" || agentId.length === 0) return null;\n const name = record[\"agentName\"] ?? record[\"agent_name\"];\n const avatar = record[\"agentAvatarUrl\"] ?? record[\"agent_avatar_url\"];\n return {\n agentId,\n ...(typeof name === \"string\" && name.length > 0 ? { agentName: name } : {}),\n ...(typeof avatar === \"string\" && avatar.length > 0 ? { agentAvatarUrl: avatar } : {}),\n };\n}\n\nexport function resolveActor(\n message: Message,\n sender: UserSummary | null,\n): ActorPresentation {\n const hint = readActorHint(message.metadata);\n const humanName = sender?.displayName ?? message.senderId;\n\n if (hint !== null) {\n return {\n // An agent NEVER inherits the human's name or avatar. When the agent did\n // not name itself, it is labeled generically — an honest \"Agent\" beats a\n // colleague's face on a message they did not write.\n displayName: hint.agentName ?? \"Agent\",\n avatarUrl: hint.agentAvatarUrl ?? null,\n isAgent: true,\n onBehalfOfName: humanName,\n principalUserId: message.senderId,\n };\n }\n\n if (sender?.isAgent === true) {\n return {\n displayName: sender.displayName,\n avatarUrl: sender.avatarUrl,\n isAgent: true,\n onBehalfOfName: null,\n principalUserId: message.senderId,\n };\n }\n\n return {\n displayName: humanName,\n avatarUrl: sender?.avatarUrl ?? null,\n isAgent: false,\n onBehalfOfName: null,\n principalUserId: message.senderId,\n };\n}\n","/**\n * AI-NATIVE, OUT OF THE BOX (R4).\n *\n * Four conversation intelligences — catch me up, summarize, extract action\n * items, draft a reply — plus agents as participants. All of it executes on the\n * platform's agent system through `@ai-matrx/agents`; this package never talks\n * to a model, never holds a prompt, and never carries an agent definition.\n *\n * 🚨 **THE USER-INPUT LAW.** `user_input` is what a HUMAN typed. Machine\n * content — a transcript, a participant roster, a cutoff timestamp — travels as\n * NAMED VARIABLES. This is not style: the server treats `user_input` as the\n * turn's human utterance (it is stored as such, shown as such, and shapes the\n * agent's framing), so smuggling a transcript through it silently corrupts\n * every conversation it touches. Every call below sends `variables` and either\n * a short human-shaped `user_input` or none.\n *\n * 🚨 **AGENT DEFINITIONS LIVE IN THE DATABASE.** The package takes agent IDs as\n * injected identity. When an id is not configured the capability reports\n * `unavailable` WITH the remedy — it never silently no-ops, and the UI hides\n * the action rather than offering a dead button (no dead ends).\n */\n\nimport {\n newEphemeralConversationStart,\n runAgentToCompletion,\n type MatrxJsonObject,\n type MatrxTransport,\n} from \"@ai-matrx/agents/matrx\";\nimport { MessagingError } from \"./errors\";\nimport { summarizeText } from \"./references\";\nimport type { ConversationId, Message, UserSummary } from \"./types\";\n\n/**\n * Which platform agent backs each capability. Every field is a DATABASE row id\n * supplied by the host; an omitted one disables exactly that capability.\n */\nexport interface MessagingAgents {\n readonly catchUp?: string | undefined;\n readonly summarize?: string | undefined;\n readonly actionItems?: string | undefined;\n readonly draftReply?: string | undefined;\n}\n\nexport interface MessagingAiOptions {\n transport: MatrxTransport;\n organizationId: string;\n agents: MessagingAgents;\n /** Stable source slugs so server-side analytics can see where a run came from. */\n sourceApp?: string | undefined;\n sourceFeature?: string | undefined;\n /** Cap on how much transcript is sent. Default 200 messages. */\n maxTranscriptMessages?: number | undefined;\n}\n\nexport type AiCapability = keyof MessagingAgents;\n\nexport interface AiResult {\n readonly capability: AiCapability;\n readonly text: string;\n readonly conversationId: string | null;\n}\n\n/** A message reduced to what an agent needs. Never the raw DB row. */\ninterface TranscriptEntry {\n readonly at: string;\n readonly author: string;\n readonly text: string;\n}\n\nexport interface MessagingAi {\n /** Which capabilities this host actually configured. Drives what the UI offers. */\n available(): readonly AiCapability[];\n isAvailable(capability: AiCapability): boolean;\n catchMeUp(args: {\n conversationId: ConversationId;\n messages: readonly Message[];\n participants: readonly UserSummary[];\n since: string | null;\n signal?: AbortSignal;\n }): Promise<AiResult>;\n summarize(args: {\n conversationId: ConversationId;\n messages: readonly Message[];\n participants: readonly UserSummary[];\n signal?: AbortSignal;\n }): Promise<AiResult>;\n extractActionItems(args: {\n conversationId: ConversationId;\n messages: readonly Message[];\n participants: readonly UserSummary[];\n signal?: AbortSignal;\n }): Promise<AiResult>;\n draftReply(args: {\n conversationId: ConversationId;\n messages: readonly Message[];\n participants: readonly UserSummary[];\n /** What the human asked for, if anything — this IS a human utterance. */\n instruction?: string | undefined;\n signal?: AbortSignal;\n }): Promise<AiResult>;\n}\n\nfunction nameOf(\n participants: readonly UserSummary[],\n senderId: Message[\"senderId\"],\n): string {\n return participants.find((p) => p.userId === senderId)?.displayName ?? senderId;\n}\n\nfunction buildTranscript(\n messages: readonly Message[],\n participants: readonly UserSummary[],\n limit: number,\n since: string | null,\n): readonly TranscriptEntry[] {\n const relevant = messages.filter((message) => {\n if (message.deletedAt !== null) return false;\n if (since === null) return true;\n return message.createdAt > since;\n });\n // Keep the MOST RECENT window when the conversation is long: the tail is what\n // \"what did I miss\" is about, and silently truncating the head is the honest\n // cut. The cap itself is reported to the agent so it can say so.\n const windowed = relevant.slice(-limit);\n return windowed.map((message) => ({\n at: message.createdAt,\n author: nameOf(participants, message.senderId),\n // The transcript is TEXT. A reference fence becomes its label, never JSON —\n // the same collapse the inbox preview uses.\n text: summarizeText(message.content, 2_000),\n }));\n}\n\nexport function createMessagingAi(options: MessagingAiOptions): MessagingAi {\n const limit = options.maxTranscriptMessages ?? 200;\n\n function agentFor(capability: AiCapability): string {\n const agentId = options.agents[capability];\n if (typeof agentId !== \"string\" || agentId.length === 0) {\n throw new MessagingError(\n \"misconfigured\",\n `Messaging AI capability \"${capability}\" has no agent configured`,\n `Pass agents={{ ${capability}: \"<agent-id>\" }} to <MessagingProvider>. Agent ` +\n `definitions live in the database, never in this package — the id is the only ` +\n `part a host injects. Until then the UI hides this action rather than ` +\n `offering a button that cannot work.`,\n );\n }\n return agentId;\n }\n\n async function run(\n capability: AiCapability,\n variables: MatrxJsonObject,\n userInput: string | null,\n signal: AbortSignal | undefined,\n ): Promise<AiResult> {\n const agentId = agentFor(capability);\n const completed = await runAgentToCompletion(\n options.transport,\n agentId,\n {\n ...newEphemeralConversationStart(),\n organization_id: options.organizationId,\n source_app: options.sourceApp ?? \"ai-matrx\",\n source_feature: options.sourceFeature ?? `messaging.${capability}`,\n initiation: \"user\",\n // THE USER-INPUT LAW: structured content is NEVER here.\n ...(userInput !== null ? { user_input: userInput } : {}),\n variables,\n },\n signal !== undefined ? { signal } : {},\n );\n return {\n capability,\n text: completed.text.trim(),\n conversationId: completed.conversationId,\n };\n }\n\n function variablesFor(args: {\n conversationId: ConversationId;\n messages: readonly Message[];\n participants: readonly UserSummary[];\n since?: string | null;\n }): MatrxJsonObject {\n const transcript = buildTranscript(\n args.messages,\n args.participants,\n limit,\n args.since ?? null,\n );\n return {\n conversation_id: args.conversationId,\n participants: args.participants.map((participant) => ({\n user_id: participant.userId,\n display_name: participant.displayName,\n is_agent: participant.isAgent,\n })),\n transcript: transcript.map((entry) => ({\n at: entry.at,\n author: entry.author,\n text: entry.text,\n })),\n transcript_message_count: transcript.length,\n // Honest about the cut, so an agent can say \"showing the last 200\".\n transcript_truncated: args.messages.length > transcript.length,\n ...(args.since != null ? { unread_since: args.since } : {}),\n };\n }\n\n return {\n available: () =>\n ([\"catchUp\", \"summarize\", \"actionItems\", \"draftReply\"] as const).filter(\n (capability) => {\n const id = options.agents[capability];\n return typeof id === \"string\" && id.length > 0;\n },\n ),\n isAvailable(capability) {\n const id = options.agents[capability];\n return typeof id === \"string\" && id.length > 0;\n },\n catchMeUp: (args) =>\n run(\"catchUp\", variablesFor(args), null, args.signal),\n summarize: (args) => run(\"summarize\", variablesFor(args), null, args.signal),\n extractActionItems: (args) =>\n run(\"actionItems\", variablesFor(args), null, args.signal),\n draftReply: (args) =>\n run(\n \"draftReply\",\n variablesFor(args),\n // The ONE genuine human utterance in this module: what the user asked\n // the drafter for. Everything else rode `variables`.\n args.instruction !== undefined && args.instruction.trim().length > 0\n ? args.instruction.trim()\n : null,\n args.signal,\n ),\n };\n}\n","/**\n * ONE error classification for the whole package (C22).\n *\n * The banned alternative is the host catching a PostgREST error and deciding\n * for itself whether it was an auth blip, a permission denial, or a real bug —\n * which is how the same three-branch `if` ends up copy-pasted into every\n * consumer and drifts. Everything this package throws is a `MessagingError`\n * with a stable `code`, and the two that hosts genuinely treat differently\n * (`session-unavailable`, `forbidden`) say so in the type.\n *\n * The incident behind `session-unavailable`: a DM reader mounted before the\n * access token existed, every RPC came back `42501`, and the app captured 909\n * errors in 0.6 seconds. A missing session is a NORMAL lifecycle moment — it\n * warns and retries; it is never a red error.\n */\n\nexport type MessagingErrorCode =\n | \"session-unavailable\"\n | \"forbidden\"\n | \"not-found\"\n | \"conflict\"\n | \"transport\"\n | \"invalid-response\"\n | \"misconfigured\"\n | \"unknown\";\n\nexport class MessagingError extends Error {\n readonly code: MessagingErrorCode;\n /** The remedy. Nothing fails silently, and nothing fails without saying what to do. */\n readonly remedy: string;\n override readonly cause?: unknown;\n\n constructor(\n code: MessagingErrorCode,\n message: string,\n remedy: string,\n cause?: unknown,\n ) {\n super(message);\n this.name = \"MessagingError\";\n this.code = code;\n this.remedy = remedy;\n if (cause !== undefined) this.cause = cause;\n }\n\n /** True when retrying after the host re-establishes a session is the fix. */\n get isRetryable(): boolean {\n return this.code === \"session-unavailable\" || this.code === \"transport\";\n }\n}\n\ninterface PostgrestErrorLike {\n message: string;\n code?: string | undefined;\n details?: string | null | undefined;\n}\n\n/** Postgres/PostgREST codes that mean \"you are not allowed\", not \"you are broken\". */\nconst FORBIDDEN_CODES = new Set([\"42501\", \"PGRST301\", \"PGRST302\"]);\nconst NOT_FOUND_CODES = new Set([\"PGRST116\", \"PGRST205\"]);\nconst CONFLICT_CODES = new Set([\"23505\"]);\n\nconst SESSION_MARKERS = [\n \"auth session missing\",\n \"jwt expired\",\n \"no api key found\",\n \"refresh_token_not_found\",\n \"invalid claim: missing sub claim\",\n];\n\n/**\n * THE ONE PLACE a raw PostgREST/transport error becomes a typed one. Called at\n * every boundary in `repository.ts`; nothing else in the package inspects a raw\n * error, and no consumer should ever have to.\n */\nexport function normalizeMessagingError(\n error: PostgrestErrorLike | Error | unknown,\n operation: string,\n): MessagingError {\n if (error instanceof MessagingError) return error;\n\n const raw = error as Partial<PostgrestErrorLike> & { name?: string };\n const message = typeof raw?.message === \"string\" ? raw.message : String(error);\n const code = typeof raw?.code === \"string\" ? raw.code : undefined;\n const lowered = message.toLowerCase();\n\n if (\n raw?.name === \"SessionUnavailableError\" ||\n SESSION_MARKERS.some((marker) => lowered.includes(marker))\n ) {\n return new MessagingError(\n \"session-unavailable\",\n `${operation}: no Supabase session available (${message})`,\n \"Normal during sign-in and token refresh. The package retries once after the \" +\n \"host's session source resolves; log it as a warning, never as an error.\",\n error,\n );\n }\n if (code !== undefined && FORBIDDEN_CODES.has(code)) {\n return new MessagingError(\n \"forbidden\",\n `${operation}: denied by the database (${code}: ${message})`,\n \"Authorization is RLS + auth-checked RPCs (R5). Fix the policy or the caller's \" +\n \"membership — never work around it with a service-role client in a browser.\",\n error,\n );\n }\n if (code !== undefined && NOT_FOUND_CODES.has(code)) {\n return new MessagingError(\n \"not-found\",\n `${operation}: not found (${code}: ${message})`,\n \"The row is gone, or RLS hides it from this user. Re-read the conversation list.\",\n error,\n );\n }\n if (code !== undefined && CONFLICT_CODES.has(code)) {\n return new MessagingError(\n \"conflict\",\n `${operation}: unique violation (${code}: ${message})`,\n \"A concurrent writer won. For messages this is the client_message_id idempotency \" +\n \"key doing its job — re-read the row rather than retrying the insert.\",\n error,\n );\n }\n return new MessagingError(\n \"transport\",\n `${operation}: ${message}`,\n \"Transient transport failure. The package retries reads; a write is surfaced to the \" +\n \"outbox so the typed message is never lost.\",\n error,\n );\n}\n\n/** A response whose SHAPE is wrong — the ingress boundary, not the wire. */\nexport function invalidResponse(operation: string, detail: string): MessagingError {\n return new MessagingError(\n \"invalid-response\",\n `${operation}: ${detail}`,\n \"The database returned a shape this package does not accept. Rendering it would put \" +\n \"a lie on the screen, so it is refused here. Fix the RPC's return contract.\",\n );\n}\n","/**\n * MATRX REFERENCES — a message can name a platform entity, and everything it\n * names must OPEN. No dead ends.\n *\n * Two transports, both supported because both already exist in the wild:\n *\n * 1. **Structured**, in `metadata.references` — what this package writes.\n * 2. **Fenced**, as a ```matrx block in the content — the platform's\n * reference-fence protocol, which arrives from senders outside messaging.\n *\n * Two failures this module exists to prevent:\n *\n * - **A raw fence rendered as a code block.** The reader sees JSON. `splitText`\n * is what lets the renderer draw a card instead.\n * - **A fence leaking into a preview.** The inbox row and the desktop\n * notification are plain text; pasting the JSON there is the same defect one\n * layer down. `summarizeText` is the one collapse, used by both.\n */\n\nimport type { MatrxReference } from \"./types\";\n\nconst FENCE = /```matrx\\s*\\n([\\s\\S]*?)\\n?```/g;\n\nexport type TextSegment =\n | { readonly type: \"text\"; readonly value: string }\n | { readonly type: \"reference\"; readonly reference: MatrxReference }\n /**\n * A ```matrx fence this package could not resolve into references.\n *\n * It is NOT dropped. Platforms carry richer fence dialects than this\n * package's own array shape — Matrx's is a `__kind` directive shell whose\n * items are typed per noun, resolvable only by the app's kind registry — and\n * a fence silently deleted on render is a message that lost a paragraph\n * between the sender and the reader. The host draws it (`renderFence`), or\n * the package draws an honest inert card. Never a code block of JSON.\n */\n | { readonly type: \"fence\"; readonly body: string };\n\nfunction parseFenceBody(body: string): readonly MatrxReference[] {\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n return [];\n }\n const entries = Array.isArray(parsed) ? parsed : [parsed];\n const references: MatrxReference[] = [];\n for (const entry of entries) {\n if (typeof entry !== \"object\" || entry === null) continue;\n const record = entry as Record<string, unknown>;\n const entityType = record[\"entityType\"] ?? record[\"entity_type\"] ?? record[\"type\"];\n const entityId = record[\"entityId\"] ?? record[\"entity_id\"] ?? record[\"id\"];\n if (typeof entityType !== \"string\" || typeof entityId !== \"string\") continue;\n if (entityType.length === 0 || entityId.length === 0) continue;\n const label = record[\"label\"] ?? record[\"title\"] ?? record[\"name\"];\n const href = record[\"href\"] ?? record[\"url\"];\n references.push({\n entityType,\n entityId,\n label: typeof label === \"string\" && label.length > 0 ? label : entityId,\n ...(typeof href === \"string\" && href.length > 0 ? { href } : {}),\n });\n }\n return references;\n}\n\n/** Every reference a message carries, from both transports, de-duplicated. */\nexport function extractReferences(\n content: string,\n structured: readonly MatrxReference[] = [],\n): readonly MatrxReference[] {\n const seen = new Map<string, MatrxReference>();\n const add = (reference: MatrxReference): void => {\n seen.set(`${reference.entityType}:${reference.entityId}`, reference);\n };\n structured.forEach(add);\n for (const match of content.matchAll(FENCE)) {\n parseFenceBody(match[1] ?? \"\").forEach(add);\n }\n return [...seen.values()];\n}\n\n/**\n * Split content into renderable segments. The renderer draws text for `text`\n * and a live card for `reference`; a fence therefore CANNOT reach the screen as\n * a code block.\n */\nexport function splitText(content: string): readonly TextSegment[] {\n const segments: TextSegment[] = [];\n let cursor = 0;\n for (const match of content.matchAll(FENCE)) {\n const start = match.index ?? 0;\n if (start > cursor) {\n segments.push({ type: \"text\", value: content.slice(cursor, start) });\n }\n const body = match[1] ?? \"\";\n const references = parseFenceBody(body);\n if (references.length === 0) {\n segments.push({ type: \"fence\", body });\n } else {\n references.forEach((reference) => {\n segments.push({ type: \"reference\", reference });\n });\n }\n cursor = start + match[0].length;\n }\n if (cursor < content.length) {\n segments.push({ type: \"text\", value: content.slice(cursor) });\n }\n return segments.filter(\n (segment) => segment.type !== \"text\" || segment.value.trim().length > 0,\n );\n}\n\n/**\n * ONE plain-text collapse, used by the inbox preview AND the notification body.\n * A fence becomes its human label; it never reaches either as raw JSON.\n */\nexport function summarizeText(content: string, maxLength = 140): string {\n const parts = splitText(content).map((segment) =>\n segment.type === \"text\"\n ? segment.value\n : segment.type === \"reference\"\n ? segment.reference.label\n : // A fence we cannot read still SAYS something. Collapsing it to\n // nothing is how a reference-only message becomes an inbox row that\n // reads \"No messages yet\" — a screen telling a lie.\n \"Reference\",\n );\n const flattened = parts.join(\" \").replace(/\\s+/g, \" \").trim();\n if (flattened.length <= maxLength) return flattened;\n return `${flattened.slice(0, maxLength - 1).trimEnd()}…`;\n}\n\n/** Serialize picked references into a fence the platform's other readers accept. */\nexport function composeFence(references: readonly MatrxReference[]): string {\n if (references.length === 0) return \"\";\n return `\\`\\`\\`matrx\\n${JSON.stringify(references, null, 2)}\\n\\`\\`\\``;\n}\n","/**\n * IN-FLIGHT DEDUP + TTL CACHE — the cure for the request storm.\n *\n * The incident (matrx-frontend, 2026-08-21): every conversation row rendered\n * called the profile lookup for its participants, every one of those calls hit\n * the network independently, the transport wobbled, and the app captured\n * **909 errors in 0.6 seconds**. The bug was never the transport — it was that\n * N identical concurrent reads were N requests.\n *\n * Two guarantees, and both are load-bearing:\n *\n * 1. **Concurrent callers for the same key share ONE promise.** The second\n * caller does not start a second request; it awaits the first.\n * 2. **A failure does not poison the cache.** The rejected promise is evicted\n * the moment it settles, so the next caller retries instead of inheriting a\n * permanent error. (The naive version caches the rejection and the surface\n * stays broken until reload — the reason this has a named test.)\n *\n * A resolved value is held for `ttlMs` and then re-read. This is a READ cache\n * only: writes go straight through and invalidate.\n */\n\nexport interface ReadCacheOptions {\n /** How long a resolved value is served without a re-read. */\n ttlMs: number;\n /** Injected clock — the tests must not sleep. */\n now?: () => number;\n /** Bound on retained entries; the oldest is evicted first. */\n maxEntries?: number;\n}\n\ninterface Entry<T> {\n value: T;\n expiresAt: number;\n}\n\nexport interface ReadCache<T> {\n /** Read through the cache. Identical concurrent keys share one call. */\n read(key: string, load: () => Promise<T>): Promise<T>;\n /** Drop one key (after a write that changes it). */\n invalidate(key: string): void;\n /** Drop everything (sign-out, org switch). */\n clear(): void;\n /** Diagnostics: how many resolved entries are held. */\n size(): number;\n}\n\nexport function createReadCache<T>(options: ReadCacheOptions): ReadCache<T> {\n const now = options.now ?? (() => Date.now());\n const maxEntries = options.maxEntries ?? 500;\n const resolved = new Map<string, Entry<T>>();\n const inFlight = new Map<string, Promise<T>>();\n\n function evictIfNeeded(): void {\n while (resolved.size > maxEntries) {\n const oldest = resolved.keys().next();\n if (oldest.done === true) return;\n resolved.delete(oldest.value);\n }\n }\n\n return {\n read(key, load) {\n const hit = resolved.get(key);\n if (hit !== undefined && hit.expiresAt > now()) {\n return Promise.resolve(hit.value);\n }\n if (hit !== undefined) resolved.delete(key);\n\n const pending = inFlight.get(key);\n if (pending !== undefined) return pending;\n\n const started = load()\n .then((value) => {\n resolved.set(key, { value, expiresAt: now() + options.ttlMs });\n evictIfNeeded();\n return value;\n })\n .finally(() => {\n // ALWAYS drop the in-flight entry — on success the value now lives in\n // `resolved`, and on failure the next caller must be free to retry.\n // Caching the rejection is the \"surface stays broken until reload\" bug.\n inFlight.delete(key);\n });\n\n inFlight.set(key, started);\n return started;\n },\n invalidate(key) {\n resolved.delete(key);\n inFlight.delete(key);\n },\n clear() {\n resolved.clear();\n inFlight.clear();\n },\n size() {\n return resolved.size;\n },\n };\n}\n","/**\n * THE MESSAGING CHANNEL NAMESPACES.\n *\n * D5: zero hand-rolled `.channel(` in this package. Every topic is declared\n * through `@ai-matrx/realtime`'s registry, which refuses a colliding\n * re-declaration and hands every connection attempt a unique instance topic.\n *\n * TWO channels, deliberately — not four. The origin ran `messages:`, `typing:`,\n * `presence:` and a global list channel as four independent subscriptions, and\n * three of the four hardening items were about them fighting each other. Here:\n *\n * - `messaging-inbox` (per user): the conversation list's own feed.\n * - `messaging-conversation` (per conversation): messages, presence, AND\n * typing on ONE channel, because they are one room. Sharing the channel is\n * what makes \"the person typing is also present\" true by construction\n * instead of by two subscriptions agreeing.\n *\n * `defineChannelNamespace` throws on a conflicting re-declaration, so these are\n * created lazily through a `globalThis` slot: with dual ESM/CJS output the\n * module can be evaluated twice in one process, and a second identical\n * declaration must be a no-op rather than a crash.\n */\n\nimport { defineChannelNamespace, type ChannelNamespace } from \"@ai-matrx/realtime\";\nimport { globalSlot } from \"./slot\";\nimport type { ConversationId, UserId } from \"./types\";\n\ninterface Namespaces {\n inbox: ChannelNamespace;\n conversation: ChannelNamespace;\n}\n\nfunction namespaces(): Namespaces {\n return globalSlot<Namespaces>(\"channel-namespaces\", () => ({\n inbox: defineChannelNamespace({\n namespace: \"messaging-inbox\",\n parts: [\"userId\"],\n description:\n \"One user's conversation list: new messages anywhere they participate, membership \" +\n \"changes, and read-state updates.\",\n }),\n conversation: defineChannelNamespace({\n namespace: \"messaging-conversation\",\n parts: [\"conversationId\"],\n description:\n \"One conversation: message inserts/updates, presence, and typing — deliberately one \" +\n \"channel, because they are one room.\",\n }),\n }));\n}\n\nexport function inboxTopic(userId: UserId): string {\n return namespaces().inbox.topic({ userId });\n}\n\nexport function conversationTopic(conversationId: ConversationId): string {\n return namespaces().conversation.topic({ conversationId });\n}\n\n/** Broadcast event names. One place, so a sender and a receiver cannot drift. */\nexport const MESSAGING_EVENTS = {\n /** A freshly sent message, broadcast beside the Postgres Changes row so a\n * receiver gets it on whichever path arrives first (both are deduped). */\n message: \"mx.message\",\n /** A message edited or soft-deleted. */\n messageUpdated: \"mx.message.updated\",\n /** An action receipt, so every viewer's chip settles at once. */\n actionSettled: \"mx.action.settled\",\n} as const;\n","/**\n * PROCESS-GLOBAL STATE LIVES HERE, NEVER IN A MODULE `let`.\n *\n * With `splitting: false` dual ESM/CJS output, one module can be instantiated\n * TWICE in a single process (a Next.js app that imports ESM while its Jest\n * suite requires CJS is the ordinary case). A module-level `let` therefore\n * silently splits into two independent values — which is how a read cache\n * \"randomly\" stops deduping and a registry \"randomly\" forgets a kind.\n *\n * Everything process-global goes through `globalThis` + `Symbol.for`, and the\n * packed-tarball canary proves the slots are shared across module graphs.\n */\nconst NAMESPACE = \"ai-matrx.messaging\";\n\nexport function globalSlot<T>(name: string, create: () => T): T {\n const key = Symbol.for(`${NAMESPACE}.${name}`);\n const host = globalThis as Record<symbol, unknown>;\n const existing = host[key];\n if (existing !== undefined) return existing as T;\n const created = create();\n host[key] = created;\n return created;\n}\n\n/** Test-only: drop a slot so a suite can start from a clean registry. */\nexport function resetGlobalSlotForTests(name: string): void {\n const key = Symbol.for(`${NAMESPACE}.${name}`);\n delete (globalThis as Record<symbol, unknown>)[key];\n}\n","/**\n * THE MESSAGING ENGINE — the object a host mounts once and never reasons about.\n *\n * It owns the wiring that every chat implementation gets wrong: which channel\n * carries what, what happens on reconnect, which of four arrival paths wins,\n * when a read receipt is written, and what a failed send does. All of it is\n * here so a consumer's job is `engine.send(draft)` and rendering a snapshot.\n *\n * THE RECONNECT RULE, inherited from `@ai-matrx/realtime` and honored here:\n * realtime has NO REPLAY. Every channel this engine opens declares an\n * `onBackfill` door, and those doors re-read from the database. A reconnect\n * without a re-read leaves a permanently wrong thread that looks perfectly\n * healthy — the single most expensive bug class in messaging.\n */\n\nimport {\n clientSessionId,\n type ChannelHandle,\n type RealtimeManager,\n} from \"@ai-matrx/realtime\";\nimport { conversationTopic, inboxTopic, MESSAGING_EVENTS } from \"./channels\";\nimport { MessagingError, normalizeMessagingError } from \"./errors\";\nimport { createOutbox, type Outbox, type OutboxEntry, type OutboxStorage } from \"./outbox\";\nimport { projectMessage } from \"./projection\";\nimport type { MessagingRepository } from \"./repository\";\nimport { createMessagingStore, optimisticMessage, type MessagingStore } from \"./store\";\nimport type {\n ClientMessageId,\n ConversationCursor,\n ConversationId,\n DraftMessage,\n Message,\n MessageId,\n MessagingIdentity,\n UserId,\n} from \"./types\";\n\nexport interface EngineDiagnostic {\n readonly level: \"info\" | \"warn\" | \"error\";\n readonly message: string;\n readonly remedy?: string | undefined;\n}\n\nexport interface MessagingEngineOptions {\n repository: MessagingRepository;\n manager: RealtimeManager;\n identity: MessagingIdentity;\n outboxStorage?: OutboxStorage | undefined;\n onDiagnostic?: ((event: EngineDiagnostic) => void) | undefined;\n /** Fired for a message from someone else, for notification sinks. */\n onIncoming?: ((message: Message) => void) | undefined;\n conversationPageSize?: number | undefined;\n messagePageSize?: number | undefined;\n}\n\nexport interface MessagingEngine {\n readonly store: MessagingStore;\n readonly outbox: Outbox;\n readonly identity: MessagingIdentity;\n /** Load page one of the inbox and start the inbox channel. */\n start(): Promise<void>;\n loadMoreConversations(): Promise<void>;\n /** Open a conversation: load its thread and subscribe to its channel. */\n openConversation(id: ConversationId): Promise<void>;\n closeConversation(id: ConversationId): void;\n loadOlderMessages(id: ConversationId): Promise<void>;\n /** Queue a message. Returns immediately with the optimistic row on screen. */\n send(draft: DraftMessage): ClientMessageId;\n retry(entryId: string): void;\n discard(entryId: string): void;\n editMessage(id: MessageId, conversationId: ConversationId, content: string): Promise<void>;\n deleteMessage(\n id: MessageId,\n conversationId: ConversationId,\n forEveryone: boolean,\n ): Promise<void>;\n markRead(id: ConversationId): Promise<void>;\n startDirectConversation(otherUserId: UserId): Promise<ConversationId>;\n dispose(): void;\n}\n\nexport function createMessagingEngine(\n options: MessagingEngineOptions,\n): MessagingEngine {\n const { repository, manager, identity } = options;\n const store = createMessagingStore();\n const conversationPageSize = options.conversationPageSize ?? 30;\n const messagePageSize = options.messagePageSize ?? 50;\n\n const openChannels = new Map<ConversationId, ChannelHandle>();\n let inboxChannel: ChannelHandle | null = null;\n let conversationCursor: ConversationCursor | null = null;\n let disposed = false;\n\n function report(event: EngineDiagnostic): void {\n options.onDiagnostic?.(event);\n }\n\n function reportError(error: unknown, operation: string): void {\n const normalized = normalizeMessagingError(error, operation);\n report({\n // A missing session is a NORMAL lifecycle moment, not a red error. This\n // one line is the cure for \"909 captured errors in 0.6s\".\n level: normalized.code === \"session-unavailable\" ? \"warn\" : \"error\",\n message: normalized.message,\n remedy: normalized.remedy,\n });\n }\n\n const outbox = createOutbox({\n ...(options.outboxStorage !== undefined ? { storage: options.outboxStorage } : {}),\n send: (draft, clientMessageId) => repository.insertMessage(draft, clientMessageId),\n onSent: (entry: OutboxEntry, message) => {\n // The confirmed row carries the same client key as the optimistic bubble,\n // so this MERGES rather than appending a second copy.\n store.ingest(message);\n broadcastMessage(message);\n // NO REFETCH. The row's new preview and sort position are derivable from\n // the message we already hold; a conversation-list RPC per sent message\n // is a full page read per burst at a busy org.\n store.applyLastMessage(message);\n void entry;\n },\n onChange: (entries) => {\n // Reflect every queued/failed entry as an optimistic row so a message the\n // user typed is VISIBLE while it waits — an invisible queue is the same\n // as a lost message from the user's side.\n entries.forEach((entry) => {\n store.ingest(\n optimisticMessage({\n conversationId: entry.draft.conversationId,\n senderId: identity.userId,\n organizationId: identity.organizationId,\n content: entry.draft.content,\n clientMessageId: entry.clientMessageId,\n ...(entry.draft.kind !== undefined ? { kind: entry.draft.kind } : {}),\n ...(entry.draft.replyToId !== undefined\n ? { replyToId: entry.draft.replyToId }\n : {}),\n ...(entry.draft.action !== undefined ? { action: entry.draft.action } : {}),\n ...(entry.draft.attachments !== undefined\n ? { attachments: [...entry.draft.attachments] }\n : {}),\n ...(entry.draft.references !== undefined\n ? { references: [...entry.draft.references] }\n : {}),\n }),\n );\n });\n },\n onDiagnostic: (message) => report({ level: \"warn\", message }),\n });\n\n /**\n * Broadcast beside the Postgres Changes row: whichever arrives first shows\n * the message, and the store's dedup makes the second one free. Receivers\n * suppress our own echo through the realtime envelope.\n *\n * 🚨 It sends ONLY on an ALREADY-OPEN handle. Opening a channel here to send\n * one message would open a channel per send and never close any of them —\n * the leak the realtime manager's enforced lifecycle otherwise makes\n * impossible. No open conversation channel simply means nobody is watching\n * this thread live, and Postgres Changes still carries the row.\n */\n function broadcastMessage(message: Message): void {\n openChannels.get(message.conversationId)?.send(MESSAGING_EVENTS.message, message);\n }\n\n async function reloadInbox(): Promise<void> {\n const page = await repository.listConversations({ limit: conversationPageSize });\n conversationCursor = page.nextCursor;\n store.setConversations(page.items, page.hasMore);\n }\n\n async function backfillConversation(id: ConversationId): Promise<void> {\n const thread = store.snapshot().threads.get(id);\n const since = thread?.latestAt ?? null;\n try {\n if (since === null) {\n const page = await repository.listMessages(id, { limit: messagePageSize });\n store.setThread(id, page.items, { hasMoreOlder: page.hasMore });\n return;\n }\n const missed = await repository.messagesSince(id, since);\n store.ingestMany(missed);\n if (missed.length > 0) {\n report({\n level: \"info\",\n message: `Recovered ${missed.length} message(s) missed while disconnected.`,\n });\n }\n } catch (error) {\n reportError(error, \"backfillConversation\");\n }\n }\n\n const engine: MessagingEngine = {\n store,\n outbox,\n identity,\n\n async start() {\n await reloadInbox();\n if (disposed || inboxChannel !== null) return;\n inboxChannel = manager.open({\n topic: inboxTopic(identity.userId),\n postgresChanges: [\n {\n event: \"INSERT\",\n schema: \"communication\",\n table: \"dm_messages\",\n rowId: (row) => (typeof row[\"id\"] === \"string\" ? row[\"id\"] : undefined),\n onChange: ({ row }) => {\n if (row === null) return;\n const message = projectMessage(row, identity.organizationId);\n // Realtime has no per-user filter on this table, so rows for\n // conversations this user is not in can arrive. Dropping unknown\n // conversations here is why the inbox does not flicker with\n // strangers' traffic; the row is authorized by RLS anyway.\n const known = store\n .snapshot()\n .conversations.some((item) => item.conversation.id === message.conversationId);\n if (!known) {\n void reloadInbox();\n return;\n }\n store.ingest(message);\n const isMine = message.senderId === identity.userId;\n // Same rule as the send path: update the row in place rather\n // than re-reading the list for every message in the org.\n store.applyLastMessage(message, { incrementUnread: !isMine });\n if (!isMine) options.onIncoming?.(message);\n },\n },\n {\n event: \"*\",\n schema: \"communication\",\n table: \"dm_conversation_participants\",\n filter: `user_id=eq.${identity.userId}`,\n rowId: (row) => (typeof row[\"id\"] === \"string\" ? row[\"id\"] : undefined),\n onChange: () => {\n // Membership or read-state changed: the list's unread counts and\n // membership come from the RPC, so re-read rather than guess.\n void reloadInbox();\n },\n },\n ],\n // THE BACKFILL DOOR for the inbox.\n onBackfill: () => {\n void reloadInbox().catch((error: unknown) => reportError(error, \"inboxBackfill\"));\n outbox.flush();\n },\n });\n },\n\n async loadMoreConversations() {\n if (conversationCursor === null) return;\n try {\n const page = await repository.listConversations({\n limit: conversationPageSize,\n cursor: conversationCursor,\n });\n conversationCursor = page.nextCursor;\n store.appendConversations(page.items, page.hasMore);\n } catch (error) {\n reportError(error, \"loadMoreConversations\");\n }\n },\n\n async openConversation(id) {\n store.setActiveConversation(id);\n try {\n const page = await repository.listMessages(id, { limit: messagePageSize });\n store.setThread(id, page.items, { hasMoreOlder: page.hasMore });\n } catch (error) {\n reportError(error, \"openConversation\");\n }\n\n if (openChannels.has(id) || disposed) return;\n const handle = manager.open({\n topic: conversationTopic(id),\n postgresChanges: [\n {\n event: \"*\",\n schema: \"communication\",\n table: \"dm_messages\",\n filter: `conversation_id=eq.${id}`,\n rowId: (row) => (typeof row[\"id\"] === \"string\" ? row[\"id\"] : undefined),\n fingerprint: (row) =>\n typeof row[\"content\"] === \"string\" ? row[\"content\"] : undefined,\n updatedAtField: \"updated_at\",\n updatedByField: \"updated_by\",\n onChange: ({ row }) => {\n if (row === null) return;\n store.ingest(projectMessage(row, identity.organizationId));\n },\n },\n ],\n broadcast: [\n {\n event: MESSAGING_EVENTS.message,\n onMessage: ({ data }) => {\n if (typeof data !== \"object\" || data === null) return;\n // Broadcast payloads are already domain-shaped (we sent them), so\n // they go through the same one door every message uses.\n store.ingest(data as Message);\n },\n },\n ],\n onBackfill: () => {\n void backfillConversation(id);\n outbox.flush();\n },\n });\n openChannels.set(id, handle);\n },\n\n closeConversation(id) {\n openChannels.get(id)?.close();\n openChannels.delete(id);\n if (store.snapshot().activeConversationId === id) {\n store.setActiveConversation(null);\n }\n },\n\n async loadOlderMessages(id) {\n const thread = store.snapshot().threads.get(id);\n const oldest = thread?.messages[0];\n if (thread === undefined || oldest === undefined || !thread.hasMoreOlder) return;\n try {\n const page = await repository.listMessages(id, {\n limit: messagePageSize,\n cursor: { beforeCreatedAt: oldest.createdAt, beforeMessageId: oldest.id },\n });\n store.prependOlder(id, page.items, page.hasMore);\n } catch (error) {\n reportError(error, \"loadOlderMessages\");\n }\n },\n\n send(draft) {\n if (draft.content.trim().length === 0 && (draft.attachments ?? []).length === 0) {\n throw new MessagingError(\n \"misconfigured\",\n \"send: empty draft\",\n \"A message needs text or at least one attachment. The composer disables Send \" +\n \"for an empty draft rather than queueing nothing.\",\n );\n }\n return outbox.enqueue(draft);\n },\n\n retry: (entryId) => outbox.retry(entryId),\n discard: (entryId) => outbox.discard(entryId),\n\n async editMessage(id, conversationId, content) {\n try {\n const message = await repository.editMessage(id, content);\n store.ingest(message);\n broadcastMessage(message);\n } catch (error) {\n reportError(error, \"editMessage\");\n throw normalizeMessagingError(error, \"editMessage\");\n }\n void conversationId;\n },\n\n async deleteMessage(id, conversationId, forEveryone) {\n try {\n await repository.deleteMessage(id, forEveryone);\n store.removeMessage(conversationId, id);\n } catch (error) {\n reportError(error, \"deleteMessage\");\n throw normalizeMessagingError(error, \"deleteMessage\");\n }\n },\n\n async markRead(id) {\n // Optimistic first: the badge must clear the instant the user opens the\n // thread, not one round trip later.\n store.markConversationRead(id);\n try {\n await repository.markRead(id);\n } catch (error) {\n reportError(error, \"markRead\");\n }\n },\n\n async startDirectConversation(otherUserId) {\n const id = await repository.getOrCreateDirectConversation(otherUserId);\n await reloadInbox();\n return id;\n },\n\n dispose() {\n disposed = true;\n outbox.dispose();\n openChannels.forEach((handle) => handle.close());\n openChannels.clear();\n inboxChannel?.close();\n inboxChannel = null;\n },\n };\n\n return engine;\n}\n\n/** The realtime client session id, exposed for diagnostics surfaces. */\nexport function messagingClientId(): string {\n return clientSessionId();\n}\n","/**\n * THE OUTBOX — a message you typed is NEVER lost (R2, table-stakes law).\n *\n * This is the piece hosts always get wrong, so it is entirely in the package\n * (C22). What \"never lost\" actually requires, and what each part defends:\n *\n * - **Durability across a reload.** The draft is persisted BEFORE the network\n * call, not after it succeeds. A tab closed mid-send comes back with the\n * message still queued. The storage port has a working default; a host that\n * injects nothing still gets in-memory queuing, and it SAYS so rather than\n * pretending to be durable.\n * - **Exactly-once on retry.** Every entry carries a client-minted\n * `clientMessageId` that is generated ONCE and reused by every attempt. A\n * retry after a response that was actually delivered is deduped by the\n * database's idempotency key instead of posting the message twice.\n * - **Order.** Entries for one conversation send strictly in order; message 2\n * never overtakes a retrying message 1.\n * - **Bounded, honest failure.** Attempts back off; after `maxAttempts` the\n * entry stays in the queue marked `failed` WITH a reason and a retry door.\n * It is never silently dropped, and it never retries forever.\n */\n\nimport { randomId } from \"@ai-matrx/realtime\";\nimport { MessagingError } from \"./errors\";\nimport type { ClientMessageId, ConversationId, DraftMessage, Message } from \"./types\";\n\nexport interface OutboxEntry {\n readonly id: string;\n readonly clientMessageId: ClientMessageId;\n readonly draft: DraftMessage;\n readonly queuedAt: number;\n readonly attempts: number;\n readonly state: \"queued\" | \"sending\" | \"failed\";\n readonly failureReason: string | null;\n}\n\n/**\n * Persistence for pending sends. A default is always supplied — the package\n * never leaves a port empty (THE ALL-INCLUSIVE LAW).\n */\nexport interface OutboxStorage {\n /** Human-readable, shown in diagnostics so \"durable\" is never assumed. */\n readonly name: string;\n readonly durable: boolean;\n load(): readonly OutboxEntry[];\n save(entries: readonly OutboxEntry[]): void;\n}\n\nexport function createMemoryOutboxStorage(): OutboxStorage {\n let held: readonly OutboxEntry[] = [];\n return {\n name: \"memory\",\n durable: false,\n load: () => held,\n save: (entries) => {\n held = entries;\n },\n };\n}\n\ninterface WebStorageLike {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n}\n\n/**\n * The real default in a browser. Falls back to memory — announcing itself —\n * when storage is unavailable (private mode, a disabled-cookies browser, SSR).\n */\nexport function createWebOutboxStorage(args: {\n key?: string;\n storage?: WebStorageLike | null;\n onFallback?: (reason: string) => void;\n}): OutboxStorage {\n const key = args.key ?? \"ai-matrx.messaging.outbox\";\n let storage: WebStorageLike | null = args.storage ?? null;\n if (storage === null) {\n try {\n const candidate = (globalThis as { localStorage?: WebStorageLike }).localStorage;\n storage = candidate ?? null;\n } catch {\n storage = null;\n }\n }\n if (storage === null) {\n args.onFallback?.(\n \"localStorage is unavailable, so queued messages will NOT survive a reload. \" +\n \"Inject an OutboxStorage on <MessagingProvider> to restore durability.\",\n );\n return createMemoryOutboxStorage();\n }\n const backing = storage;\n return {\n name: \"web-storage\",\n durable: true,\n load() {\n try {\n const raw = backing.getItem(key);\n if (raw === null) return [];\n const parsed: unknown = JSON.parse(raw);\n return Array.isArray(parsed) ? (parsed as OutboxEntry[]) : [];\n } catch {\n // Corrupt storage must not brick the composer. Start empty and say so\n // through the next save.\n return [];\n }\n },\n save(entries) {\n try {\n backing.setItem(key, JSON.stringify(entries));\n } catch {\n args.onFallback?.(\n \"Writing the outbox to localStorage failed (quota or private mode); queued \" +\n \"messages are in memory only for this session.\",\n );\n }\n },\n };\n}\n\nexport interface OutboxOptions {\n /** Actually send. Returns the persisted message. */\n send: (draft: DraftMessage, clientMessageId: ClientMessageId) => Promise<Message>;\n /** Called when an entry lands. The store collapses it with the optimistic row. */\n onSent: (entry: OutboxEntry, message: Message) => void;\n onChange: (entries: readonly OutboxEntry[]) => void;\n onDiagnostic?: (message: string) => void;\n storage?: OutboxStorage;\n maxAttempts?: number;\n /** Backoff schedule per attempt, ms. The last value repeats. */\n backoffMs?: readonly number[];\n timers?: {\n setTimeout: (run: () => void, ms: number) => unknown;\n clearTimeout: (handle: unknown) => void;\n now: () => number;\n };\n}\n\nexport interface Outbox {\n entries(): readonly OutboxEntry[];\n /** Queue a draft. Returns the id the optimistic message must carry. */\n enqueue(draft: DraftMessage): ClientMessageId;\n /** Retry one failed entry, on the user's explicit ask. */\n retry(entryId: string): void;\n /** Drop one entry — the only sanctioned way a typed message leaves the queue. */\n discard(entryId: string): void;\n /** Network came back / tab woke: try everything that is waiting. */\n flush(): void;\n pendingFor(conversationId: ConversationId): readonly OutboxEntry[];\n dispose(): void;\n}\n\nconst DEFAULT_BACKOFF = [0, 1_000, 3_000, 8_000, 20_000] as const;\n\nexport function createOutbox(options: OutboxOptions): Outbox {\n const timers = options.timers ?? {\n setTimeout: (run, ms) => setTimeout(run, ms),\n clearTimeout: (handle) => clearTimeout(handle as ReturnType<typeof setTimeout>),\n now: () => Date.now(),\n };\n const storage = options.storage ?? createMemoryOutboxStorage();\n const maxAttempts = options.maxAttempts ?? DEFAULT_BACKOFF.length;\n const backoff = options.backoffMs ?? DEFAULT_BACKOFF;\n\n let entries: OutboxEntry[] = [...storage.load()].map((entry) => ({\n ...entry,\n // Anything found mid-`sending` after a reload is genuinely unknown: it may\n // or may not have reached the database. It goes back to `queued` and the\n // idempotency key makes the re-send safe — that is exactly what the key is\n // for. Marking it failed instead would strand a message that was typed.\n state: entry.state === \"sending\" ? \"queued\" : entry.state,\n }));\n let timer: unknown = null;\n let disposed = false;\n\n function persist(): void {\n storage.save(entries);\n options.onChange(entries);\n }\n\n function delayFor(attempts: number): number {\n return backoff[Math.min(attempts, backoff.length - 1)] ?? 0;\n }\n\n function schedule(ms: number): void {\n if (disposed || timer !== null) return;\n timer = timers.setTimeout(() => {\n timer = null;\n void pump();\n }, ms);\n }\n\n async function pump(): Promise<void> {\n if (disposed) return;\n // ORDER: one entry at a time, oldest first. Message 2 must never overtake a\n // retrying message 1 in the same conversation, and the simplest guarantee\n // of that is a single-file queue.\n const next = entries.find((entry) => entry.state === \"queued\");\n if (next === undefined) return;\n\n entries = entries.map((entry) =>\n entry.id === next.id ? { ...entry, state: \"sending\" as const } : entry,\n );\n persist();\n\n try {\n const message = await options.send(next.draft, next.clientMessageId);\n entries = entries.filter((entry) => entry.id !== next.id);\n persist();\n options.onSent(next, message);\n schedule(0);\n } catch (error) {\n const attempts = next.attempts + 1;\n const reason =\n error instanceof MessagingError ? error.message : String((error as Error)?.message ?? error);\n const exhausted = attempts >= maxAttempts;\n entries = entries.map((entry) =>\n entry.id === next.id\n ? {\n ...entry,\n attempts,\n state: exhausted ? (\"failed\" as const) : (\"queued\" as const),\n failureReason: reason,\n }\n : entry,\n );\n persist();\n if (exhausted) {\n options.onDiagnostic?.(\n `Message could not be sent after ${attempts} attempts: ${reason}. It is still in ` +\n `the outbox — offer the user Retry or Discard; never drop it.`,\n );\n // Keep going: a later entry may be for a healthy conversation.\n schedule(0);\n } else {\n schedule(delayFor(attempts));\n }\n }\n }\n\n const outbox: Outbox = {\n entries: () => entries,\n enqueue(draft) {\n const clientMessageId = `mx-${randomId()}` as ClientMessageId;\n entries = [\n ...entries,\n {\n id: randomId(),\n clientMessageId,\n draft,\n queuedAt: timers.now(),\n attempts: 0,\n state: \"queued\",\n failureReason: null,\n },\n ];\n // PERSIST FIRST, send second. The window between \"user pressed enter\" and\n // \"the request left\" is exactly where messages get lost.\n persist();\n schedule(0);\n return clientMessageId;\n },\n retry(entryId) {\n entries = entries.map((entry) =>\n entry.id === entryId\n ? { ...entry, state: \"queued\" as const, attempts: 0, failureReason: null }\n : entry,\n );\n persist();\n schedule(0);\n },\n discard(entryId) {\n entries = entries.filter((entry) => entry.id !== entryId);\n persist();\n },\n flush() {\n entries = entries.map((entry) =>\n entry.state === \"failed\"\n ? { ...entry, state: \"queued\" as const, attempts: 0 }\n : entry,\n );\n persist();\n schedule(0);\n },\n pendingFor(conversationId) {\n return entries.filter((entry) => entry.draft.conversationId === conversationId);\n },\n dispose() {\n disposed = true;\n if (timer !== null) timers.clearTimeout(timer);\n timer = null;\n },\n };\n\n if (entries.length > 0) {\n options.onDiagnostic?.(\n `Restored ${entries.length} unsent message(s) from the ${storage.name} outbox.`,\n );\n schedule(0);\n }\n\n return outbox;\n}\n","/**\n * THE INGRESS BOUNDARY — where a database row becomes a domain object.\n *\n * Everything crossing this line is validated, because the alternative is a\n * screen that lies. A conversation row whose `participants` aggregate came back\n * as a string instead of an array used to render an inbox entry named\n * `undefined` with a working click target; refusing the row here turns a silent\n * wrong screen into a loud, remediable error (nothing fails silently).\n *\n * The rules:\n * - A missing REQUIRED field is a refusal, never a `?? \"\"` that renders blank.\n * - A missing OPTIONAL field is a null, never an invented default.\n * - `metadata` / `action_data` are host-and-DB-owned JSON: typed over `unknown`\n * and narrowed by shape, never cast (the data 0.2.1 `Json = unknown` lesson).\n */\n\nimport { invalidResponse } from \"./errors\";\nimport type {\n Attachment,\n ClientMessageId,\n ConversationId,\n ConversationSummary,\n ConversationType,\n DeliveryState,\n JsonObject,\n MatrxReference,\n Message,\n MessageAction,\n MessageId,\n MessageKind,\n OrganizationId,\n ParticipantRole,\n UserId,\n UserSummary,\n} from \"./types\";\n\nfunction str(row: Record<string, unknown>, key: string): string | null {\n const value = row[key];\n return typeof value === \"string\" && value.length > 0 ? value : null;\n}\n\nfunction requiredStr(\n row: Record<string, unknown>,\n key: string,\n operation: string,\n): string {\n const value = str(row, key);\n if (value === null) {\n throw invalidResponse(operation, `required field \"${key}\" was ${JSON.stringify(row[key])}`);\n }\n return value;\n}\n\nfunction bool(row: Record<string, unknown>, key: string, fallback: boolean): boolean {\n const value = row[key];\n return typeof value === \"boolean\" ? value : fallback;\n}\n\nfunction jsonObject(value: unknown): JsonObject {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as JsonObject)\n : {};\n}\n\nconst CONVERSATION_TYPES: ReadonlySet<string> = new Set([\"direct\", \"group\", \"org\"]);\nconst MESSAGE_KINDS: ReadonlySet<string> = new Set([\n \"text\",\n \"image\",\n \"video\",\n \"audio\",\n \"file\",\n \"system\",\n \"action\",\n]);\nconst ROLES: ReadonlySet<string> = new Set([\"owner\", \"admin\", \"member\"]);\nconst DELIVERY_STATES: ReadonlySet<string> = new Set([\n \"sending\",\n \"sent\",\n \"delivered\",\n \"read\",\n \"failed\",\n]);\n\nexport function projectUserSummary(row: Record<string, unknown>): UserSummary {\n const userId = requiredStr(row, \"user_id\", \"projectUserSummary\") as UserId;\n const displayName = str(row, \"display_name\") ?? str(row, \"email\") ?? userId;\n return {\n userId,\n displayName,\n email: str(row, \"email\"),\n avatarUrl: str(row, \"avatar_url\"),\n isAgent: bool(row, \"is_agent\", false),\n };\n}\n\n/** The `participants` aggregate on the conversation-list RPC row. */\nfunction projectParticipants(value: unknown, operation: string): readonly UserSummary[] {\n if (value === null || value === undefined) return [];\n if (!Array.isArray(value)) {\n throw invalidResponse(\n operation,\n `\"participants\" was ${typeof value}, not an array — the RPC's jsonb aggregate is malformed`,\n );\n }\n const summaries: UserSummary[] = [];\n for (const entry of value) {\n if (typeof entry !== \"object\" || entry === null) continue;\n const record = entry as Record<string, unknown>;\n // A participant aggregate that carries `id` instead of `user_id` is the\n // real historical shape drift; accept both, refuse neither-present.\n const id = str(record, \"user_id\") ?? str(record, \"id\");\n if (id === null) continue;\n summaries.push(\n projectUserSummary({\n ...record,\n user_id: id,\n }),\n );\n }\n return summaries;\n}\n\nexport function projectMessageAction(value: unknown): MessageAction | null {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return null;\n const record = value as Record<string, unknown>;\n const kind = record[\"kind\"];\n if (typeof kind !== \"string\" || kind.length === 0) return null;\n const version = record[\"version\"];\n return {\n kind,\n // A payload without a version is version 1 — the shape that predates the\n // envelope. Refusing it would silently blank every message sent before the\n // envelope existed.\n version: typeof version === \"number\" && Number.isFinite(version) ? version : 1,\n payload: jsonObject(record[\"payload\"]),\n };\n}\n\nfunction projectAttachments(metadata: JsonObject): readonly Attachment[] {\n const raw = (metadata as Record<string, unknown>)[\"attachments\"];\n if (!Array.isArray(raw)) return [];\n const attachments: Attachment[] = [];\n for (const entry of raw) {\n if (typeof entry !== \"object\" || entry === null) continue;\n const record = entry as Record<string, unknown>;\n // A DURABLE file id is the identity. An entry that only has a URL is a\n // signed-URL identity — the thing that breaks the moment the link expires —\n // so it is dropped rather than rendered as an attachment that will 403.\n const fileId = str(record, \"fileId\") ?? str(record, \"file_id\");\n if (fileId === null) continue;\n const size = record[\"sizeBytes\"] ?? record[\"size_bytes\"];\n const width = record[\"width\"];\n const height = record[\"height\"];\n attachments.push({\n fileId,\n fileName: str(record, \"fileName\") ?? str(record, \"file_name\") ?? fileId,\n mimeType: str(record, \"mimeType\") ?? str(record, \"mime_type\"),\n sizeBytes: typeof size === \"number\" ? size : null,\n width: typeof width === \"number\" ? width : null,\n height: typeof height === \"number\" ? height : null,\n });\n }\n return attachments;\n}\n\nfunction projectReferences(metadata: JsonObject): readonly MatrxReference[] {\n const raw = (metadata as Record<string, unknown>)[\"references\"];\n if (!Array.isArray(raw)) return [];\n const references: MatrxReference[] = [];\n for (const entry of raw) {\n if (typeof entry !== \"object\" || entry === null) continue;\n const record = entry as Record<string, unknown>;\n const entityType = str(record, \"entityType\") ?? str(record, \"entity_type\");\n const entityId = str(record, \"entityId\") ?? str(record, \"entity_id\");\n // NO DEAD ENDS: a reference without a resolvable identity cannot be opened,\n // so it is never rendered as an openable card.\n if (entityType === null || entityId === null) continue;\n const href = str(record, \"href\");\n references.push({\n entityType,\n entityId,\n label: str(record, \"label\") ?? entityId,\n ...(href !== null ? { href } : {}),\n });\n }\n return references;\n}\n\nexport function projectMessage(\n row: Record<string, unknown>,\n fallbackOrganizationId: OrganizationId,\n): Message {\n const operation = \"projectMessage\";\n const metadata = jsonObject(row[\"metadata\"]);\n const kindRaw = str(row, \"message_type\") ?? \"text\";\n const stateRaw = str(row, \"status\") ?? \"sent\";\n const replyTo = str(row, \"reply_to_id\");\n const clientMessageId = str(row, \"client_message_id\");\n const action = projectMessageAction(row[\"action_data\"]);\n return {\n id: requiredStr(row, \"id\", operation) as MessageId,\n conversationId: requiredStr(row, \"conversation_id\", operation) as ConversationId,\n senderId: requiredStr(row, \"sender_id\", operation) as UserId,\n organizationId: (str(row, \"organization_id\") ?? fallbackOrganizationId) as OrganizationId,\n content: typeof row[\"content\"] === \"string\" ? (row[\"content\"] as string) : \"\",\n kind: (MESSAGE_KINDS.has(kindRaw) ? kindRaw : \"text\") as MessageKind,\n // A row read from the DB is at least `sent`. `sending`/`failed` are outbox\n // states and can never be projected from a persisted row.\n deliveryState: (DELIVERY_STATES.has(stateRaw) && stateRaw !== \"sending\" && stateRaw !== \"failed\"\n ? stateRaw\n : \"sent\") as DeliveryState,\n replyToId: replyTo === null ? null : (replyTo as MessageId),\n clientMessageId: clientMessageId === null ? null : (clientMessageId as ClientMessageId),\n createdAt: requiredStr(row, \"created_at\", operation),\n editedAt: str(row, \"edited_at\"),\n deletedAt: str(row, \"deleted_at\"),\n deletedForEveryone: bool(row, \"deleted_for_everyone\", false),\n action,\n attachments: projectAttachments(metadata),\n references: projectReferences(metadata),\n metadata,\n };\n}\n\nexport function projectParticipantRole(value: unknown): ParticipantRole {\n return (typeof value === \"string\" && ROLES.has(value) ? value : \"member\") as ParticipantRole;\n}\n\nexport function projectConversationSummary(\n row: Record<string, unknown>,\n viewerId: UserId,\n fallbackOrganizationId: OrganizationId,\n): ConversationSummary {\n const operation = \"projectConversationSummary\";\n const id = requiredStr(row, \"conversation_id\", operation) as ConversationId;\n const typeRaw = str(row, \"conversation_type\") ?? \"direct\";\n const type = (CONVERSATION_TYPES.has(typeRaw) ? typeRaw : \"direct\") as ConversationType;\n const participants = projectParticipants(row[\"participants\"], operation);\n const groupName = str(row, \"group_name\");\n const groupImageUrl = str(row, \"group_image_url\");\n const createdBy = str(row, \"created_by\");\n const updatedAt = str(row, \"conversation_updated_at\") ?? str(row, \"updated_at\");\n const createdAt = str(row, \"conversation_created_at\") ?? str(row, \"created_at\") ?? updatedAt;\n const lastMessageAt = str(row, \"last_message_at\");\n const lastSender = str(row, \"last_message_sender_id\");\n const unreadRaw = row[\"unread_count\"];\n\n if (createdAt === null || updatedAt === null) {\n throw invalidResponse(operation, `conversation ${id} carried no timestamps`);\n }\n\n const others = participants.filter((participant) => participant.userId !== viewerId);\n const displayName =\n type === \"direct\"\n ? (others[0]?.displayName ?? \"Direct message\")\n : (groupName ?? \"Group conversation\");\n const displayImageUrl = type === \"direct\" ? (others[0]?.avatarUrl ?? null) : groupImageUrl;\n\n return {\n conversation: {\n id,\n type,\n groupName,\n groupImageUrl,\n createdBy: createdBy === null ? null : (createdBy as UserId),\n organizationId: (str(row, \"organization_id\") ?? fallbackOrganizationId) as OrganizationId,\n createdAt,\n updatedAt,\n metadata: jsonObject(row[\"metadata\"]),\n },\n participants,\n lastMessageContent: str(row, \"last_message_content\"),\n lastMessageSenderId: lastSender === null ? null : (lastSender as UserId),\n lastMessageAt,\n unreadCount:\n typeof unreadRaw === \"number\" && Number.isFinite(unreadRaw) && unreadRaw > 0\n ? Math.floor(unreadRaw)\n : 0,\n isMuted: bool(row, \"is_muted\", false),\n isArchived: bool(row, \"is_archived\", false),\n displayName,\n displayImageUrl,\n // The keyset sort value: the last message if there is one, else the\n // conversation's own update stamp. Empty conversations must still sort.\n sortAt: lastMessageAt ?? updatedAt,\n };\n}\n","/**\n * THE MESSAGE STORE — where \"the same message twice\" is made impossible.\n *\n * A message can reach this store by FOUR different paths, often in any order:\n * the optimistic bubble, the insert's own response, the broadcast, and the\n * Postgres Changes row. Every duplicate bug in every chat product is one of\n * those four arriving out of order. The rules here, each with a regression test:\n *\n * 1. **Identity is `id` OR `clientMessageId`.** The confirmed row and the\n * optimistic bubble are the SAME message; the client-minted key is what\n * proves it. Matching on `(sender, content)` — the tempting shortcut — merges\n * two genuinely different \"ok\" messages into one.\n * 2. **Merge collapses; it never appends.** A confirmed row that finds an\n * optimistic twin REPLACES it in place, keeping the twin's position so the\n * bubble does not jump.\n * 3. **Older never overwrites newer.** An out-of-order UPDATE carrying an older\n * `edited_at`/`created_at` than the held copy is DROPPED. Without this an\n * edit visibly reverts itself a second later.\n * 4. **Order is `created_at` then `id`.** The unique tiebreaker is not\n * decoration: two messages in the same millisecond otherwise swap places on\n * every re-render.\n * 5. **The active conversation's unread count is forced to zero.** A stale\n * server count must never re-badge a conversation the user is reading — a\n * safety net the origin needed in three separate places, so it lives in one\n * place here.\n */\n\nimport type {\n ClientMessageId,\n ConversationId,\n ConversationSummary,\n Message,\n MessageId,\n UserId,\n} from \"./types\";\n\nexport interface ConversationThread {\n readonly conversationId: ConversationId;\n readonly messages: readonly Message[];\n readonly hasMoreOlder: boolean;\n /** The newest `created_at` held — the reconnect backfill's high-water mark. */\n readonly latestAt: string | null;\n}\n\nexport interface MessagingSnapshot {\n readonly conversations: readonly ConversationSummary[];\n readonly hasMoreConversations: boolean;\n /**\n * True once the inbox has actually been READ at least once.\n *\n * An empty list means two completely different things — \"you have no\n * conversations\" and \"we have not looked yet\" — and a UI that cannot tell\n * them apart shows a confident \"No conversations yet\" to someone who has\n * hundreds. That is a screen telling a lie, so the distinction is a fact the\n * store carries rather than something each surface guesses from a timer.\n */\n readonly hasLoadedConversations: boolean;\n readonly threads: ReadonlyMap<ConversationId, ConversationThread>;\n readonly activeConversationId: ConversationId | null;\n /** Conversations WITH unread, not total unread messages (the origin's semantics). */\n readonly totalUnreadConversations: number;\n}\n\nexport interface MessagingStore {\n snapshot(): MessagingSnapshot;\n subscribe(listener: (snapshot: MessagingSnapshot) => void): () => void;\n setConversations(items: readonly ConversationSummary[], hasMore: boolean): void;\n appendConversations(items: readonly ConversationSummary[], hasMore: boolean): void;\n upsertConversation(item: ConversationSummary): void;\n removeConversation(id: ConversationId): void;\n setActiveConversation(id: ConversationId | null): void;\n setThread(\n id: ConversationId,\n messages: readonly Message[],\n args?: { hasMoreOlder?: boolean },\n ): void;\n prependOlder(id: ConversationId, messages: readonly Message[], hasMoreOlder: boolean): void;\n /** The one door every incoming message uses. Returns what actually happened. */\n ingest(message: Message): \"added\" | \"merged\" | \"dropped-stale\" | \"dropped-duplicate\";\n ingestMany(messages: readonly Message[]): void;\n removeMessage(conversationId: ConversationId, messageId: MessageId): void;\n markConversationRead(id: ConversationId): void;\n /**\n * Move a conversation's preview + sort position to reflect a message, with NO\n * refetch. Sending a message must not cost a conversation-list RPC — at a\n * busy org that is one full page read per keystroke-burst, and the row's new\n * state is entirely derivable from the message we already hold.\n */\n applyLastMessage(message: Message, args?: { incrementUnread?: boolean }): void;\n setUnreadCount(id: ConversationId, count: number): void;\n}\n\nfunction timeOf(message: Message): number {\n const stamp = message.editedAt ?? message.createdAt;\n const parsed = Date.parse(stamp);\n return Number.isFinite(parsed) ? parsed : 0;\n}\n\n/** `created_at` then `id` — the unique tiebreaker keeps the order stable. */\nfunction compare(a: Message, b: Message): number {\n if (a.createdAt !== b.createdAt) return a.createdAt < b.createdAt ? -1 : 1;\n return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;\n}\n\nfunction sameMessage(a: Message, b: Message): boolean {\n if (a.id === b.id) return true;\n const key: ClientMessageId | null = b.clientMessageId;\n return key !== null && a.clientMessageId === key;\n}\n\nfunction insertOrdered(messages: readonly Message[], message: Message): Message[] {\n const next = [...messages];\n // Newest-last is the overwhelmingly common case, so scan from the end.\n let index = next.length;\n while (index > 0) {\n const candidate = next[index - 1];\n if (candidate === undefined || compare(candidate, message) <= 0) break;\n index -= 1;\n }\n next.splice(index, 0, message);\n return next;\n}\n\nexport function createMessagingStore(): MessagingStore {\n let conversations: readonly ConversationSummary[] = [];\n let hasMoreConversations = false;\n let hasLoadedConversations = false;\n let threads = new Map<ConversationId, ConversationThread>();\n let activeConversationId: ConversationId | null = null;\n const listeners = new Set<(snapshot: MessagingSnapshot) => void>();\n let cached: MessagingSnapshot | null = null;\n\n function snapshot(): MessagingSnapshot {\n if (cached !== null) return cached;\n cached = {\n conversations,\n hasMoreConversations,\n hasLoadedConversations,\n threads,\n activeConversationId,\n totalUnreadConversations: conversations.filter((item) => item.unreadCount > 0).length,\n };\n return cached;\n }\n\n function emit(): void {\n cached = null;\n const next = snapshot();\n listeners.forEach((listener) => listener(next));\n }\n\n /**\n * RULE 5, in one place. Any path that writes the conversation list runs\n * through here, so a stale server count can never re-badge the conversation\n * the user is currently looking at.\n */\n function normalizeConversations(items: readonly ConversationSummary[]): readonly ConversationSummary[] {\n const active = activeConversationId;\n const zeroed =\n active === null\n ? items\n : items.map((item) =>\n item.conversation.id === active && item.unreadCount !== 0\n ? { ...item, unreadCount: 0 }\n : item,\n );\n return [...zeroed].sort((a, b) => (a.sortAt < b.sortAt ? 1 : a.sortAt > b.sortAt ? -1 : 0));\n }\n\n function threadFor(id: ConversationId): ConversationThread {\n return (\n threads.get(id) ?? {\n conversationId: id,\n messages: [],\n hasMoreOlder: false,\n latestAt: null,\n }\n );\n }\n\n function writeThread(thread: ConversationThread): void {\n const next = new Map(threads);\n const latest = thread.messages.at(-1);\n next.set(thread.conversationId, {\n ...thread,\n latestAt: latest?.createdAt ?? thread.latestAt,\n });\n threads = next;\n }\n\n const store: MessagingStore = {\n snapshot,\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n setConversations(items, hasMore) {\n conversations = normalizeConversations(items);\n hasMoreConversations = hasMore;\n hasLoadedConversations = true;\n emit();\n },\n appendConversations(items, hasMore) {\n // Merge by id: a realtime insert may already have put a row here, and a\n // \"load more\" page that appends it again is the duplicate-row bug.\n const byId = new Map(conversations.map((item) => [item.conversation.id, item]));\n items.forEach((item) => byId.set(item.conversation.id, item));\n conversations = normalizeConversations([...byId.values()]);\n hasMoreConversations = hasMore;\n emit();\n },\n upsertConversation(item) {\n const byId = new Map(conversations.map((entry) => [entry.conversation.id, entry]));\n byId.set(item.conversation.id, item);\n conversations = normalizeConversations([...byId.values()]);\n emit();\n },\n removeConversation(id) {\n conversations = conversations.filter((item) => item.conversation.id !== id);\n const next = new Map(threads);\n next.delete(id);\n threads = next;\n emit();\n },\n setActiveConversation(id) {\n activeConversationId = id;\n conversations = normalizeConversations(conversations);\n emit();\n },\n setThread(id, messages, args = {}) {\n writeThread({\n conversationId: id,\n messages: [...messages].sort(compare),\n hasMoreOlder: args.hasMoreOlder ?? false,\n latestAt: null,\n });\n emit();\n },\n prependOlder(id, messages, hasMoreOlder) {\n const thread = threadFor(id);\n const known = new Set(thread.messages.map((message) => message.id));\n const fresh = messages.filter((message) => !known.has(message.id));\n writeThread({\n ...thread,\n messages: [...fresh, ...thread.messages].sort(compare),\n hasMoreOlder,\n });\n emit();\n },\n ingest(message) {\n const thread = threadFor(message.conversationId);\n const index = thread.messages.findIndex((held) => sameMessage(held, message));\n\n if (index === -1) {\n writeThread({ ...thread, messages: insertOrdered(thread.messages, message) });\n emit();\n return \"added\";\n }\n\n const held = thread.messages[index];\n if (held === undefined) return \"dropped-duplicate\";\n\n // RULE 3. An optimistic row is ALWAYS superseded by a persisted one, even\n // though its clock may read later — the optimistic timestamp is a guess.\n const heldIsOptimistic =\n held.deliveryState === \"sending\" || held.deliveryState === \"failed\";\n if (!heldIsOptimistic && timeOf(message) < timeOf(held)) return \"dropped-stale\";\n if (!heldIsOptimistic && held.id === message.id && timeOf(message) === timeOf(held)) {\n return \"dropped-duplicate\";\n }\n\n // RULE 2: collapse in place, keeping the position.\n const merged: Message = {\n ...held,\n ...message,\n // Never lose the client key — it is what future echoes match on.\n clientMessageId: message.clientMessageId ?? held.clientMessageId,\n };\n const messages = [...thread.messages];\n messages[index] = merged;\n writeThread({ ...thread, messages: messages.sort(compare) });\n emit();\n return \"merged\";\n },\n ingestMany(messages) {\n messages.forEach((message) => {\n store.ingest(message);\n });\n },\n removeMessage(conversationId, messageId) {\n const thread = threadFor(conversationId);\n writeThread({\n ...thread,\n messages: thread.messages.filter((message) => message.id !== messageId),\n });\n emit();\n },\n applyLastMessage(message, args = {}) {\n const existing = conversations.find(\n (item) => item.conversation.id === message.conversationId,\n );\n // A message for a conversation the list has never seen cannot be\n // synthesized honestly (no participants, no display name). The caller\n // re-reads instead; that is the one case worth a request.\n if (existing === undefined) return;\n if (message.deliveryState === \"sending\" || message.deletedAt !== null) return;\n // Never move a row BACKWARDS: an out-of-order arrival must not make the\n // inbox show an older preview than it already has.\n if (existing.lastMessageAt !== null && message.createdAt < existing.lastMessageAt) return;\n\n // The caller decides whether this counts as unread (it knows who \"I\" am);\n // the store only refuses to badge the conversation being read.\n const isActive = activeConversationId === message.conversationId;\n const shouldCount = args.incrementUnread === true && !isActive;\n conversations = normalizeConversations(\n conversations.map((item) =>\n item.conversation.id === message.conversationId\n ? {\n ...item,\n lastMessageContent: message.content,\n lastMessageSenderId: message.senderId,\n lastMessageAt: message.createdAt,\n sortAt: message.createdAt,\n unreadCount: shouldCount ? item.unreadCount + 1 : item.unreadCount,\n }\n : item,\n ),\n );\n emit();\n },\n markConversationRead(id) {\n conversations = conversations.map((item) =>\n item.conversation.id === id ? { ...item, unreadCount: 0 } : item,\n );\n emit();\n },\n setUnreadCount(id, count) {\n conversations = normalizeConversations(\n conversations.map((item) =>\n item.conversation.id === id ? { ...item, unreadCount: Math.max(0, count) } : item,\n ),\n );\n emit();\n },\n };\n\n return store;\n}\n\n/**\n * The optimistic row a composer shows the instant Enter is pressed. It carries\n * the outbox's client key, which is the whole reason the confirmed row can find\n * and replace it rather than appearing beside it.\n */\nexport function optimisticMessage(args: {\n conversationId: ConversationId;\n senderId: UserId;\n organizationId: Message[\"organizationId\"];\n content: string;\n clientMessageId: ClientMessageId;\n kind?: Message[\"kind\"];\n replyToId?: MessageId | null;\n action?: Message[\"action\"];\n attachments?: readonly Message[\"attachments\"][number][];\n references?: readonly Message[\"references\"][number][];\n now?: () => number;\n}): Message {\n const at = new Date(args.now?.() ?? Date.now()).toISOString();\n return {\n // A temporary id that can never collide with a uuid from the database.\n id: `optimistic:${args.clientMessageId}` as MessageId,\n conversationId: args.conversationId,\n senderId: args.senderId,\n organizationId: args.organizationId,\n content: args.content,\n kind: args.kind ?? \"text\",\n deliveryState: \"sending\",\n replyToId: args.replyToId ?? null,\n clientMessageId: args.clientMessageId,\n createdAt: at,\n editedAt: null,\n deletedAt: null,\n deletedForEveryone: false,\n action: args.action ?? null,\n attachments: args.attachments ?? [],\n references: args.references ?? [],\n metadata: {},\n };\n}\n","/**\n * Display formatting. In the package because every consumer otherwise rebuilds\n * it slightly differently and the inbox and the thread disagree about what\n * \"yesterday\" means.\n *\n * Every function takes an explicit `now` so the tests do not depend on the\n * clock, and none of them touches `Intl` with an implicit locale — a package\n * that silently formats in the build machine's locale is a bug that only shows\n * up for users in other timezones.\n */\n\nimport type { Message, UserId, UserSummary } from \"./types\";\n\nconst MINUTE = 60_000;\nconst HOUR = 60 * MINUTE;\nconst DAY = 24 * HOUR;\n\nexport function isSameDay(a: Date, b: Date): boolean {\n return (\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n );\n}\n\n/** Compact stamp for a conversation row: `9:41 AM`, `Yesterday`, `Mar 4`. */\nexport function formatConversationTime(\n isoString: string | null,\n now: number = Date.now(),\n locale?: string,\n): string {\n if (isoString === null) return \"\";\n const parsed = Date.parse(isoString);\n if (!Number.isFinite(parsed)) return \"\";\n const then = new Date(parsed);\n const today = new Date(now);\n if (isSameDay(then, today)) {\n return then.toLocaleTimeString(locale, { hour: \"numeric\", minute: \"2-digit\" });\n }\n const yesterday = new Date(now - DAY);\n if (isSameDay(then, yesterday)) return \"Yesterday\";\n if (now - parsed < 7 * DAY) return then.toLocaleDateString(locale, { weekday: \"short\" });\n return then.toLocaleDateString(locale, { month: \"short\", day: \"numeric\" });\n}\n\n/** The stamp under a bubble. */\nexport function formatMessageTime(\n isoString: string,\n locale?: string,\n): string {\n const parsed = Date.parse(isoString);\n if (!Number.isFinite(parsed)) return \"\";\n return new Date(parsed).toLocaleTimeString(locale, {\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n}\n\n/** The separator between day groups in a thread. */\nexport function formatDateSeparator(\n isoString: string,\n now: number = Date.now(),\n locale?: string,\n): string {\n const parsed = Date.parse(isoString);\n if (!Number.isFinite(parsed)) return \"\";\n const then = new Date(parsed);\n const today = new Date(now);\n if (isSameDay(then, today)) return \"Today\";\n if (isSameDay(then, new Date(now - DAY))) return \"Yesterday\";\n if (then.getFullYear() === today.getFullYear()) {\n return then.toLocaleDateString(locale, { month: \"long\", day: \"numeric\" });\n }\n return then.toLocaleDateString(locale, {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n });\n}\n\nexport function getInitials(name: string): string {\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return \"?\";\n const first = parts[0]?.[0] ?? \"\";\n const last = parts.length > 1 ? (parts.at(-1)?.[0] ?? \"\") : \"\";\n return `${first}${last}`.toUpperCase() || \"?\";\n}\n\n/**\n * A stable palette index for an avatar with no image. Deterministic on the id,\n * so the same person is the same color on every device and every reload — a\n * random color per render is a surprisingly loud bug.\n */\nexport function avatarPaletteIndex(seed: string, buckets = 8): number {\n let hash = 0;\n for (let index = 0; index < seed.length; index += 1) {\n hash = (hash * 31 + seed.charCodeAt(index)) | 0;\n }\n return Math.abs(hash) % buckets;\n}\n\n/**\n * Group consecutive messages by the same sender within a window, the way every\n * good chat UI does — one avatar and one name per burst.\n */\nexport interface MessageGroup {\n readonly senderId: UserId;\n readonly messages: readonly Message[];\n readonly dateSeparator: string | null;\n}\n\nexport function groupMessages(\n messages: readonly Message[],\n args: { now?: number; windowMs?: number; locale?: string } = {},\n): readonly MessageGroup[] {\n const windowMs = args.windowMs ?? 5 * MINUTE;\n const now = args.now ?? Date.now();\n const groups: MessageGroup[] = [];\n let current: { senderId: UserId; messages: Message[]; dateSeparator: string | null } | null =\n null;\n let previousDay: string | null = null;\n\n for (const message of messages) {\n const day = message.createdAt.slice(0, 10);\n const startsNewDay = day !== previousDay;\n previousDay = day;\n\n const last = current?.messages.at(-1);\n const withinWindow =\n last !== undefined &&\n Math.abs(Date.parse(message.createdAt) - Date.parse(last.createdAt)) <= windowMs;\n\n if (\n current !== null &&\n current.senderId === message.senderId &&\n withinWindow &&\n !startsNewDay\n ) {\n current.messages.push(message);\n continue;\n }\n if (current !== null) groups.push(current);\n current = {\n senderId: message.senderId,\n messages: [message],\n dateSeparator: startsNewDay\n ? formatDateSeparator(message.createdAt, now, args.locale)\n : null,\n };\n }\n if (current !== null) groups.push(current);\n return groups;\n}\n\n/** \"Ana is typing…\" / \"Ana and Bo are typing…\" / \"3 people are typing…\" */\nexport function formatTypists(names: readonly string[]): string | null {\n if (names.length === 0) return null;\n if (names.length === 1) return `${names[0]} is typing…`;\n if (names.length === 2) return `${names[0]} and ${names[1]} are typing…`;\n return `${names.length} people are typing…`;\n}\n\nexport function participantNames(\n participants: readonly UserSummary[],\n excluding: UserId,\n): readonly string[] {\n return participants\n .filter((participant) => participant.userId !== excluding)\n .map((participant) => participant.displayName);\n}\n\n/** \"Active now\" / \"Active 5m ago\" — presence, said honestly. */\nexport function formatLastSeen(lastSeenMs: number | null, now: number = Date.now()): string {\n if (lastSeenMs === null) return \"\";\n const elapsed = now - lastSeenMs;\n if (elapsed < 2 * MINUTE) return \"Active now\";\n if (elapsed < HOUR) return `Active ${Math.round(elapsed / MINUTE)}m ago`;\n if (elapsed < DAY) return `Active ${Math.round(elapsed / HOUR)}h ago`;\n return `Active ${Math.round(elapsed / DAY)}d ago`;\n}\n","/**\n * THE DATA CONTRACT — the ONE place a messaging table or RPC name exists.\n *\n * Every name below was verified against Matrx Main (`https://db.matrxserver.com`)\n * on 2026-08-31, not guessed from a client. `matrx-dm` grew a parallel schema in\n * its own project (`public.conversations` / `messages` / `message_reactions`);\n * per R8 exactly ONE canonical schema survives and it is this one — the\n * platform-conventional `communication.dm_*` tables with explicit\n * `organization_id`, `version`, and soft-delete columns, behind auth-checked\n * SECURITY DEFINER RPCs.\n *\n * Four rules hold this file together:\n *\n * 1. **Org is explicit on every write (R5).** Not defaulted by a trigger we\n * hope fires, not inherited — passed, and refused in-package when absent.\n * 2. **Authorization is the database's job.** There is no permission branch in\n * this file. An RPC that says no is a `forbidden` MessagingError, never a\n * client-side re-decision.\n * 3. **Direct conversations are created ATOMICALLY, by RPC.** The banned\n * pattern — read `find_dm_direct_conversation`, then insert — races two\n * tabs into two conversations for the same pair. The RPC advisory-locks the\n * unordered pair. Nothing here may reintroduce the read-then-insert shape.\n * 4. **Pagination is keyset and terminated by a unique column.** Ordering by a\n * timestamp alone duplicates or skips rows whenever two share a millisecond\n * — which in a large org is every page.\n */\n\nimport { createReadCache, type ReadCache } from \"./cache\";\nimport { invalidResponse, MessagingError, normalizeMessagingError } from \"./errors\";\nimport { projectConversationSummary, projectMessage, projectUserSummary } from \"./projection\";\nimport type { SchemaLike, SupabaseLike } from \"./supabase-shape\";\nimport type {\n ClientMessageId,\n Conversation,\n ConversationCursor,\n ConversationId,\n ConversationSummary,\n DraftMessage,\n Message,\n MessageCursor,\n MessageId,\n MessagingIdentity,\n OrganizationId,\n Page,\n ParticipantRole,\n UserId,\n UserSummary,\n} from \"./types\";\n\n/** The schema. Messaging is never in `public`. */\nexport const MESSAGING_SCHEMA = \"communication\";\n\nexport const TABLES = {\n conversations: \"dm_conversations\",\n participants: \"dm_conversation_participants\",\n messages: \"dm_messages\",\n} as const;\n\nexport const RPCS = {\n /** Atomic direct-conversation creation. Advisory-locks the unordered pair. */\n getOrCreateDirect: \"dm_get_or_create_direct_conversation\",\n /** Conversation list + participants + last message + unread, keyset paged. */\n conversationsWithDetails: \"get_dm_conversations_with_details\",\n unreadCount: \"get_dm_unread_count\",\n userInfo: \"get_dm_user_info\",\n isParticipant: \"is_dm_participant\",\n} as const;\n\n/** Columns selected for a message. Explicit — `select(\"*\")` silently drifts. */\nconst MESSAGE_COLUMNS =\n \"id,conversation_id,sender_id,organization_id,content,message_type,status,\" +\n \"reply_to_id,client_message_id,action_data,media_url,media_thumbnail_url,\" +\n \"media_metadata,created_at,edited_at,deleted_at,deleted_for_everyone,metadata\";\n\nconst PARTICIPANT_COLUMNS =\n \"conversation_id,user_id,role,joined_at,last_read_at,is_muted,is_archived\";\n\nconst CONVERSATION_COLUMNS =\n \"id,type,group_name,group_image_url,created_by,organization_id,created_at,updated_at,metadata\";\n\n/**\n * The host's session source. A messaging read that lands between sign-in and\n * token arrival must not become a red error — it retries ONCE after the host\n * resolves a session, then reports `session-unavailable`.\n */\nexport type SessionResolver = () => Promise<unknown>;\n\nexport interface RepositoryOptions {\n client: SupabaseLike;\n identity: MessagingIdentity;\n /** Called once before a retry when a read fails with a missing session. */\n resolveSession?: SessionResolver | undefined;\n /** Profile-lookup cache TTL. Default 5 minutes (the frontend's proven value). */\n userTtlMs?: number | undefined;\n now?: (() => number) | undefined;\n}\n\nexport interface MessagingRepository {\n readonly identity: MessagingIdentity;\n listConversations(args?: {\n limit?: number;\n cursor?: ConversationCursor | null;\n }): Promise<Page<ConversationSummary, ConversationCursor>>;\n getConversation(id: ConversationId): Promise<Conversation>;\n listMessages(\n conversationId: ConversationId,\n args?: { limit?: number; cursor?: MessageCursor | null },\n ): Promise<Page<Message, MessageCursor>>;\n /** Messages created strictly after `since` — the reconnect backfill read. */\n messagesSince(conversationId: ConversationId, since: string): Promise<readonly Message[]>;\n getOrCreateDirectConversation(otherUserId: UserId): Promise<ConversationId>;\n createGroupConversation(args: {\n name: string;\n memberIds: readonly UserId[];\n }): Promise<ConversationId>;\n insertMessage(draft: DraftMessage, clientMessageId: ClientMessageId): Promise<Message>;\n editMessage(id: MessageId, content: string): Promise<Message>;\n deleteMessage(id: MessageId, forEveryone: boolean): Promise<void>;\n markRead(conversationId: ConversationId, at?: string): Promise<void>;\n setConversationFlags(\n conversationId: ConversationId,\n flags: { isMuted?: boolean; isArchived?: boolean },\n ): Promise<void>;\n addMembers(conversationId: ConversationId, memberIds: readonly UserId[]): Promise<void>;\n removeMember(conversationId: ConversationId, memberId: UserId): Promise<void>;\n setMemberRole(\n conversationId: ConversationId,\n memberId: UserId,\n role: ParticipantRole,\n ): Promise<void>;\n /** Cached + in-flight-deduped. N callers for one id make ONE request. */\n getUser(userId: UserId): Promise<UserSummary | null>;\n getUsers(userIds: readonly UserId[]): Promise<ReadonlyMap<UserId, UserSummary>>;\n searchMessages(args: {\n query: string;\n conversationId?: ConversationId | null;\n limit?: number;\n }): Promise<readonly Message[]>;\n invalidateUser(userId: UserId): void;\n}\n\nfunction requireOrg(organizationId: OrganizationId | undefined, operation: string): OrganizationId {\n if (typeof organizationId !== \"string\" || organizationId.length === 0) {\n throw new MessagingError(\n \"misconfigured\",\n `${operation}: no organization_id`,\n \"Every conversation and message write carries an explicit organization_id (R5). \" +\n \"Pass a real org on <MessagingProvider>; the package refuses the write rather \" +\n \"than letting an unscoped row reach the database.\",\n );\n }\n return organizationId;\n}\n\nexport function createMessagingRepository(\n options: RepositoryOptions,\n): MessagingRepository {\n const { client, identity } = options;\n const org = requireOrg(identity.organizationId, \"createMessagingRepository\");\n const userCache: ReadCache<UserSummary | null> = createReadCache({\n ttlMs: options.userTtlMs ?? 5 * 60_000,\n ...(options.now !== undefined ? { now: options.now } : {}),\n });\n\n const db = (): SchemaLike => client.schema(MESSAGING_SCHEMA);\n\n /**\n * THE ONE RETRY. A read that failed only because the session had not arrived\n * yet is retried once after the host resolves one. Everything else is thrown\n * as-is: retrying a `forbidden` is how a request storm is built.\n */\n async function withSessionRetry<T>(operation: string, run: () => Promise<T>): Promise<T> {\n try {\n return await run();\n } catch (error) {\n const normalized = normalizeMessagingError(error, operation);\n if (normalized.code !== \"session-unavailable\" || options.resolveSession === undefined) {\n throw normalized;\n }\n await options.resolveSession();\n try {\n return await run();\n } catch (retryError) {\n throw normalizeMessagingError(retryError, operation);\n }\n }\n }\n\n async function rpc<T>(fn: string, args: Record<string, unknown>, operation: string): Promise<T> {\n const { data, error } = await db().rpc<T>(fn, args);\n if (error !== null) throw normalizeMessagingError(error, operation);\n return data as T;\n }\n\n async function getUsers(\n userIds: readonly UserId[],\n ): Promise<ReadonlyMap<UserId, UserSummary>> {\n const unique = [...new Set(userIds)];\n const found = new Map<UserId, UserSummary>();\n // One failed profile must NOT fail the batch — a conversation row with one\n // unresolvable participant still renders; it just shows that participant by\n // id. Failing the whole list here is how one bad row blanks an inbox.\n const results = await Promise.all(\n unique.map(async (id) => {\n try {\n return await repository.getUser(id);\n } catch {\n return null;\n }\n }),\n );\n results.forEach((summary) => {\n if (summary !== null) found.set(summary.userId, summary);\n });\n return found;\n }\n\n const repository: MessagingRepository = {\n identity,\n\n async listConversations(args = {}) {\n const limit = args.limit ?? 30;\n const operation = \"listConversations\";\n // Ask for one MORE than requested: `hasMore` is then a fact about the\n // database, not a guess from \"we got a full page\" (which is wrong exactly\n // when the last page is exactly full).\n const rows = await withSessionRetry(operation, () =>\n rpc<readonly Record<string, unknown>[] | null>(\n RPCS.conversationsWithDetails,\n {\n p_user_id: identity.userId,\n p_limit: limit + 1,\n p_before_sort_at: args.cursor?.beforeSortAt ?? null,\n p_before_conversation_id: args.cursor?.beforeConversationId ?? null,\n },\n operation,\n ),\n );\n if (rows !== null && !Array.isArray(rows)) {\n throw invalidResponse(operation, `${RPCS.conversationsWithDetails} did not return rows`);\n }\n const projected = (rows ?? []).map((row) =>\n projectConversationSummary(row, identity.userId, org),\n );\n const hasMore = projected.length > limit;\n const items = hasMore ? projected.slice(0, limit) : projected;\n const last = items.at(-1);\n return {\n items,\n hasMore,\n nextCursor:\n hasMore && last !== undefined\n ? { beforeSortAt: last.sortAt, beforeConversationId: last.conversation.id }\n : null,\n };\n },\n\n async getConversation(id) {\n const operation = \"getConversation\";\n const { data, error } = await withSessionRetry(operation, async () =>\n db()\n .from<Record<string, unknown>>(TABLES.conversations)\n .select(CONVERSATION_COLUMNS)\n .eq(\"id\", id)\n .single(),\n );\n if (error !== null) throw normalizeMessagingError(error, operation);\n if (data === null) throw invalidResponse(operation, `conversation ${id} returned no row`);\n const summary = projectConversationSummary(\n {\n conversation_id: data[\"id\"],\n conversation_type: data[\"type\"],\n group_name: data[\"group_name\"],\n group_image_url: data[\"group_image_url\"],\n conversation_created_at: data[\"created_at\"],\n conversation_updated_at: data[\"updated_at\"],\n organization_id: data[\"organization_id\"],\n created_by: data[\"created_by\"],\n metadata: data[\"metadata\"],\n participants: [],\n unread_count: 0,\n },\n identity.userId,\n org,\n );\n return summary.conversation;\n },\n\n async listMessages(conversationId, args = {}) {\n const limit = args.limit ?? 50;\n const operation = \"listMessages\";\n // Newest-first with a UNIQUE tiebreaker, then reversed for display.\n // `or(...)` expresses the keyset predicate PostgREST has no tuple syntax\n // for: (created_at < c) OR (created_at = c AND id < i).\n const rows = await withSessionRetry(operation, async () => {\n let query = db()\n .from<Record<string, unknown>>(TABLES.messages)\n .select(MESSAGE_COLUMNS)\n .eq(\"conversation_id\", conversationId);\n const cursor = args.cursor;\n if (cursor != null) {\n query = query.or(\n `created_at.lt.${cursor.beforeCreatedAt},` +\n `and(created_at.eq.${cursor.beforeCreatedAt},id.lt.${cursor.beforeMessageId})`,\n );\n }\n const { data, error } = await query\n .order(\"created_at\", { ascending: false })\n .order(\"id\", { ascending: false })\n .limit(limit + 1);\n if (error !== null) throw normalizeMessagingError(error, operation);\n return data ?? [];\n });\n\n const hasMore = rows.length > limit;\n const page = hasMore ? rows.slice(0, limit) : rows;\n const oldest = page.at(-1);\n const items = page.map((row) => projectMessage(row, org)).reverse();\n return {\n items,\n hasMore,\n nextCursor:\n hasMore && oldest !== undefined\n ? {\n beforeCreatedAt: String(oldest[\"created_at\"]),\n beforeMessageId: String(oldest[\"id\"]) as MessageId,\n }\n : null,\n };\n },\n\n async messagesSince(conversationId, since) {\n const operation = \"messagesSince\";\n const rows = await withSessionRetry(operation, async () => {\n const { data, error } = await db()\n .from<Record<string, unknown>>(TABLES.messages)\n .select(MESSAGE_COLUMNS)\n .eq(\"conversation_id\", conversationId)\n .gt(\"created_at\", since)\n .order(\"created_at\", { ascending: true })\n .limit(500);\n if (error !== null) throw normalizeMessagingError(error, operation);\n return data ?? [];\n });\n return rows.map((row) => projectMessage(row, org));\n },\n\n async getOrCreateDirectConversation(otherUserId) {\n const operation = \"getOrCreateDirectConversation\";\n const id = await withSessionRetry(operation, () =>\n rpc<string | null>(\n RPCS.getOrCreateDirect,\n {\n // The RPC's own guard requires an `authenticated` caller to pass\n // THEMSELVES as user1; passing them in the other slot is a denial,\n // not a preference.\n p_user1_id: identity.userId,\n p_user2_id: otherUserId,\n p_organization_id: org,\n },\n operation,\n ),\n );\n if (typeof id !== \"string\" || id.length === 0) {\n throw invalidResponse(operation, `${RPCS.getOrCreateDirect} returned no conversation id`);\n }\n return id as ConversationId;\n },\n\n async createGroupConversation({ name, memberIds }) {\n const operation = \"createGroupConversation\";\n const { data, error } = await withSessionRetry(operation, async () =>\n db()\n .from<Record<string, unknown>>(TABLES.conversations)\n .insert({\n type: \"group\",\n group_name: name,\n organization_id: org,\n created_by: identity.userId,\n })\n .select(\"id\")\n .single(),\n );\n if (error !== null) throw normalizeMessagingError(error, operation);\n const conversationId = data?.[\"id\"];\n if (typeof conversationId !== \"string\") {\n throw invalidResponse(operation, \"insert returned no conversation id\");\n }\n const members = [...new Set<UserId>([identity.userId, ...memberIds])];\n const { error: memberError } = await db()\n .from(TABLES.participants)\n .insert(\n members.map((userId) => ({\n conversation_id: conversationId,\n user_id: userId,\n role: userId === identity.userId ? \"owner\" : \"member\",\n organization_id: org,\n created_by: identity.userId,\n })),\n );\n if (memberError !== null) throw normalizeMessagingError(memberError, operation);\n return conversationId as ConversationId;\n },\n\n async insertMessage(draft, clientMessageId) {\n const operation = \"insertMessage\";\n const { data, error } = await db()\n .from<Record<string, unknown>>(TABLES.messages)\n .insert({\n conversation_id: draft.conversationId,\n sender_id: identity.userId,\n organization_id: org,\n created_by: identity.userId,\n content: draft.content,\n message_type: draft.kind ?? \"text\",\n status: \"sent\",\n reply_to_id: draft.replyToId ?? null,\n // THE IDEMPOTENCY KEY. It is what makes a retried send exactly-once\n // and what lets a receiver collapse the optimistic bubble with the\n // confirmed row instead of showing the message twice.\n client_message_id: clientMessageId,\n action_data: draft.action ?? null,\n metadata: {\n ...(draft.metadata ?? {}),\n ...(draft.attachments !== undefined && draft.attachments.length > 0\n ? { attachments: draft.attachments }\n : {}),\n ...(draft.references !== undefined && draft.references.length > 0\n ? { references: draft.references }\n : {}),\n },\n })\n .select(MESSAGE_COLUMNS)\n .single();\n if (error !== null) throw normalizeMessagingError(error, operation);\n if (data === null) throw invalidResponse(operation, \"insert returned no row\");\n return projectMessage(data, org);\n },\n\n async editMessage(id, content) {\n const operation = \"editMessage\";\n const { data, error } = await db()\n .from<Record<string, unknown>>(TABLES.messages)\n .update({\n content,\n edited_at: new Date().toISOString(),\n updated_by: identity.userId,\n })\n .eq(\"id\", id)\n .select(MESSAGE_COLUMNS)\n .single();\n if (error !== null) throw normalizeMessagingError(error, operation);\n if (data === null) throw invalidResponse(operation, \"update returned no row\");\n return projectMessage(data, org);\n },\n\n async deleteMessage(id, forEveryone) {\n const operation = \"deleteMessage\";\n // SOFT delete, always. A hard delete loses the audit row and breaks every\n // reply that points at it.\n const { error } = await db()\n .from(TABLES.messages)\n .update({\n deleted_at: new Date().toISOString(),\n deleted_for_everyone: forEveryone,\n updated_by: identity.userId,\n })\n .eq(\"id\", id);\n if (error !== null) throw normalizeMessagingError(error, operation);\n },\n\n async markRead(conversationId, at) {\n const operation = \"markRead\";\n const { error } = await db()\n .from(TABLES.participants)\n .update({ last_read_at: at ?? new Date().toISOString(), updated_by: identity.userId })\n .eq(\"conversation_id\", conversationId)\n .eq(\"user_id\", identity.userId);\n if (error !== null) throw normalizeMessagingError(error, operation);\n },\n\n async setConversationFlags(conversationId, flags) {\n const operation = \"setConversationFlags\";\n const patch: Record<string, unknown> = { updated_by: identity.userId };\n if (flags.isMuted !== undefined) patch[\"is_muted\"] = flags.isMuted;\n if (flags.isArchived !== undefined) patch[\"is_archived\"] = flags.isArchived;\n const { error } = await db()\n .from(TABLES.participants)\n .update(patch)\n .eq(\"conversation_id\", conversationId)\n .eq(\"user_id\", identity.userId);\n if (error !== null) throw normalizeMessagingError(error, operation);\n },\n\n async addMembers(conversationId, memberIds) {\n const operation = \"addMembers\";\n const { error } = await db()\n .from(TABLES.participants)\n .insert(\n memberIds.map((userId) => ({\n conversation_id: conversationId,\n user_id: userId,\n role: \"member\",\n organization_id: org,\n created_by: identity.userId,\n })),\n );\n if (error !== null) throw normalizeMessagingError(error, operation);\n },\n\n async removeMember(conversationId, memberId) {\n const operation = \"removeMember\";\n const { error } = await db()\n .from(TABLES.participants)\n .update({ deleted_at: new Date().toISOString(), updated_by: identity.userId })\n .eq(\"conversation_id\", conversationId)\n .eq(\"user_id\", memberId);\n if (error !== null) throw normalizeMessagingError(error, operation);\n },\n\n async setMemberRole(conversationId, memberId, role) {\n const operation = \"setMemberRole\";\n const { error } = await db()\n .from(TABLES.participants)\n .update({ role, updated_by: identity.userId })\n .eq(\"conversation_id\", conversationId)\n .eq(\"user_id\", memberId);\n if (error !== null) throw normalizeMessagingError(error, operation);\n },\n\n getUser(userId) {\n const operation = \"getUser\";\n return userCache.read(userId, async () => {\n const rows = await withSessionRetry(operation, () =>\n rpc<readonly Record<string, unknown>[] | null>(\n RPCS.userInfo,\n { p_user_id: userId },\n operation,\n ),\n );\n const row = Array.isArray(rows) ? rows[0] : null;\n return row === undefined || row === null ? null : projectUserSummary(row);\n });\n },\n\n getUsers,\n\n async searchMessages({ query, conversationId = null, limit = 50 }) {\n const operation = \"searchMessages\";\n const trimmed = query.trim();\n if (trimmed.length === 0) return [];\n const rows = await withSessionRetry(operation, async () => {\n let base = db()\n .from<Record<string, unknown>>(TABLES.messages)\n .select(MESSAGE_COLUMNS)\n .is(\"deleted_at\", null);\n if (conversationId !== null) base = base.eq(\"conversation_id\", conversationId);\n // PostgREST pattern matching: `%`, `,`, `(`, `)` and `*` are filter\n // GRAMMAR, not text. They are stripped rather than escaped — a search\n // box must never be able to author a filter.\n const safe = trimmed.replace(/[%,()*]/g, \" \").trim();\n const { data, error } = await base\n .or(`content.ilike.*${safe}*`)\n .order(\"created_at\", { ascending: false })\n .limit(limit);\n if (error !== null) throw normalizeMessagingError(error, operation);\n return data ?? [];\n });\n return rows.map((row) => projectMessage(row, org));\n },\n\n invalidateUser(userId) {\n userCache.invalidate(userId);\n },\n };\n\n return repository;\n}\n","/**\n * THE MESSAGING DOMAIN TYPES.\n *\n * Ported from `matrx-frontend/features/messaging/types.ts` with the coupling\n * seams inverted: no imported host types, no app singletons. The DB-shaped\n * types mirror `communication.dm_*` exactly (snake_case, nullable where the\n * column is nullable) so a row read from PostgREST IS one of these without a\n * mapping step that can silently drop a column.\n *\n * Strictness posture (C22): identity-bearing values are BRANDED, invalid states\n * are unrepresentable (a `sending` message has a `clientMessageId`; a `failed`\n * one carries a reason), and no surface here is `any`. The one honest `unknown`\n * is the JSON boundary — `metadata` and `action_data` are host/DB-generated\n * shapes this package does not own (the data 0.2.1 `Json = unknown` lesson).\n */\n\n/** Nominal id types. A conversation id can never be passed where a user id goes. */\ndeclare const brand: unique symbol;\ntype Brand<T, B extends string> = T & { readonly [brand]: B };\n\nexport type ConversationId = Brand<string, \"ConversationId\">;\nexport type MessageId = Brand<string, \"MessageId\">;\nexport type UserId = Brand<string, \"UserId\">;\nexport type OrganizationId = Brand<string, \"OrganizationId\">;\n/** The client-minted idempotency key that makes a send exactly-once end to end. */\nexport type ClientMessageId = Brand<string, \"ClientMessageId\">;\n\nexport const asConversationId = (value: string): ConversationId => value as ConversationId;\nexport const asMessageId = (value: string): MessageId => value as MessageId;\nexport const asUserId = (value: string): UserId => value as UserId;\nexport const asOrganizationId = (value: string): OrganizationId => value as OrganizationId;\nexport const asClientMessageId = (value: string): ClientMessageId =>\n value as ClientMessageId;\n\nexport type JsonValue = unknown;\nexport type JsonObject = Readonly<Record<string, JsonValue>>;\n\nexport type ConversationType = \"direct\" | \"group\" | \"org\";\nexport type ParticipantRole = \"owner\" | \"admin\" | \"member\";\nexport type MessageKind = \"text\" | \"image\" | \"video\" | \"audio\" | \"file\" | \"system\" | \"action\";\n\n/**\n * The delivery ladder. `sending` and `failed` exist only client-side: they are\n * the outbox's states, and the DB's `status` column never holds them.\n */\nexport type DeliveryState = \"sending\" | \"sent\" | \"delivered\" | \"read\" | \"failed\";\n\nexport interface Conversation {\n readonly id: ConversationId;\n readonly type: ConversationType;\n readonly groupName: string | null;\n readonly groupImageUrl: string | null;\n readonly createdBy: UserId | null;\n readonly organizationId: OrganizationId;\n readonly createdAt: string;\n readonly updatedAt: string;\n readonly metadata: JsonObject;\n}\n\nexport interface Participant {\n readonly conversationId: ConversationId;\n readonly userId: UserId;\n readonly role: ParticipantRole;\n readonly joinedAt: string | null;\n readonly lastReadAt: string | null;\n readonly isMuted: boolean;\n readonly isArchived: boolean;\n}\n\nexport interface UserSummary {\n readonly userId: UserId;\n readonly displayName: string;\n readonly email: string | null;\n readonly avatarUrl: string | null;\n /** True when this participant is an AI agent rather than a person (R4). */\n readonly isAgent: boolean;\n}\n\n/**\n * A structured action carried by a message. THE SEAM other packages extend —\n * `@ai-matrx/meet` puts a call invitation here rather than inventing a message\n * type (D1).\n *\n * `version` is not decoration: a renderer that does not understand a version\n * renders NOTHING rather than guessing, which is what lets a new sender ship\n * before every reader has caught up.\n */\nexport interface MessageAction<TKind extends string = string, TPayload = JsonObject> {\n readonly kind: TKind;\n readonly version: number;\n readonly payload: TPayload;\n}\n\n/**\n * A typed reference to a platform entity, rendered as a live openable card.\n * NO DEAD ENDS: everything a message names must open, so a reference always\n * carries enough to resolve — never a bare label.\n */\nexport interface MatrxReference {\n readonly entityType: string;\n readonly entityId: string;\n readonly label: string;\n readonly href?: string | undefined;\n}\n\nexport interface Attachment {\n /** A DURABLE file ref, never a signed URL. Signed URLs expire; identities do not. */\n readonly fileId: string;\n readonly fileName: string;\n readonly mimeType: string | null;\n readonly sizeBytes: number | null;\n readonly width: number | null;\n readonly height: number | null;\n}\n\nexport interface Message {\n readonly id: MessageId;\n readonly conversationId: ConversationId;\n readonly senderId: UserId;\n readonly organizationId: OrganizationId;\n readonly content: string;\n readonly kind: MessageKind;\n readonly deliveryState: DeliveryState;\n readonly replyToId: MessageId | null;\n readonly clientMessageId: ClientMessageId | null;\n readonly createdAt: string;\n readonly editedAt: string | null;\n readonly deletedAt: string | null;\n readonly deletedForEveryone: boolean;\n readonly action: MessageAction | null;\n readonly attachments: readonly Attachment[];\n readonly references: readonly MatrxReference[];\n readonly metadata: JsonObject;\n /** Set only while this message is in the outbox and its last send failed. */\n readonly failureReason?: string | undefined;\n}\n\nexport interface ConversationSummary {\n readonly conversation: Conversation;\n readonly participants: readonly UserSummary[];\n readonly lastMessageContent: string | null;\n readonly lastMessageSenderId: UserId | null;\n readonly lastMessageAt: string | null;\n readonly unreadCount: number;\n readonly isMuted: boolean;\n readonly isArchived: boolean;\n /** Resolved for display: the group name, or the other participant's name. */\n readonly displayName: string;\n readonly displayImageUrl: string | null;\n /** The keyset sort value. Pagination cursors are built from this + `id`. */\n readonly sortAt: string;\n}\n\n/**\n * THE PAGINATION CURSOR. Stable ordering is terminated by a UNIQUE column\n * (R5 scale honesty): ordering by a timestamp alone silently duplicates or\n * skips rows whenever two rows share a millisecond, which in a large org is\n * every page.\n */\nexport interface ConversationCursor {\n readonly beforeSortAt: string;\n readonly beforeConversationId: ConversationId;\n}\n\nexport interface MessageCursor {\n readonly beforeCreatedAt: string;\n readonly beforeMessageId: MessageId;\n}\n\nexport interface Page<TItem, TCursor> {\n readonly items: readonly TItem[];\n readonly nextCursor: TCursor | null;\n readonly hasMore: boolean;\n}\n\n/** What the host injects. Identity ONLY — every hard part is in the package. */\nexport interface MessagingIdentity {\n readonly userId: UserId;\n readonly organizationId: OrganizationId;\n}\n\nexport interface DraftMessage {\n readonly conversationId: ConversationId;\n readonly content: string;\n readonly kind?: MessageKind | undefined;\n readonly replyToId?: MessageId | undefined;\n readonly action?: MessageAction | undefined;\n readonly attachments?: readonly Attachment[] | undefined;\n readonly references?: readonly MatrxReference[] | undefined;\n readonly metadata?: JsonObject | undefined;\n}\n"],"mappings":"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","names":["import_realtime","import_realtime","held"]}