@ai-matrx/messaging 0.9.0 → 0.10.1

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,5 +1,5 @@
1
1
  import { ReactNode, ComponentType, SVGProps } from 'react';
2
- import { MatrxTransport } from '@ai-matrx/agents/matrx';
2
+ import { MatrxJsonObject, MatrxTransport } from '@ai-matrx/agents/matrx';
3
3
  import { RealtimeClientLike, RealtimeManager } from '@ai-matrx/realtime';
4
4
 
5
5
  /**
@@ -285,27 +285,59 @@ declare function createActionRegistry(): ActionRegistry;
285
285
  * every conversation it touches. Every call below sends `variables` and either
286
286
  * a short human-shaped `user_input` or none.
287
287
  *
288
- * 🚨 **AGENT DEFINITIONS LIVE IN THE DATABASE.** The package takes agent IDs as
289
- * injected identity. When an id is not configured the capability reports
290
- * `unavailable` WITH the remedy it never silently no-ops, and the UI hides
291
- * the action rather than offering a dead button (no dead ends).
288
+ * 🚨 **AGENT DEFINITIONS LIVE IN THE DATABASE.** The package takes an injected
289
+ * IDENTITY per capability the database agent id and the settings that came
290
+ * with it and nothing else. When an identity is not configured the capability
291
+ * reports `unavailable` WITH the remedy; it never silently no-ops, and the UI
292
+ * hides the action rather than offering a dead button (no dead ends).
293
+ *
294
+ * 🚨 **AN IDENTITY IS BOTH HALVES.** A host that resolves a capability's agent
295
+ * from its own switching layer resolves a PAIR: which agent, and the settings
296
+ * that layer decided for it. Taking only the id silently drops the settings
297
+ * half, so `configOverrides` rides with the id and is sent verbatim as the
298
+ * turn's `config_overrides`.
299
+ *
300
+ * 🚨 **A RUN THE USER WATCHES STREAMS.** Every call takes `onText` and reports
301
+ * the accumulated answer as it arrives. A skeleton that sits until a run
302
+ * finishes is a spinner standing in for an answer; the caller renders the
303
+ * partial text instead.
292
304
  */
293
305
 
294
306
  /**
295
- * Which platform agent backs each capability. Every field is a DATABASE row id
296
- * supplied by the host; an omitted one disables exactly that capability.
307
+ * WHO fulfils one capability, as the host resolved it. Both halves travel
308
+ * together: an id alone is half an answer whenever the host's switching layer
309
+ * also decided settings for the job.
310
+ */
311
+ interface MessagingAgentIdentity {
312
+ /** The DATABASE agent row id. Never a name, never a prompt. */
313
+ readonly agentId: string;
314
+ /**
315
+ * The settings half, in LLMParams shape, sent verbatim as the turn's
316
+ * `config_overrides`. `null`/omitted = the agent's own settings stand.
317
+ */
318
+ readonly configOverrides?: MatrxJsonObject | null | undefined;
319
+ }
320
+ /**
321
+ * Which platform agent backs each capability. Every field is an identity the
322
+ * host injects; an omitted one disables exactly that capability.
297
323
  */
298
324
  interface MessagingAgents {
299
- readonly catchUp?: string | undefined;
300
- readonly summarize?: string | undefined;
301
- readonly actionItems?: string | undefined;
302
- readonly draftReply?: string | undefined;
325
+ readonly catchUp?: MessagingAgentIdentity | undefined;
326
+ readonly summarize?: MessagingAgentIdentity | undefined;
327
+ readonly actionItems?: MessagingAgentIdentity | undefined;
328
+ readonly draftReply?: MessagingAgentIdentity | undefined;
303
329
  }
304
330
  interface MessagingAiOptions {
305
331
  transport: MatrxTransport;
306
332
  organizationId: string;
307
333
  agents: MessagingAgents;
308
- /** 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
+ */
309
341
  sourceApp?: string | undefined;
310
342
  sourceFeature?: string | undefined;
311
343
  /** Cap on how much transcript is sent. Default 200 messages. */
@@ -317,38 +349,44 @@ interface AiResult {
317
349
  readonly text: string;
318
350
  readonly conversationId: string | null;
319
351
  }
352
+ /** What every capability takes: the conversation, and how to watch the run. */
353
+ interface AiCallArgs {
354
+ conversationId: ConversationId;
355
+ messages: readonly Message[];
356
+ participants: readonly UserSummary[];
357
+ signal?: AbortSignal | undefined;
358
+ /** The accumulated answer so far, after every streamed chunk. */
359
+ onText?: ((fullText: string) => void) | undefined;
360
+ }
320
361
  interface MessagingAi {
321
362
  /** Which capabilities this host actually configured. Drives what the UI offers. */
322
363
  available(): readonly AiCapability[];
323
364
  isAvailable(capability: AiCapability): boolean;
324
- catchMeUp(args: {
325
- conversationId: ConversationId;
326
- messages: readonly Message[];
327
- participants: readonly UserSummary[];
365
+ catchMeUp(args: AiCallArgs & {
366
+ /**
367
+ * Exclusive cutoff — only messages after it are sent. `null` means the
368
+ * caller genuinely has no read mark and the whole window applies.
369
+ */
328
370
  since: string | null;
329
- signal?: AbortSignal;
330
- }): Promise<AiResult>;
331
- summarize(args: {
332
- conversationId: ConversationId;
333
- messages: readonly Message[];
334
- participants: readonly UserSummary[];
335
- signal?: AbortSignal;
336
371
  }): Promise<AiResult>;
337
- extractActionItems(args: {
338
- conversationId: ConversationId;
339
- messages: readonly Message[];
340
- participants: readonly UserSummary[];
341
- signal?: AbortSignal;
342
- }): Promise<AiResult>;
343
- draftReply(args: {
344
- conversationId: ConversationId;
345
- messages: readonly Message[];
346
- participants: readonly UserSummary[];
372
+ summarize(args: AiCallArgs): Promise<AiResult>;
373
+ extractActionItems(args: AiCallArgs): Promise<AiResult>;
374
+ draftReply(args: AiCallArgs & {
347
375
  /** What the human asked for, if anything — this IS a human utterance. */
348
376
  instruction?: string | undefined;
349
- signal?: AbortSignal;
350
377
  }): Promise<AiResult>;
351
378
  }
379
+ /**
380
+ * The exclusive cutoff "what did I miss" means, derived from what the store
381
+ * already knows: the message just BEFORE the first unread one. No unread
382
+ * messages, or a loaded window that does not reach back that far, means there
383
+ * is no honest cutoff — the whole window applies, and the caller says so by
384
+ * passing `null` rather than guessing a timestamp.
385
+ *
386
+ * Framework-free on purpose: a Redux or React Native consumer must not have to
387
+ * re-derive what "unread" means.
388
+ */
389
+ declare function unreadCutoff(messages: readonly Message[], unreadCount: number): string | null;
352
390
  declare function createMessagingAi(options: MessagingAiOptions): MessagingAi;
353
391
 
354
392
  /**
@@ -947,8 +985,23 @@ interface MessagingProviderProps {
947
985
  * the AI actions then do not render at all (never a dead button).
948
986
  */
949
987
  transport?: MatrxTransport | undefined;
950
- /** Which database agent backs each AI capability. */
988
+ /** Which database agent backs each AI capability, with its settings. */
951
989
  agents?: MessagingAgents | undefined;
990
+ /**
991
+ * How much transcript rides an AI call, in messages. The package's 200 is a
992
+ * starting value, not a taste: a host with a knob for it passes the resolved
993
+ * number so an organization can decide.
994
+ */
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;
952
1005
  /** Handlers for actionable messages. Registered once per handler identity. */
953
1006
  actions?: readonly ActionHandler<never>[] | undefined;
954
1007
  /**
@@ -1051,6 +1104,14 @@ declare function useOnlineUserIds(conversationId: ConversationId | null): Readon
1051
1104
  interface UseMessagingAiResult {
1052
1105
  available: readonly AiCapability[];
1053
1106
  isRunning: boolean;
1107
+ /**
1108
+ * The answer as it arrives, while `isRunning`. A surface renders THIS, not a
1109
+ * skeleton: a placeholder that sits until a run finishes is a spinner
1110
+ * standing in for an answer. Empty string until the first chunk lands.
1111
+ */
1112
+ partialText: string;
1113
+ /** Which capability is running (or produced `result`). */
1114
+ activeCapability: AiCapability | null;
1054
1115
  result: AiResult | null;
1055
1116
  error: string | null;
1056
1117
  run: (capability: AiCapability, args?: {
@@ -1458,4 +1519,4 @@ declare function summarizeText(content: string, maxLength?: number): string;
1458
1519
  /** Serialize picked references into a fence the platform's other readers accept. */
1459
1520
  declare function composeFence(references: readonly MatrxReference[]): string;
1460
1521
 
1461
- export { type ActionChoice, type ActionContext, type ActionHandler, type ActionOutcome, type ActionReceipt, type ActionRegistry, type ActorPresentation, AgentTag, 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 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, useComposer, useConversation, useConversations, useMessageAction, useMessagingAi, useMessagingHost, useMessagingSnapshot, useOnlineUserIds, useRequiredMessagingHost, useTypists };
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 };
package/dist/react.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ReactNode, ComponentType, SVGProps } from 'react';
2
- import { MatrxTransport } from '@ai-matrx/agents/matrx';
2
+ import { MatrxJsonObject, MatrxTransport } from '@ai-matrx/agents/matrx';
3
3
  import { RealtimeClientLike, RealtimeManager } from '@ai-matrx/realtime';
4
4
 
5
5
  /**
@@ -285,27 +285,59 @@ declare function createActionRegistry(): ActionRegistry;
285
285
  * every conversation it touches. Every call below sends `variables` and either
286
286
  * a short human-shaped `user_input` or none.
287
287
  *
288
- * 🚨 **AGENT DEFINITIONS LIVE IN THE DATABASE.** The package takes agent IDs as
289
- * injected identity. When an id is not configured the capability reports
290
- * `unavailable` WITH the remedy it never silently no-ops, and the UI hides
291
- * the action rather than offering a dead button (no dead ends).
288
+ * 🚨 **AGENT DEFINITIONS LIVE IN THE DATABASE.** The package takes an injected
289
+ * IDENTITY per capability the database agent id and the settings that came
290
+ * with it and nothing else. When an identity is not configured the capability
291
+ * reports `unavailable` WITH the remedy; it never silently no-ops, and the UI
292
+ * hides the action rather than offering a dead button (no dead ends).
293
+ *
294
+ * 🚨 **AN IDENTITY IS BOTH HALVES.** A host that resolves a capability's agent
295
+ * from its own switching layer resolves a PAIR: which agent, and the settings
296
+ * that layer decided for it. Taking only the id silently drops the settings
297
+ * half, so `configOverrides` rides with the id and is sent verbatim as the
298
+ * turn's `config_overrides`.
299
+ *
300
+ * 🚨 **A RUN THE USER WATCHES STREAMS.** Every call takes `onText` and reports
301
+ * the accumulated answer as it arrives. A skeleton that sits until a run
302
+ * finishes is a spinner standing in for an answer; the caller renders the
303
+ * partial text instead.
292
304
  */
293
305
 
294
306
  /**
295
- * Which platform agent backs each capability. Every field is a DATABASE row id
296
- * supplied by the host; an omitted one disables exactly that capability.
307
+ * WHO fulfils one capability, as the host resolved it. Both halves travel
308
+ * together: an id alone is half an answer whenever the host's switching layer
309
+ * also decided settings for the job.
310
+ */
311
+ interface MessagingAgentIdentity {
312
+ /** The DATABASE agent row id. Never a name, never a prompt. */
313
+ readonly agentId: string;
314
+ /**
315
+ * The settings half, in LLMParams shape, sent verbatim as the turn's
316
+ * `config_overrides`. `null`/omitted = the agent's own settings stand.
317
+ */
318
+ readonly configOverrides?: MatrxJsonObject | null | undefined;
319
+ }
320
+ /**
321
+ * Which platform agent backs each capability. Every field is an identity the
322
+ * host injects; an omitted one disables exactly that capability.
297
323
  */
298
324
  interface MessagingAgents {
299
- readonly catchUp?: string | undefined;
300
- readonly summarize?: string | undefined;
301
- readonly actionItems?: string | undefined;
302
- readonly draftReply?: string | undefined;
325
+ readonly catchUp?: MessagingAgentIdentity | undefined;
326
+ readonly summarize?: MessagingAgentIdentity | undefined;
327
+ readonly actionItems?: MessagingAgentIdentity | undefined;
328
+ readonly draftReply?: MessagingAgentIdentity | undefined;
303
329
  }
304
330
  interface MessagingAiOptions {
305
331
  transport: MatrxTransport;
306
332
  organizationId: string;
307
333
  agents: MessagingAgents;
308
- /** 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
+ */
309
341
  sourceApp?: string | undefined;
310
342
  sourceFeature?: string | undefined;
311
343
  /** Cap on how much transcript is sent. Default 200 messages. */
@@ -317,38 +349,44 @@ interface AiResult {
317
349
  readonly text: string;
318
350
  readonly conversationId: string | null;
319
351
  }
352
+ /** What every capability takes: the conversation, and how to watch the run. */
353
+ interface AiCallArgs {
354
+ conversationId: ConversationId;
355
+ messages: readonly Message[];
356
+ participants: readonly UserSummary[];
357
+ signal?: AbortSignal | undefined;
358
+ /** The accumulated answer so far, after every streamed chunk. */
359
+ onText?: ((fullText: string) => void) | undefined;
360
+ }
320
361
  interface MessagingAi {
321
362
  /** Which capabilities this host actually configured. Drives what the UI offers. */
322
363
  available(): readonly AiCapability[];
323
364
  isAvailable(capability: AiCapability): boolean;
324
- catchMeUp(args: {
325
- conversationId: ConversationId;
326
- messages: readonly Message[];
327
- participants: readonly UserSummary[];
365
+ catchMeUp(args: AiCallArgs & {
366
+ /**
367
+ * Exclusive cutoff — only messages after it are sent. `null` means the
368
+ * caller genuinely has no read mark and the whole window applies.
369
+ */
328
370
  since: string | null;
329
- signal?: AbortSignal;
330
- }): Promise<AiResult>;
331
- summarize(args: {
332
- conversationId: ConversationId;
333
- messages: readonly Message[];
334
- participants: readonly UserSummary[];
335
- signal?: AbortSignal;
336
371
  }): Promise<AiResult>;
337
- extractActionItems(args: {
338
- conversationId: ConversationId;
339
- messages: readonly Message[];
340
- participants: readonly UserSummary[];
341
- signal?: AbortSignal;
342
- }): Promise<AiResult>;
343
- draftReply(args: {
344
- conversationId: ConversationId;
345
- messages: readonly Message[];
346
- participants: readonly UserSummary[];
372
+ summarize(args: AiCallArgs): Promise<AiResult>;
373
+ extractActionItems(args: AiCallArgs): Promise<AiResult>;
374
+ draftReply(args: AiCallArgs & {
347
375
  /** What the human asked for, if anything — this IS a human utterance. */
348
376
  instruction?: string | undefined;
349
- signal?: AbortSignal;
350
377
  }): Promise<AiResult>;
351
378
  }
379
+ /**
380
+ * The exclusive cutoff "what did I miss" means, derived from what the store
381
+ * already knows: the message just BEFORE the first unread one. No unread
382
+ * messages, or a loaded window that does not reach back that far, means there
383
+ * is no honest cutoff — the whole window applies, and the caller says so by
384
+ * passing `null` rather than guessing a timestamp.
385
+ *
386
+ * Framework-free on purpose: a Redux or React Native consumer must not have to
387
+ * re-derive what "unread" means.
388
+ */
389
+ declare function unreadCutoff(messages: readonly Message[], unreadCount: number): string | null;
352
390
  declare function createMessagingAi(options: MessagingAiOptions): MessagingAi;
353
391
 
354
392
  /**
@@ -947,8 +985,23 @@ interface MessagingProviderProps {
947
985
  * the AI actions then do not render at all (never a dead button).
948
986
  */
949
987
  transport?: MatrxTransport | undefined;
950
- /** Which database agent backs each AI capability. */
988
+ /** Which database agent backs each AI capability, with its settings. */
951
989
  agents?: MessagingAgents | undefined;
990
+ /**
991
+ * How much transcript rides an AI call, in messages. The package's 200 is a
992
+ * starting value, not a taste: a host with a knob for it passes the resolved
993
+ * number so an organization can decide.
994
+ */
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;
952
1005
  /** Handlers for actionable messages. Registered once per handler identity. */
953
1006
  actions?: readonly ActionHandler<never>[] | undefined;
954
1007
  /**
@@ -1051,6 +1104,14 @@ declare function useOnlineUserIds(conversationId: ConversationId | null): Readon
1051
1104
  interface UseMessagingAiResult {
1052
1105
  available: readonly AiCapability[];
1053
1106
  isRunning: boolean;
1107
+ /**
1108
+ * The answer as it arrives, while `isRunning`. A surface renders THIS, not a
1109
+ * skeleton: a placeholder that sits until a run finishes is a spinner
1110
+ * standing in for an answer. Empty string until the first chunk lands.
1111
+ */
1112
+ partialText: string;
1113
+ /** Which capability is running (or produced `result`). */
1114
+ activeCapability: AiCapability | null;
1054
1115
  result: AiResult | null;
1055
1116
  error: string | null;
1056
1117
  run: (capability: AiCapability, args?: {
@@ -1458,4 +1519,4 @@ declare function summarizeText(content: string, maxLength?: number): string;
1458
1519
  /** Serialize picked references into a fence the platform's other readers accept. */
1459
1520
  declare function composeFence(references: readonly MatrxReference[]): string;
1460
1521
 
1461
- export { type ActionChoice, type ActionContext, type ActionHandler, type ActionOutcome, type ActionReceipt, type ActionRegistry, type ActorPresentation, AgentTag, 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 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, useComposer, useConversation, useConversations, useMessageAction, useMessagingAi, useMessagingHost, useMessagingSnapshot, useOnlineUserIds, useRequiredMessagingHost, useTypists };
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 };
package/dist/react.js CHANGED
@@ -261,6 +261,12 @@ ${JSON.stringify(references, null, 2)}
261
261
  }
262
262
 
263
263
  // src/core/ai.ts
264
+ function unreadCutoff(messages, unreadCount) {
265
+ if (unreadCount <= 0) return null;
266
+ const index = messages.length - unreadCount - 1;
267
+ if (index < 0) return null;
268
+ return messages[index]?.createdAt ?? null;
269
+ }
264
270
  function nameOf(participants, senderId) {
265
271
  return participants.find((p) => p.userId === senderId)?.displayName ?? senderId;
266
272
  }
@@ -281,22 +287,23 @@ function buildTranscript(messages, participants, limit, since) {
281
287
  }
282
288
  function createMessagingAi(options) {
283
289
  const limit = options.maxTranscriptMessages ?? 200;
284
- function agentFor(capability) {
285
- const agentId = options.agents[capability];
286
- if (typeof agentId !== "string" || agentId.length === 0) {
290
+ function identityFor(capability) {
291
+ const identity = options.agents[capability];
292
+ if (identity === void 0 || identity.agentId.length === 0) {
287
293
  throw new MessagingError(
288
294
  "misconfigured",
289
295
  `Messaging AI capability "${capability}" has no agent configured`,
290
- `Pass agents={{ ${capability}: "<agent-id>" }} to <MessagingProvider>. Agent definitions live in the database, never in this package \u2014 the id is the only part a host injects. Until then the UI hides this action rather than offering a button that cannot work.`
296
+ `Pass agents={{ ${capability}: { agentId: "<agent-id>" } }} to <MessagingProvider>. Agent definitions live in the database, never in this package \u2014 the identity is the only part a host injects. Until then the UI hides this action rather than offering a button that cannot work.`
291
297
  );
292
298
  }
293
- return agentId;
299
+ return identity;
294
300
  }
295
- async function run(capability, variables, userInput, signal) {
296
- const agentId = agentFor(capability);
301
+ async function run(capability, variables, userInput, signal, onText) {
302
+ const identity = identityFor(capability);
303
+ const overrides = identity.configOverrides;
297
304
  const completed = await runAgentToCompletion(
298
305
  options.transport,
299
- agentId,
306
+ identity.agentId,
300
307
  {
301
308
  ...newEphemeralConversationStart(),
302
309
  organization_id: options.organizationId,
@@ -305,9 +312,15 @@ function createMessagingAi(options) {
305
312
  initiation: "user",
306
313
  // THE USER-INPUT LAW: structured content is NEVER here.
307
314
  ...userInput !== null ? { user_input: userInput } : {},
315
+ // The settings half of the injected identity, verbatim. An absent one
316
+ // is OMITTED rather than sent as null: the agent's own settings stand.
317
+ ...overrides != null ? { config_overrides: overrides } : {},
308
318
  variables
309
319
  },
310
- signal !== void 0 ? { signal } : {}
320
+ {
321
+ ...signal !== void 0 ? { signal } : {},
322
+ ...onText !== void 0 ? { onChunk: onText } : {}
323
+ }
311
324
  );
312
325
  return {
313
326
  capability,
@@ -340,27 +353,26 @@ function createMessagingAi(options) {
340
353
  ...args.since != null ? { unread_since: args.since } : {}
341
354
  };
342
355
  }
356
+ function configured(capability) {
357
+ const identity = options.agents[capability];
358
+ return identity !== void 0 && identity.agentId.length > 0;
359
+ }
343
360
  return {
344
361
  available: () => ["catchUp", "summarize", "actionItems", "draftReply"].filter(
345
- (capability) => {
346
- const id = options.agents[capability];
347
- return typeof id === "string" && id.length > 0;
348
- }
362
+ configured
349
363
  ),
350
- isAvailable(capability) {
351
- const id = options.agents[capability];
352
- return typeof id === "string" && id.length > 0;
353
- },
354
- catchMeUp: (args) => run("catchUp", variablesFor(args), null, args.signal),
355
- summarize: (args) => run("summarize", variablesFor(args), null, args.signal),
356
- extractActionItems: (args) => run("actionItems", variablesFor(args), null, args.signal),
364
+ isAvailable: configured,
365
+ catchMeUp: (args) => run("catchUp", variablesFor(args), null, args.signal, args.onText),
366
+ summarize: (args) => run("summarize", variablesFor(args), null, args.signal, args.onText),
367
+ extractActionItems: (args) => run("actionItems", variablesFor(args), null, args.signal, args.onText),
357
368
  draftReply: (args) => run(
358
369
  "draftReply",
359
370
  variablesFor(args),
360
371
  // The ONE genuine human utterance in this module: what the user asked
361
372
  // the drafter for. Everything else rode `variables`.
362
373
  args.instruction !== void 0 && args.instruction.trim().length > 0 ? args.instruction.trim() : null,
363
- args.signal
374
+ args.signal,
375
+ args.onText
364
376
  )
365
377
  };
366
378
  }
@@ -1804,14 +1816,28 @@ function MessagingRuntime(props) {
1804
1816
  }, [engine]);
1805
1817
  const transport = props.transport;
1806
1818
  const agents = props.agents;
1819
+ const maxTranscriptMessages = props.maxTranscriptMessages;
1820
+ const sourceApp = props.sourceApp;
1821
+ const sourceFeature = props.sourceFeature;
1807
1822
  const ai = useMemo(() => {
1808
1823
  if (transport === void 0 || agents === void 0 || !ready) return null;
1809
1824
  return createMessagingAi({
1810
1825
  transport,
1811
1826
  organizationId,
1812
- agents
1827
+ agents,
1828
+ ...maxTranscriptMessages !== void 0 ? { maxTranscriptMessages } : {},
1829
+ ...sourceApp !== void 0 ? { sourceApp } : {},
1830
+ ...sourceFeature !== void 0 ? { sourceFeature } : {}
1813
1831
  });
1814
- }, [transport, agents, ready, organizationId]);
1832
+ }, [
1833
+ transport,
1834
+ agents,
1835
+ ready,
1836
+ organizationId,
1837
+ maxTranscriptMessages,
1838
+ sourceApp,
1839
+ sourceFeature
1840
+ ]);
1815
1841
  const renderers = props.actionRenderers;
1816
1842
  const rendererMap = useMemo(() => {
1817
1843
  const map = /* @__PURE__ */ new Map();
@@ -2139,6 +2165,10 @@ function useMessagingAi(conversationId) {
2139
2165
  const host = useMessagingHost();
2140
2166
  const conversation = useConversation(conversationId);
2141
2167
  const [isRunning, setRunning] = useState(false);
2168
+ const [partialText, setPartialText] = useState("");
2169
+ const [activeCapability, setActiveCapability] = useState(
2170
+ null
2171
+ );
2142
2172
  const [result, setResult] = useState(null);
2143
2173
  const [error, setError] = useState(null);
2144
2174
  const abortRef = useRef2(null);
@@ -2152,11 +2182,15 @@ function useMessagingAi(conversationId) {
2152
2182
  return {
2153
2183
  available: ai?.available() ?? [],
2154
2184
  isRunning,
2185
+ partialText,
2186
+ activeCapability,
2155
2187
  result,
2156
2188
  error,
2157
2189
  clear: () => {
2158
2190
  setResult(null);
2159
2191
  setError(null);
2192
+ setPartialText("");
2193
+ setActiveCapability(null);
2160
2194
  },
2161
2195
  run: (capability, args = {}) => {
2162
2196
  if (ai === null || conversationId === null) return;
@@ -2165,22 +2199,32 @@ function useMessagingAi(conversationId) {
2165
2199
  abortRef.current = controller;
2166
2200
  setRunning(true);
2167
2201
  setError(null);
2202
+ setResult(null);
2203
+ setPartialText("");
2204
+ setActiveCapability(capability);
2168
2205
  const base = {
2169
2206
  conversationId,
2170
2207
  messages: conversation.messages,
2171
2208
  participants: conversation.participants,
2172
- signal: controller.signal
2209
+ signal: controller.signal,
2210
+ // Live progress. Aborted runs stop painting immediately — a cancelled
2211
+ // answer must not keep growing on screen.
2212
+ onText: (text) => {
2213
+ if (!controller.signal.aborted) setPartialText(text);
2214
+ }
2173
2215
  };
2174
2216
  const call = () => {
2175
2217
  switch (capability) {
2176
- case "catchUp": {
2177
- const summary = conversation.summary;
2178
- const self = summary?.participants.find(
2179
- (participant) => participant.userId === host?.identity.userId
2180
- );
2181
- void self;
2182
- return ai.catchMeUp({ ...base, since: null });
2183
- }
2218
+ case "catchUp":
2219
+ return ai.catchMeUp({
2220
+ ...base,
2221
+ // "What did I miss" is about the UNREAD tail. Sending the whole
2222
+ // window made this capability a second Summarize.
2223
+ since: unreadCutoff(
2224
+ conversation.messages,
2225
+ conversation.summary?.unreadCount ?? 0
2226
+ )
2227
+ });
2184
2228
  case "summarize":
2185
2229
  return ai.summarize(base);
2186
2230
  case "actionItems":
@@ -2703,7 +2747,16 @@ function ConversationView(props) {
2703
2747
  },
2704
2748
  capability
2705
2749
  )) }) : null,
2706
- ai.isRunning ? /* @__PURE__ */ jsx4("p", { className: "mx-msg__ai-output", "aria-busy": "true", children: /* @__PURE__ */ jsx4("span", { className: "mx-msg__skeleton", style: { display: "block", height: 11, width: "72%" } }) }) : 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: [
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: [
2751
+ /* @__PURE__ */ jsx4("span", { className: "mx-msg__ai-output-label", children: ai.activeCapability !== null ? `${AI_LABELS[ai.activeCapability]}\u2026` : "Working\u2026" }),
2752
+ /* @__PURE__ */ jsx4(
2753
+ "span",
2754
+ {
2755
+ className: "mx-msg__skeleton",
2756
+ style: { display: "block", height: 11, width: "72%" }
2757
+ }
2758
+ )
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: [
2707
2760
  ai.result.text,
2708
2761
  /* @__PURE__ */ jsx4(
2709
2762
  "button",
@@ -3106,6 +3159,7 @@ export {
3106
3159
  resolveActor,
3107
3160
  splitText,
3108
3161
  summarizeText,
3162
+ unreadCutoff,
3109
3163
  useComposer,
3110
3164
  useConversation,
3111
3165
  useConversations,