@ai-matrx/messaging 0.10.1 → 0.10.3

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
 
@@ -1157,6 +1157,21 @@ interface ConversationViewProps {
1157
1157
  conversationId: ConversationId | null;
1158
1158
  onBack?: () => void;
1159
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;
1160
1175
  }
1161
1176
  declare function ConversationView(props: ConversationViewProps): ReactNode;
1162
1177
  declare function MessageBubble(props: {
@@ -1285,6 +1300,8 @@ interface ActorPresentation {
1285
1300
  /** The audit principal. Always the row's `sender_id`. Never rewritten. */
1286
1301
  readonly principalUserId: Message["senderId"];
1287
1302
  }
1303
+ /** Stable presentation identity for grouping adjacent messages in the UI. */
1304
+ declare function effectiveActorKey(message: Message): string;
1288
1305
  declare function resolveActor(message: Message, sender: UserSummary | null): ActorPresentation;
1289
1306
 
1290
1307
  /**
@@ -1434,6 +1451,8 @@ declare function groupMessages(messages: readonly Message[], args?: {
1434
1451
  now?: number;
1435
1452
  windowMs?: number;
1436
1453
  locale?: string;
1454
+ /** Split audit-principal rows by their effective actor presentation. */
1455
+ actorKey?: (message: Message) => string;
1437
1456
  }): readonly MessageGroup[];
1438
1457
  /** "Ana is typing…" / "Ana and Bo are typing…" / "3 people are typing…" */
1439
1458
  declare function formatTypists(names: readonly string[]): string | null;
@@ -1519,4 +1538,4 @@ declare function summarizeText(content: string, maxLength?: number): string;
1519
1538
  /** Serialize picked references into a fence the platform's other readers accept. */
1520
1539
  declare function composeFence(references: readonly MatrxReference[]): string;
1521
1540
 
1522
- 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
 
@@ -1157,6 +1157,21 @@ interface ConversationViewProps {
1157
1157
  conversationId: ConversationId | null;
1158
1158
  onBack?: () => void;
1159
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;
1160
1175
  }
1161
1176
  declare function ConversationView(props: ConversationViewProps): ReactNode;
1162
1177
  declare function MessageBubble(props: {
@@ -1285,6 +1300,8 @@ interface ActorPresentation {
1285
1300
  /** The audit principal. Always the row's `sender_id`. Never rewritten. */
1286
1301
  readonly principalUserId: Message["senderId"];
1287
1302
  }
1303
+ /** Stable presentation identity for grouping adjacent messages in the UI. */
1304
+ declare function effectiveActorKey(message: Message): string;
1288
1305
  declare function resolveActor(message: Message, sender: UserSummary | null): ActorPresentation;
1289
1306
 
1290
1307
  /**
@@ -1434,6 +1451,8 @@ declare function groupMessages(messages: readonly Message[], args?: {
1434
1451
  now?: number;
1435
1452
  windowMs?: number;
1436
1453
  locale?: string;
1454
+ /** Split audit-principal rows by their effective actor presentation. */
1455
+ actorKey?: (message: Message) => string;
1437
1456
  }): readonly MessageGroup[];
1438
1457
  /** "Ana is typing…" / "Ana and Bo are typing…" / "3 people are typing…" */
1439
1458
  declare function formatTypists(names: readonly string[]): string | null;
@@ -1519,4 +1538,4 @@ declare function summarizeText(content: string, maxLength?: number): string;
1519
1538
  /** Serialize picked references into a fence the platform's other readers accept. */
1520
1539
  declare function composeFence(references: readonly MatrxReference[]): string;
1521
1540
 
1522
- 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
@@ -1211,6 +1211,11 @@ function createMessagingEngine(options) {
1211
1211
  if (openChannels.has(id) || disposed) return;
1212
1212
  const handle = manager.open({
1213
1213
  topic: conversationTopic(id),
1214
+ // Presence callbacks are JOIN-TIME in supabase-js. Declare the
1215
+ // read-only room shape on the first holder (the engine) so the
1216
+ // composer/typist/online hooks can attach without forcing a rebuild
1217
+ // after this durable channel has already subscribed.
1218
+ presence: { publish: false, state: {} },
1214
1219
  postgresChanges: [
1215
1220
  {
1216
1221
  event: "*",
@@ -1956,18 +1961,20 @@ function groupMessages(messages, args = {}) {
1956
1961
  let current = null;
1957
1962
  let previousDay = null;
1958
1963
  for (const message of messages) {
1964
+ const actorKey = args.actorKey?.(message) ?? `principal:${message.senderId}`;
1959
1965
  const day = message.createdAt.slice(0, 10);
1960
1966
  const startsNewDay = day !== previousDay;
1961
1967
  previousDay = day;
1962
1968
  const last = current?.messages.at(-1);
1963
1969
  const withinWindow = last !== void 0 && Math.abs(Date.parse(message.createdAt) - Date.parse(last.createdAt)) <= windowMs;
1964
- if (current !== null && current.senderId === message.senderId && withinWindow && !startsNewDay) {
1970
+ if (current !== null && current.senderId === message.senderId && current.actorKey === actorKey && withinWindow && !startsNewDay) {
1965
1971
  current.messages.push(message);
1966
1972
  continue;
1967
1973
  }
1968
1974
  if (current !== null) groups.push(current);
1969
1975
  current = {
1970
1976
  senderId: message.senderId,
1977
+ actorKey,
1971
1978
  messages: [message],
1972
1979
  dateSeparator: startsNewDay ? formatDateSeparator(message.createdAt, now, args.locale) : null
1973
1980
  };
@@ -2285,23 +2292,47 @@ function useMessageAction(message) {
2285
2292
  }
2286
2293
 
2287
2294
  // src/react/components.tsx
2288
- import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef3, useState as useState2 } from "react";
2295
+ import {
2296
+ useEffect as useEffect3,
2297
+ useMemo as useMemo3,
2298
+ useRef as useRef3,
2299
+ useState as useState2
2300
+ } from "react";
2289
2301
 
2290
2302
  // src/core/actor.ts
2291
2303
  function readActorHint(metadata) {
2292
- const raw = metadata["actor"];
2293
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
2294
- const record = raw;
2295
- const agentId = record["agentId"] ?? record["agent_id"];
2296
- if (typeof agentId !== "string" || agentId.length === 0) return null;
2297
- const name = record["agentName"] ?? record["agent_name"];
2298
- const avatar = record["agentAvatarUrl"] ?? record["agent_avatar_url"];
2304
+ const envelope = metadata;
2305
+ const raw = envelope["actor"];
2306
+ if (typeof raw === "object" && raw !== null && !Array.isArray(raw)) {
2307
+ const record = raw;
2308
+ const agentId = record["agentId"] ?? record["agent_id"];
2309
+ if (typeof agentId !== "string" || agentId.length === 0) return null;
2310
+ const name2 = record["agentName"] ?? record["agent_name"];
2311
+ const avatar2 = record["agentAvatarUrl"] ?? record["agent_avatar_url"];
2312
+ return {
2313
+ agentId,
2314
+ showPrincipalAttribution: true,
2315
+ ...typeof name2 === "string" && name2.length > 0 ? { agentName: name2 } : {},
2316
+ ...typeof avatar2 === "string" && avatar2.length > 0 ? { agentAvatarUrl: avatar2 } : {}
2317
+ };
2318
+ }
2319
+ const actorKind = envelope["actor_kind"] ?? envelope["actorKind"];
2320
+ if (actorKind !== "agent") return null;
2321
+ const id = envelope["actor_id"] ?? envelope["actorId"];
2322
+ const name = envelope["actor_label"] ?? envelope["actorLabel"];
2323
+ const avatar = envelope["actor_avatar_url"] ?? envelope["actorAvatarUrl"];
2299
2324
  return {
2300
- agentId,
2325
+ showPrincipalAttribution: false,
2326
+ ...typeof id === "string" && id.length > 0 ? { agentId: id } : {},
2301
2327
  ...typeof name === "string" && name.length > 0 ? { agentName: name } : {},
2302
2328
  ...typeof avatar === "string" && avatar.length > 0 ? { agentAvatarUrl: avatar } : {}
2303
2329
  };
2304
2330
  }
2331
+ function effectiveActorKey(message) {
2332
+ const hint = readActorHint(message.metadata);
2333
+ if (hint === null) return `principal:${message.senderId}`;
2334
+ return `agent:${hint.agentId ?? hint.agentName ?? "anonymous"}`;
2335
+ }
2305
2336
  function resolveActor(message, sender) {
2306
2337
  const hint = readActorHint(message.metadata);
2307
2338
  const humanName = sender?.displayName ?? message.senderId;
@@ -2313,7 +2344,11 @@ function resolveActor(message, sender) {
2313
2344
  displayName: hint.agentName ?? "Agent",
2314
2345
  avatarUrl: hint.agentAvatarUrl ?? null,
2315
2346
  isAgent: true,
2316
- onBehalfOfName: humanName,
2347
+ // The nested envelope explicitly models an agent acting on behalf of a
2348
+ // session principal. Platform event rows use the flat actor envelope as
2349
+ // the complete presentation identity; surfacing their audit writer as
2350
+ // "via Human" wrongly re-attaches that person to an automated event.
2351
+ onBehalfOfName: hint.showPrincipalAttribution ? humanName : null,
2317
2352
  principalUserId: message.senderId
2318
2353
  };
2319
2354
  }
@@ -2697,7 +2732,7 @@ function ConversationView(props) {
2697
2732
  );
2698
2733
  const anyOnline = others.some((participant) => online.has(participant.userId));
2699
2734
  return /* @__PURE__ */ jsxs2("div", { className: `mx-msg__thread${props.className !== void 0 ? ` ${props.className}` : ""}`, children: [
2700
- /* @__PURE__ */ jsxs2("header", { className: "mx-msg__header", children: [
2735
+ props.showHeader !== false ? /* @__PURE__ */ jsxs2("header", { className: "mx-msg__header", children: [
2701
2736
  props.onBack !== void 0 ? /* @__PURE__ */ jsx4(
2702
2737
  "button",
2703
2738
  {
@@ -2732,8 +2767,8 @@ function ConversationView(props) {
2732
2767
  children: /* @__PURE__ */ jsx4(UsersIcon, {})
2733
2768
  }
2734
2769
  ) : null })
2735
- ] }),
2736
- ai.available.length > 0 ? /* @__PURE__ */ jsx4("div", { className: "mx-msg__ai-bar", children: ai.available.map((capability) => /* @__PURE__ */ jsxs2(
2770
+ ] }) : null,
2771
+ props.showAi !== false && ai.available.length > 0 ? /* @__PURE__ */ jsx4("div", { className: "mx-msg__ai-bar", children: ai.available.map((capability) => /* @__PURE__ */ jsxs2(
2737
2772
  "button",
2738
2773
  {
2739
2774
  type: "button",
@@ -2747,7 +2782,7 @@ function ConversationView(props) {
2747
2782
  },
2748
2783
  capability
2749
2784
  )) }) : null,
2750
- 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: [
2785
+ 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: [
2751
2786
  /* @__PURE__ */ jsx4("span", { className: "mx-msg__ai-output-label", children: ai.activeCapability !== null ? `${AI_LABELS[ai.activeCapability]}\u2026` : "Working\u2026" }),
2752
2787
  /* @__PURE__ */ jsx4(
2753
2788
  "span",
@@ -2756,7 +2791,7 @@ function ConversationView(props) {
2756
2791
  style: { display: "block", height: 11, width: "72%" }
2757
2792
  }
2758
2793
  )
2759
- ] }) }) : 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: [
2794
+ ] }) }) : 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: [
2760
2795
  ai.result.text,
2761
2796
  /* @__PURE__ */ jsx4(
2762
2797
  "button",
@@ -2781,26 +2816,24 @@ function ConversationView(props) {
2781
2816
  children: "Load earlier messages"
2782
2817
  }
2783
2818
  ) : null,
2784
- groupMessages(messages).map((group) => {
2819
+ groupMessages(messages, { actorKey: effectiveActorKey }).map((group) => {
2785
2820
  const first = group.messages[0];
2786
2821
  if (first === void 0) return null;
2787
- const isMine = group.senderId === selfId;
2788
2822
  const sender = conversation.participants.find(
2789
2823
  (participant) => participant.userId === group.senderId
2790
2824
  ) ?? null;
2825
+ const actor = resolveActor(first, sender);
2826
+ const isMine = group.senderId === selfId && !actor.isAgent;
2791
2827
  return /* @__PURE__ */ jsxs2("div", { children: [
2792
2828
  group.dateSeparator !== null ? /* @__PURE__ */ jsx4("div", { className: "mx-msg__day", children: group.dateSeparator }) : null,
2793
2829
  /* @__PURE__ */ jsx4(
2794
2830
  MessageGroupView,
2795
2831
  {
2796
2832
  messages: group.messages,
2797
- sender,
2833
+ actor,
2798
2834
  isMine,
2799
2835
  onRetry: conversation.retry,
2800
2836
  onReply: composer.setReplyTo,
2801
- pendingIds: new Set(
2802
- conversation.pending.filter((entry) => entry.state === "failed").map((entry) => entry.clientMessageId)
2803
- ),
2804
2837
  failedEntryIdByClientId: new Map(
2805
2838
  conversation.pending.map((entry) => [entry.clientMessageId, entry.id])
2806
2839
  )
@@ -2813,13 +2846,20 @@ function ConversationView(props) {
2813
2846
  /* @__PURE__ */ jsx4(TypingDots, {}),
2814
2847
  typists.label
2815
2848
  ] }) : null }),
2816
- /* @__PURE__ */ jsx4(ComposerView, { composer, disabled: false })
2849
+ /* @__PURE__ */ jsx4(
2850
+ ComposerView,
2851
+ {
2852
+ composer,
2853
+ disabled: false,
2854
+ ...props.renderComposerInput !== void 0 ? { renderInput: props.renderComposerInput } : {}
2855
+ }
2856
+ )
2817
2857
  ] });
2818
2858
  }
2819
2859
  function MessageGroupView(props) {
2820
2860
  const first = props.messages[0];
2821
2861
  if (first === void 0) return null;
2822
- const actor = resolveActor(first, props.sender);
2862
+ const actor = props.actor;
2823
2863
  return /* @__PURE__ */ jsxs2("div", { className: `mx-msg__group${props.isMine ? " mx-msg__group--mine" : ""}`, children: [
2824
2864
  /* @__PURE__ */ jsx4(
2825
2865
  Avatar,
@@ -3006,7 +3046,24 @@ function ComposerView(props) {
3006
3046
  }
3007
3047
  )
3008
3048
  ] }) : null,
3009
- /* @__PURE__ */ jsxs2("div", { className: "mx-msg__composer-row", children: [
3049
+ props.renderInput !== void 0 ? props.renderInput({
3050
+ value: composer.value,
3051
+ disabled: props.disabled,
3052
+ canSend: composer.canSend,
3053
+ placeholder: "Write a reply\u2026",
3054
+ onChange: (value) => {
3055
+ composer.setValue(value);
3056
+ composer.onKeystroke();
3057
+ },
3058
+ onKeyDown: (event) => {
3059
+ if (event.key !== "Enter" || event.shiftKey) return;
3060
+ const isTouch = typeof globalThis.matchMedia === "function" && globalThis.matchMedia("(pointer: coarse)").matches;
3061
+ if (isTouch) return;
3062
+ event.preventDefault();
3063
+ composer.send();
3064
+ },
3065
+ onSubmit: composer.send
3066
+ }) : /* @__PURE__ */ jsxs2("div", { className: "mx-msg__composer-row", children: [
3010
3067
  /* @__PURE__ */ jsx4(
3011
3068
  "textarea",
3012
3069
  {
@@ -3137,6 +3194,7 @@ export {
3137
3194
  createOutbox,
3138
3195
  createReadCache,
3139
3196
  createWebOutboxStorage,
3197
+ effectiveActorKey,
3140
3198
  extractReferences,
3141
3199
  formatConversationTime,
3142
3200
  formatDateSeparator,