@ai-matrx/messaging 0.10.0 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { ReactNode, ComponentType, SVGProps } from 'react';
1
+ import { ReactNode, ComponentType, KeyboardEvent, SVGProps } from 'react';
2
2
  import { MatrxJsonObject, MatrxTransport } from '@ai-matrx/agents/matrx';
3
3
  import { RealtimeClientLike, RealtimeManager } from '@ai-matrx/realtime';
4
4
 
@@ -331,7 +331,13 @@ interface MessagingAiOptions {
331
331
  transport: MatrxTransport;
332
332
  organizationId: string;
333
333
  agents: MessagingAgents;
334
- /** Stable source slugs so server-side analytics can see where a run came from. */
334
+ /**
335
+ * The producer slugs stamped on every run. 🚨 The AI Matrx server keeps a
336
+ * REGISTER of legal producers and refuses an unregistered one with a 422, so
337
+ * the defaults below are placeholders that will NOT survive a real server —
338
+ * only the host knows its registered slugs. Not analytics garnish: an
339
+ * unstamped or unregistered run is a refused run.
340
+ */
335
341
  sourceApp?: string | undefined;
336
342
  sourceFeature?: string | undefined;
337
343
  /** Cap on how much transcript is sent. Default 200 messages. */
@@ -987,6 +993,15 @@ interface MessagingProviderProps {
987
993
  * number so an organization can decide.
988
994
  */
989
995
  maxTranscriptMessages?: number | undefined;
996
+ /**
997
+ * 🚨 REQUIRED IN PRACTICE FOR AI. The producer slugs stamped on every AI run.
998
+ * The AI Matrx server keeps a REGISTER of legal producers and REFUSES an
999
+ * unregistered one with a 422 — so this package's placeholder defaults
1000
+ * (`ai-matrx` / `messaging.<capability>`) fail every call against a real
1001
+ * server. Only the host knows its registered slugs; pass them.
1002
+ */
1003
+ sourceApp?: string | undefined;
1004
+ sourceFeature?: string | undefined;
990
1005
  /** Handlers for actionable messages. Registered once per handler identity. */
991
1006
  actions?: readonly ActionHandler<never>[] | undefined;
992
1007
  /**
@@ -1142,6 +1157,21 @@ interface ConversationViewProps {
1142
1157
  conversationId: ConversationId | null;
1143
1158
  onBack?: () => void;
1144
1159
  className?: string;
1160
+ /** Hide package chrome when a containing workspace already owns the title. */
1161
+ showHeader?: boolean;
1162
+ /** Hide conversation AI controls on a surface that deliberately does not offer them. */
1163
+ showAi?: boolean;
1164
+ /** Let a host inject its canonical textarea chrome without taking composer behavior. */
1165
+ renderComposerInput?: (props: ComposerInputRenderProps) => ReactNode;
1166
+ }
1167
+ interface ComposerInputRenderProps {
1168
+ readonly value: string;
1169
+ readonly disabled: boolean;
1170
+ readonly canSend: boolean;
1171
+ readonly placeholder: string;
1172
+ readonly onChange: (value: string) => void;
1173
+ readonly onKeyDown: (event: KeyboardEvent<HTMLTextAreaElement>) => void;
1174
+ readonly onSubmit: () => void;
1145
1175
  }
1146
1176
  declare function ConversationView(props: ConversationViewProps): ReactNode;
1147
1177
  declare function MessageBubble(props: {
@@ -1270,6 +1300,8 @@ interface ActorPresentation {
1270
1300
  /** The audit principal. Always the row's `sender_id`. Never rewritten. */
1271
1301
  readonly principalUserId: Message["senderId"];
1272
1302
  }
1303
+ /** Stable presentation identity for grouping adjacent messages in the UI. */
1304
+ declare function effectiveActorKey(message: Message): string;
1273
1305
  declare function resolveActor(message: Message, sender: UserSummary | null): ActorPresentation;
1274
1306
 
1275
1307
  /**
@@ -1419,6 +1451,8 @@ declare function groupMessages(messages: readonly Message[], args?: {
1419
1451
  now?: number;
1420
1452
  windowMs?: number;
1421
1453
  locale?: string;
1454
+ /** Split audit-principal rows by their effective actor presentation. */
1455
+ actorKey?: (message: Message) => string;
1422
1456
  }): readonly MessageGroup[];
1423
1457
  /** "Ana is typing…" / "Ana and Bo are typing…" / "3 people are typing…" */
1424
1458
  declare function formatTypists(names: readonly string[]): string | null;
@@ -1504,4 +1538,4 @@ declare function summarizeText(content: string, maxLength?: number): string;
1504
1538
  /** Serialize picked references into a fence the platform's other readers accept. */
1505
1539
  declare function composeFence(references: readonly MatrxReference[]): string;
1506
1540
 
1507
- export { type ActionChoice, type ActionContext, type ActionHandler, type ActionOutcome, type ActionReceipt, type ActionRegistry, type ActorPresentation, AgentTag, type AiCallArgs, type AiCapability, type AiResult, AlertIcon, type Attachment, Avatar, BotIcon, CheckIcon, ChevronLeftIcon, type ClientMessageId, ClockIcon, CloseIcon, Composer, type Conversation, type ConversationCursor, type ConversationId, ConversationList, type ConversationListProps, type ConversationRowWrapperProps, ConversationSkeleton, type ConversationSummary, type ConversationThread, type ConversationType, ConversationView, type ConversationViewProps, type DeliveryState, DeliveryTick, DoubleCheckIcon, type DraftMessage, EmptyState, type EngineDiagnostic, type IncomingMessageContext, type JsonObject, type JsonValue, LinkIcon, MESSAGING_EVENTS, MESSAGING_RPC_SCHEMA, MESSAGING_SCHEMA, type MatrxReference, type Message, type MessageAction, MessageActionChips, type MessageActionRenderProps, type MessageActionRenderer, MessageBubble, type MessageCursor, type MessageGroup, type MessageId, type MessageKind, type MessageWrapperProps, type MessagingAgentIdentity, type MessagingAgents, type MessagingAi, type MessagingAiOptions, type MessagingEngine, type MessagingEngineOptions, MessagingError, type MessagingErrorCode, type MessagingHost, type MessagingIdentity, MessagingInbox, type MessagingInboxProps, MessagingProvider, type MessagingProviderProps, type MessagingRepository, type MessagingSnapshot, type MessagingStore, type MessagingSupabaseClient, type MessagingSupabaseInternal, type OrganizationId, type Outbox, type OutboxEntry, type OutboxOptions, type OutboxStorage, type Page, PaperclipIcon, type Participant, type ParticipantRole, PlusIcon, type PostgrestFilterLike, type PostgrestLikeResponse, type PostgrestTableLike, RPCS, type ReadCache, type ReadCacheOptions, ReferenceCard, type ReferenceRenderer, ReplyIcon, type RepositoryOptions, type SchemaLike, SearchIcon, SendIcon, type SessionResolver, SparklesIcon, type SupabaseLike, TABLES, type TextSegment, TypingDots, type UseComposerResult, type UseConversationResult, type UseConversationsResult, type UseMessageActionResult, type UseMessagingAiResult, type UseTypistsResult, type UserId, type UserSummary, UsersIcon, asClientMessageId, asConversationId, asMessageId, asOrganizationId, asUserId, composeFence, conversationTopic, createActionRegistry, createMemoryOutboxStorage, createMessagingAi, createMessagingEngine, createMessagingRepository, createMessagingStore, createOutbox, createReadCache, createWebOutboxStorage, extractReferences, formatConversationTime, formatDateSeparator, formatLastSeen, formatMessageTime, formatTypists, groupMessages, inboxTopic, invalidResponse, isSameDay, messagingClientId, normalizeMessagingError, optimisticMessage, participantNames, projectConversationSummary, projectMessage, projectMessageAction, projectParticipantRole, projectUserSummary, resolveActor, splitText, summarizeText, unreadCutoff, useComposer, useConversation, useConversations, useMessageAction, useMessagingAi, useMessagingHost, useMessagingSnapshot, useOnlineUserIds, useRequiredMessagingHost, useTypists };
1541
+ export { type ActionChoice, type ActionContext, type ActionHandler, type ActionOutcome, type ActionReceipt, type ActionRegistry, type ActorPresentation, AgentTag, type AiCallArgs, type AiCapability, type AiResult, AlertIcon, type Attachment, Avatar, BotIcon, CheckIcon, ChevronLeftIcon, type ClientMessageId, ClockIcon, CloseIcon, Composer, type ComposerInputRenderProps, type Conversation, type ConversationCursor, type ConversationId, ConversationList, type ConversationListProps, type ConversationRowWrapperProps, ConversationSkeleton, type ConversationSummary, type ConversationThread, type ConversationType, ConversationView, type ConversationViewProps, type DeliveryState, DeliveryTick, DoubleCheckIcon, type DraftMessage, EmptyState, type EngineDiagnostic, type IncomingMessageContext, type JsonObject, type JsonValue, LinkIcon, MESSAGING_EVENTS, MESSAGING_RPC_SCHEMA, MESSAGING_SCHEMA, type MatrxReference, type Message, type MessageAction, MessageActionChips, type MessageActionRenderProps, type MessageActionRenderer, MessageBubble, type MessageCursor, type MessageGroup, type MessageId, type MessageKind, type MessageWrapperProps, type MessagingAgentIdentity, type MessagingAgents, type MessagingAi, type MessagingAiOptions, type MessagingEngine, type MessagingEngineOptions, MessagingError, type MessagingErrorCode, type MessagingHost, type MessagingIdentity, MessagingInbox, type MessagingInboxProps, MessagingProvider, type MessagingProviderProps, type MessagingRepository, type MessagingSnapshot, type MessagingStore, type MessagingSupabaseClient, type MessagingSupabaseInternal, type OrganizationId, type Outbox, type OutboxEntry, type OutboxOptions, type OutboxStorage, type Page, PaperclipIcon, type Participant, type ParticipantRole, PlusIcon, type PostgrestFilterLike, type PostgrestLikeResponse, type PostgrestTableLike, RPCS, type ReadCache, type ReadCacheOptions, ReferenceCard, type ReferenceRenderer, ReplyIcon, type RepositoryOptions, type SchemaLike, SearchIcon, SendIcon, type SessionResolver, SparklesIcon, type SupabaseLike, TABLES, type TextSegment, TypingDots, type UseComposerResult, type UseConversationResult, type UseConversationsResult, type UseMessageActionResult, type UseMessagingAiResult, type UseTypistsResult, type UserId, type UserSummary, UsersIcon, asClientMessageId, asConversationId, asMessageId, asOrganizationId, asUserId, composeFence, conversationTopic, createActionRegistry, createMemoryOutboxStorage, createMessagingAi, createMessagingEngine, createMessagingRepository, createMessagingStore, createOutbox, createReadCache, createWebOutboxStorage, effectiveActorKey, extractReferences, formatConversationTime, formatDateSeparator, formatLastSeen, formatMessageTime, formatTypists, groupMessages, inboxTopic, invalidResponse, isSameDay, messagingClientId, normalizeMessagingError, optimisticMessage, participantNames, projectConversationSummary, projectMessage, projectMessageAction, projectParticipantRole, projectUserSummary, resolveActor, splitText, summarizeText, unreadCutoff, useComposer, useConversation, useConversations, useMessageAction, useMessagingAi, useMessagingHost, useMessagingSnapshot, useOnlineUserIds, useRequiredMessagingHost, useTypists };
package/dist/react.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ReactNode, ComponentType, SVGProps } from 'react';
1
+ import { ReactNode, ComponentType, KeyboardEvent, SVGProps } from 'react';
2
2
  import { MatrxJsonObject, MatrxTransport } from '@ai-matrx/agents/matrx';
3
3
  import { RealtimeClientLike, RealtimeManager } from '@ai-matrx/realtime';
4
4
 
@@ -331,7 +331,13 @@ interface MessagingAiOptions {
331
331
  transport: MatrxTransport;
332
332
  organizationId: string;
333
333
  agents: MessagingAgents;
334
- /** Stable source slugs so server-side analytics can see where a run came from. */
334
+ /**
335
+ * The producer slugs stamped on every run. 🚨 The AI Matrx server keeps a
336
+ * REGISTER of legal producers and refuses an unregistered one with a 422, so
337
+ * the defaults below are placeholders that will NOT survive a real server —
338
+ * only the host knows its registered slugs. Not analytics garnish: an
339
+ * unstamped or unregistered run is a refused run.
340
+ */
335
341
  sourceApp?: string | undefined;
336
342
  sourceFeature?: string | undefined;
337
343
  /** Cap on how much transcript is sent. Default 200 messages. */
@@ -987,6 +993,15 @@ interface MessagingProviderProps {
987
993
  * number so an organization can decide.
988
994
  */
989
995
  maxTranscriptMessages?: number | undefined;
996
+ /**
997
+ * 🚨 REQUIRED IN PRACTICE FOR AI. The producer slugs stamped on every AI run.
998
+ * The AI Matrx server keeps a REGISTER of legal producers and REFUSES an
999
+ * unregistered one with a 422 — so this package's placeholder defaults
1000
+ * (`ai-matrx` / `messaging.<capability>`) fail every call against a real
1001
+ * server. Only the host knows its registered slugs; pass them.
1002
+ */
1003
+ sourceApp?: string | undefined;
1004
+ sourceFeature?: string | undefined;
990
1005
  /** Handlers for actionable messages. Registered once per handler identity. */
991
1006
  actions?: readonly ActionHandler<never>[] | undefined;
992
1007
  /**
@@ -1142,6 +1157,21 @@ interface ConversationViewProps {
1142
1157
  conversationId: ConversationId | null;
1143
1158
  onBack?: () => void;
1144
1159
  className?: string;
1160
+ /** Hide package chrome when a containing workspace already owns the title. */
1161
+ showHeader?: boolean;
1162
+ /** Hide conversation AI controls on a surface that deliberately does not offer them. */
1163
+ showAi?: boolean;
1164
+ /** Let a host inject its canonical textarea chrome without taking composer behavior. */
1165
+ renderComposerInput?: (props: ComposerInputRenderProps) => ReactNode;
1166
+ }
1167
+ interface ComposerInputRenderProps {
1168
+ readonly value: string;
1169
+ readonly disabled: boolean;
1170
+ readonly canSend: boolean;
1171
+ readonly placeholder: string;
1172
+ readonly onChange: (value: string) => void;
1173
+ readonly onKeyDown: (event: KeyboardEvent<HTMLTextAreaElement>) => void;
1174
+ readonly onSubmit: () => void;
1145
1175
  }
1146
1176
  declare function ConversationView(props: ConversationViewProps): ReactNode;
1147
1177
  declare function MessageBubble(props: {
@@ -1270,6 +1300,8 @@ interface ActorPresentation {
1270
1300
  /** The audit principal. Always the row's `sender_id`. Never rewritten. */
1271
1301
  readonly principalUserId: Message["senderId"];
1272
1302
  }
1303
+ /** Stable presentation identity for grouping adjacent messages in the UI. */
1304
+ declare function effectiveActorKey(message: Message): string;
1273
1305
  declare function resolveActor(message: Message, sender: UserSummary | null): ActorPresentation;
1274
1306
 
1275
1307
  /**
@@ -1419,6 +1451,8 @@ declare function groupMessages(messages: readonly Message[], args?: {
1419
1451
  now?: number;
1420
1452
  windowMs?: number;
1421
1453
  locale?: string;
1454
+ /** Split audit-principal rows by their effective actor presentation. */
1455
+ actorKey?: (message: Message) => string;
1422
1456
  }): readonly MessageGroup[];
1423
1457
  /** "Ana is typing…" / "Ana and Bo are typing…" / "3 people are typing…" */
1424
1458
  declare function formatTypists(names: readonly string[]): string | null;
@@ -1504,4 +1538,4 @@ declare function summarizeText(content: string, maxLength?: number): string;
1504
1538
  /** Serialize picked references into a fence the platform's other readers accept. */
1505
1539
  declare function composeFence(references: readonly MatrxReference[]): string;
1506
1540
 
1507
- export { type ActionChoice, type ActionContext, type ActionHandler, type ActionOutcome, type ActionReceipt, type ActionRegistry, type ActorPresentation, AgentTag, type AiCallArgs, type AiCapability, type AiResult, AlertIcon, type Attachment, Avatar, BotIcon, CheckIcon, ChevronLeftIcon, type ClientMessageId, ClockIcon, CloseIcon, Composer, type Conversation, type ConversationCursor, type ConversationId, ConversationList, type ConversationListProps, type ConversationRowWrapperProps, ConversationSkeleton, type ConversationSummary, type ConversationThread, type ConversationType, ConversationView, type ConversationViewProps, type DeliveryState, DeliveryTick, DoubleCheckIcon, type DraftMessage, EmptyState, type EngineDiagnostic, type IncomingMessageContext, type JsonObject, type JsonValue, LinkIcon, MESSAGING_EVENTS, MESSAGING_RPC_SCHEMA, MESSAGING_SCHEMA, type MatrxReference, type Message, type MessageAction, MessageActionChips, type MessageActionRenderProps, type MessageActionRenderer, MessageBubble, type MessageCursor, type MessageGroup, type MessageId, type MessageKind, type MessageWrapperProps, type MessagingAgentIdentity, type MessagingAgents, type MessagingAi, type MessagingAiOptions, type MessagingEngine, type MessagingEngineOptions, MessagingError, type MessagingErrorCode, type MessagingHost, type MessagingIdentity, MessagingInbox, type MessagingInboxProps, MessagingProvider, type MessagingProviderProps, type MessagingRepository, type MessagingSnapshot, type MessagingStore, type MessagingSupabaseClient, type MessagingSupabaseInternal, type OrganizationId, type Outbox, type OutboxEntry, type OutboxOptions, type OutboxStorage, type Page, PaperclipIcon, type Participant, type ParticipantRole, PlusIcon, type PostgrestFilterLike, type PostgrestLikeResponse, type PostgrestTableLike, RPCS, type ReadCache, type ReadCacheOptions, ReferenceCard, type ReferenceRenderer, ReplyIcon, type RepositoryOptions, type SchemaLike, SearchIcon, SendIcon, type SessionResolver, SparklesIcon, type SupabaseLike, TABLES, type TextSegment, TypingDots, type UseComposerResult, type UseConversationResult, type UseConversationsResult, type UseMessageActionResult, type UseMessagingAiResult, type UseTypistsResult, type UserId, type UserSummary, UsersIcon, asClientMessageId, asConversationId, asMessageId, asOrganizationId, asUserId, composeFence, conversationTopic, createActionRegistry, createMemoryOutboxStorage, createMessagingAi, createMessagingEngine, createMessagingRepository, createMessagingStore, createOutbox, createReadCache, createWebOutboxStorage, extractReferences, formatConversationTime, formatDateSeparator, formatLastSeen, formatMessageTime, formatTypists, groupMessages, inboxTopic, invalidResponse, isSameDay, messagingClientId, normalizeMessagingError, optimisticMessage, participantNames, projectConversationSummary, projectMessage, projectMessageAction, projectParticipantRole, projectUserSummary, resolveActor, splitText, summarizeText, unreadCutoff, useComposer, useConversation, useConversations, useMessageAction, useMessagingAi, useMessagingHost, useMessagingSnapshot, useOnlineUserIds, useRequiredMessagingHost, useTypists };
1541
+ export { type ActionChoice, type ActionContext, type ActionHandler, type ActionOutcome, type ActionReceipt, type ActionRegistry, type ActorPresentation, AgentTag, type AiCallArgs, type AiCapability, type AiResult, AlertIcon, type Attachment, Avatar, BotIcon, CheckIcon, ChevronLeftIcon, type ClientMessageId, ClockIcon, CloseIcon, Composer, type ComposerInputRenderProps, type Conversation, type ConversationCursor, type ConversationId, ConversationList, type ConversationListProps, type ConversationRowWrapperProps, ConversationSkeleton, type ConversationSummary, type ConversationThread, type ConversationType, ConversationView, type ConversationViewProps, type DeliveryState, DeliveryTick, DoubleCheckIcon, type DraftMessage, EmptyState, type EngineDiagnostic, type IncomingMessageContext, type JsonObject, type JsonValue, LinkIcon, MESSAGING_EVENTS, MESSAGING_RPC_SCHEMA, MESSAGING_SCHEMA, type MatrxReference, type Message, type MessageAction, MessageActionChips, type MessageActionRenderProps, type MessageActionRenderer, MessageBubble, type MessageCursor, type MessageGroup, type MessageId, type MessageKind, type MessageWrapperProps, type MessagingAgentIdentity, type MessagingAgents, type MessagingAi, type MessagingAiOptions, type MessagingEngine, type MessagingEngineOptions, MessagingError, type MessagingErrorCode, type MessagingHost, type MessagingIdentity, MessagingInbox, type MessagingInboxProps, MessagingProvider, type MessagingProviderProps, type MessagingRepository, type MessagingSnapshot, type MessagingStore, type MessagingSupabaseClient, type MessagingSupabaseInternal, type OrganizationId, type Outbox, type OutboxEntry, type OutboxOptions, type OutboxStorage, type Page, PaperclipIcon, type Participant, type ParticipantRole, PlusIcon, type PostgrestFilterLike, type PostgrestLikeResponse, type PostgrestTableLike, RPCS, type ReadCache, type ReadCacheOptions, ReferenceCard, type ReferenceRenderer, ReplyIcon, type RepositoryOptions, type SchemaLike, SearchIcon, SendIcon, type SessionResolver, SparklesIcon, type SupabaseLike, TABLES, type TextSegment, TypingDots, type UseComposerResult, type UseConversationResult, type UseConversationsResult, type UseMessageActionResult, type UseMessagingAiResult, type UseTypistsResult, type UserId, type UserSummary, UsersIcon, asClientMessageId, asConversationId, asMessageId, asOrganizationId, asUserId, composeFence, conversationTopic, createActionRegistry, createMemoryOutboxStorage, createMessagingAi, createMessagingEngine, createMessagingRepository, createMessagingStore, createOutbox, createReadCache, createWebOutboxStorage, effectiveActorKey, extractReferences, formatConversationTime, formatDateSeparator, formatLastSeen, formatMessageTime, formatTypists, groupMessages, inboxTopic, invalidResponse, isSameDay, messagingClientId, normalizeMessagingError, optimisticMessage, participantNames, projectConversationSummary, projectMessage, projectMessageAction, projectParticipantRole, projectUserSummary, resolveActor, splitText, summarizeText, unreadCutoff, useComposer, useConversation, useConversations, useMessageAction, useMessagingAi, useMessagingHost, useMessagingSnapshot, useOnlineUserIds, useRequiredMessagingHost, useTypists };
package/dist/react.js CHANGED
@@ -1817,15 +1817,27 @@ function MessagingRuntime(props) {
1817
1817
  const transport = props.transport;
1818
1818
  const agents = props.agents;
1819
1819
  const maxTranscriptMessages = props.maxTranscriptMessages;
1820
+ const sourceApp = props.sourceApp;
1821
+ const sourceFeature = props.sourceFeature;
1820
1822
  const ai = useMemo(() => {
1821
1823
  if (transport === void 0 || agents === void 0 || !ready) return null;
1822
1824
  return createMessagingAi({
1823
1825
  transport,
1824
1826
  organizationId,
1825
1827
  agents,
1826
- ...maxTranscriptMessages !== void 0 ? { maxTranscriptMessages } : {}
1828
+ ...maxTranscriptMessages !== void 0 ? { maxTranscriptMessages } : {},
1829
+ ...sourceApp !== void 0 ? { sourceApp } : {},
1830
+ ...sourceFeature !== void 0 ? { sourceFeature } : {}
1827
1831
  });
1828
- }, [transport, agents, ready, organizationId, maxTranscriptMessages]);
1832
+ }, [
1833
+ transport,
1834
+ agents,
1835
+ ready,
1836
+ organizationId,
1837
+ maxTranscriptMessages,
1838
+ sourceApp,
1839
+ sourceFeature
1840
+ ]);
1829
1841
  const renderers = props.actionRenderers;
1830
1842
  const rendererMap = useMemo(() => {
1831
1843
  const map = /* @__PURE__ */ new Map();
@@ -1944,18 +1956,20 @@ function groupMessages(messages, args = {}) {
1944
1956
  let current = null;
1945
1957
  let previousDay = null;
1946
1958
  for (const message of messages) {
1959
+ const actorKey = args.actorKey?.(message) ?? `principal:${message.senderId}`;
1947
1960
  const day = message.createdAt.slice(0, 10);
1948
1961
  const startsNewDay = day !== previousDay;
1949
1962
  previousDay = day;
1950
1963
  const last = current?.messages.at(-1);
1951
1964
  const withinWindow = last !== void 0 && Math.abs(Date.parse(message.createdAt) - Date.parse(last.createdAt)) <= windowMs;
1952
- if (current !== null && current.senderId === message.senderId && withinWindow && !startsNewDay) {
1965
+ if (current !== null && current.senderId === message.senderId && current.actorKey === actorKey && withinWindow && !startsNewDay) {
1953
1966
  current.messages.push(message);
1954
1967
  continue;
1955
1968
  }
1956
1969
  if (current !== null) groups.push(current);
1957
1970
  current = {
1958
1971
  senderId: message.senderId,
1972
+ actorKey,
1959
1973
  messages: [message],
1960
1974
  dateSeparator: startsNewDay ? formatDateSeparator(message.createdAt, now, args.locale) : null
1961
1975
  };
@@ -2273,23 +2287,45 @@ function useMessageAction(message) {
2273
2287
  }
2274
2288
 
2275
2289
  // src/react/components.tsx
2276
- import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef3, useState as useState2 } from "react";
2290
+ import {
2291
+ useEffect as useEffect3,
2292
+ useMemo as useMemo3,
2293
+ useRef as useRef3,
2294
+ useState as useState2
2295
+ } from "react";
2277
2296
 
2278
2297
  // src/core/actor.ts
2279
2298
  function readActorHint(metadata) {
2280
- const raw = metadata["actor"];
2281
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
2282
- const record = raw;
2283
- const agentId = record["agentId"] ?? record["agent_id"];
2284
- if (typeof agentId !== "string" || agentId.length === 0) return null;
2285
- const name = record["agentName"] ?? record["agent_name"];
2286
- const avatar = record["agentAvatarUrl"] ?? record["agent_avatar_url"];
2299
+ const envelope = metadata;
2300
+ const raw = envelope["actor"];
2301
+ if (typeof raw === "object" && raw !== null && !Array.isArray(raw)) {
2302
+ const record = raw;
2303
+ const agentId = record["agentId"] ?? record["agent_id"];
2304
+ if (typeof agentId !== "string" || agentId.length === 0) return null;
2305
+ const name2 = record["agentName"] ?? record["agent_name"];
2306
+ const avatar2 = record["agentAvatarUrl"] ?? record["agent_avatar_url"];
2307
+ return {
2308
+ agentId,
2309
+ ...typeof name2 === "string" && name2.length > 0 ? { agentName: name2 } : {},
2310
+ ...typeof avatar2 === "string" && avatar2.length > 0 ? { agentAvatarUrl: avatar2 } : {}
2311
+ };
2312
+ }
2313
+ const actorKind = envelope["actor_kind"] ?? envelope["actorKind"];
2314
+ if (actorKind !== "agent") return null;
2315
+ const id = envelope["actor_id"] ?? envelope["actorId"];
2316
+ const name = envelope["actor_label"] ?? envelope["actorLabel"];
2317
+ const avatar = envelope["actor_avatar_url"] ?? envelope["actorAvatarUrl"];
2287
2318
  return {
2288
- agentId,
2319
+ ...typeof id === "string" && id.length > 0 ? { agentId: id } : {},
2289
2320
  ...typeof name === "string" && name.length > 0 ? { agentName: name } : {},
2290
2321
  ...typeof avatar === "string" && avatar.length > 0 ? { agentAvatarUrl: avatar } : {}
2291
2322
  };
2292
2323
  }
2324
+ function effectiveActorKey(message) {
2325
+ const hint = readActorHint(message.metadata);
2326
+ if (hint === null) return `principal:${message.senderId}`;
2327
+ return `agent:${hint.agentId ?? hint.agentName ?? "anonymous"}`;
2328
+ }
2293
2329
  function resolveActor(message, sender) {
2294
2330
  const hint = readActorHint(message.metadata);
2295
2331
  const humanName = sender?.displayName ?? message.senderId;
@@ -2685,7 +2721,7 @@ function ConversationView(props) {
2685
2721
  );
2686
2722
  const anyOnline = others.some((participant) => online.has(participant.userId));
2687
2723
  return /* @__PURE__ */ jsxs2("div", { className: `mx-msg__thread${props.className !== void 0 ? ` ${props.className}` : ""}`, children: [
2688
- /* @__PURE__ */ jsxs2("header", { className: "mx-msg__header", children: [
2724
+ props.showHeader !== false ? /* @__PURE__ */ jsxs2("header", { className: "mx-msg__header", children: [
2689
2725
  props.onBack !== void 0 ? /* @__PURE__ */ jsx4(
2690
2726
  "button",
2691
2727
  {
@@ -2720,8 +2756,8 @@ function ConversationView(props) {
2720
2756
  children: /* @__PURE__ */ jsx4(UsersIcon, {})
2721
2757
  }
2722
2758
  ) : null })
2723
- ] }),
2724
- ai.available.length > 0 ? /* @__PURE__ */ jsx4("div", { className: "mx-msg__ai-bar", children: ai.available.map((capability) => /* @__PURE__ */ jsxs2(
2759
+ ] }) : null,
2760
+ props.showAi !== false && ai.available.length > 0 ? /* @__PURE__ */ jsx4("div", { className: "mx-msg__ai-bar", children: ai.available.map((capability) => /* @__PURE__ */ jsxs2(
2725
2761
  "button",
2726
2762
  {
2727
2763
  type: "button",
@@ -2735,7 +2771,7 @@ function ConversationView(props) {
2735
2771
  },
2736
2772
  capability
2737
2773
  )) }) : null,
2738
- ai.isRunning ? /* @__PURE__ */ jsx4("p", { className: "mx-msg__ai-output", "aria-busy": "true", "aria-live": "polite", children: ai.partialText.length > 0 ? ai.partialText : /* @__PURE__ */ jsxs2(Fragment, { children: [
2774
+ props.showAi !== false && ai.isRunning ? /* @__PURE__ */ jsx4("p", { className: "mx-msg__ai-output", "aria-busy": "true", "aria-live": "polite", children: ai.partialText.length > 0 ? ai.partialText : /* @__PURE__ */ jsxs2(Fragment, { children: [
2739
2775
  /* @__PURE__ */ jsx4("span", { className: "mx-msg__ai-output-label", children: ai.activeCapability !== null ? `${AI_LABELS[ai.activeCapability]}\u2026` : "Working\u2026" }),
2740
2776
  /* @__PURE__ */ jsx4(
2741
2777
  "span",
@@ -2744,7 +2780,7 @@ function ConversationView(props) {
2744
2780
  style: { display: "block", height: 11, width: "72%" }
2745
2781
  }
2746
2782
  )
2747
- ] }) }) : ai.error !== null ? /* @__PURE__ */ jsx4("p", { className: "mx-msg__ai-output", style: { color: "var(--mx-msg-danger)" }, children: ai.error }) : ai.result !== null ? /* @__PURE__ */ jsxs2("p", { className: "mx-msg__ai-output", children: [
2783
+ ] }) }) : props.showAi !== false && ai.error !== null ? /* @__PURE__ */ jsx4("p", { className: "mx-msg__ai-output", style: { color: "var(--mx-msg-danger)" }, children: ai.error }) : props.showAi !== false && ai.result !== null ? /* @__PURE__ */ jsxs2("p", { className: "mx-msg__ai-output", children: [
2748
2784
  ai.result.text,
2749
2785
  /* @__PURE__ */ jsx4(
2750
2786
  "button",
@@ -2769,26 +2805,24 @@ function ConversationView(props) {
2769
2805
  children: "Load earlier messages"
2770
2806
  }
2771
2807
  ) : null,
2772
- groupMessages(messages).map((group) => {
2808
+ groupMessages(messages, { actorKey: effectiveActorKey }).map((group) => {
2773
2809
  const first = group.messages[0];
2774
2810
  if (first === void 0) return null;
2775
- const isMine = group.senderId === selfId;
2776
2811
  const sender = conversation.participants.find(
2777
2812
  (participant) => participant.userId === group.senderId
2778
2813
  ) ?? null;
2814
+ const actor = resolveActor(first, sender);
2815
+ const isMine = group.senderId === selfId && !actor.isAgent;
2779
2816
  return /* @__PURE__ */ jsxs2("div", { children: [
2780
2817
  group.dateSeparator !== null ? /* @__PURE__ */ jsx4("div", { className: "mx-msg__day", children: group.dateSeparator }) : null,
2781
2818
  /* @__PURE__ */ jsx4(
2782
2819
  MessageGroupView,
2783
2820
  {
2784
2821
  messages: group.messages,
2785
- sender,
2822
+ actor,
2786
2823
  isMine,
2787
2824
  onRetry: conversation.retry,
2788
2825
  onReply: composer.setReplyTo,
2789
- pendingIds: new Set(
2790
- conversation.pending.filter((entry) => entry.state === "failed").map((entry) => entry.clientMessageId)
2791
- ),
2792
2826
  failedEntryIdByClientId: new Map(
2793
2827
  conversation.pending.map((entry) => [entry.clientMessageId, entry.id])
2794
2828
  )
@@ -2801,13 +2835,20 @@ function ConversationView(props) {
2801
2835
  /* @__PURE__ */ jsx4(TypingDots, {}),
2802
2836
  typists.label
2803
2837
  ] }) : null }),
2804
- /* @__PURE__ */ jsx4(ComposerView, { composer, disabled: false })
2838
+ /* @__PURE__ */ jsx4(
2839
+ ComposerView,
2840
+ {
2841
+ composer,
2842
+ disabled: false,
2843
+ ...props.renderComposerInput !== void 0 ? { renderInput: props.renderComposerInput } : {}
2844
+ }
2845
+ )
2805
2846
  ] });
2806
2847
  }
2807
2848
  function MessageGroupView(props) {
2808
2849
  const first = props.messages[0];
2809
2850
  if (first === void 0) return null;
2810
- const actor = resolveActor(first, props.sender);
2851
+ const actor = props.actor;
2811
2852
  return /* @__PURE__ */ jsxs2("div", { className: `mx-msg__group${props.isMine ? " mx-msg__group--mine" : ""}`, children: [
2812
2853
  /* @__PURE__ */ jsx4(
2813
2854
  Avatar,
@@ -2994,7 +3035,24 @@ function ComposerView(props) {
2994
3035
  }
2995
3036
  )
2996
3037
  ] }) : null,
2997
- /* @__PURE__ */ jsxs2("div", { className: "mx-msg__composer-row", children: [
3038
+ props.renderInput !== void 0 ? props.renderInput({
3039
+ value: composer.value,
3040
+ disabled: props.disabled,
3041
+ canSend: composer.canSend,
3042
+ placeholder: "Write a reply\u2026",
3043
+ onChange: (value) => {
3044
+ composer.setValue(value);
3045
+ composer.onKeystroke();
3046
+ },
3047
+ onKeyDown: (event) => {
3048
+ if (event.key !== "Enter" || event.shiftKey) return;
3049
+ const isTouch = typeof globalThis.matchMedia === "function" && globalThis.matchMedia("(pointer: coarse)").matches;
3050
+ if (isTouch) return;
3051
+ event.preventDefault();
3052
+ composer.send();
3053
+ },
3054
+ onSubmit: composer.send
3055
+ }) : /* @__PURE__ */ jsxs2("div", { className: "mx-msg__composer-row", children: [
2998
3056
  /* @__PURE__ */ jsx4(
2999
3057
  "textarea",
3000
3058
  {
@@ -3125,6 +3183,7 @@ export {
3125
3183
  createOutbox,
3126
3184
  createReadCache,
3127
3185
  createWebOutboxStorage,
3186
+ effectiveActorKey,
3128
3187
  extractReferences,
3129
3188
  formatConversationTime,
3130
3189
  formatDateSeparator,