@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.cjs CHANGED
@@ -95,6 +95,7 @@ __export(react_exports, {
95
95
  resolveActor: () => resolveActor,
96
96
  splitText: () => splitText,
97
97
  summarizeText: () => summarizeText,
98
+ unreadCutoff: () => unreadCutoff,
98
99
  useComposer: () => useComposer,
99
100
  useConversation: () => useConversation,
100
101
  useConversations: () => useConversations,
@@ -354,6 +355,12 @@ ${JSON.stringify(references, null, 2)}
354
355
  }
355
356
 
356
357
  // src/core/ai.ts
358
+ function unreadCutoff(messages, unreadCount) {
359
+ if (unreadCount <= 0) return null;
360
+ const index = messages.length - unreadCount - 1;
361
+ if (index < 0) return null;
362
+ return messages[index]?.createdAt ?? null;
363
+ }
357
364
  function nameOf(participants, senderId) {
358
365
  return participants.find((p) => p.userId === senderId)?.displayName ?? senderId;
359
366
  }
@@ -374,22 +381,23 @@ function buildTranscript(messages, participants, limit, since) {
374
381
  }
375
382
  function createMessagingAi(options) {
376
383
  const limit = options.maxTranscriptMessages ?? 200;
377
- function agentFor(capability) {
378
- const agentId = options.agents[capability];
379
- if (typeof agentId !== "string" || agentId.length === 0) {
384
+ function identityFor(capability) {
385
+ const identity = options.agents[capability];
386
+ if (identity === void 0 || identity.agentId.length === 0) {
380
387
  throw new MessagingError(
381
388
  "misconfigured",
382
389
  `Messaging AI capability "${capability}" has no agent configured`,
383
- `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.`
390
+ `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.`
384
391
  );
385
392
  }
386
- return agentId;
393
+ return identity;
387
394
  }
388
- async function run(capability, variables, userInput, signal) {
389
- const agentId = agentFor(capability);
395
+ async function run(capability, variables, userInput, signal, onText) {
396
+ const identity = identityFor(capability);
397
+ const overrides = identity.configOverrides;
390
398
  const completed = await (0, import_matrx.runAgentToCompletion)(
391
399
  options.transport,
392
- agentId,
400
+ identity.agentId,
393
401
  {
394
402
  ...(0, import_matrx.newEphemeralConversationStart)(),
395
403
  organization_id: options.organizationId,
@@ -398,9 +406,15 @@ function createMessagingAi(options) {
398
406
  initiation: "user",
399
407
  // THE USER-INPUT LAW: structured content is NEVER here.
400
408
  ...userInput !== null ? { user_input: userInput } : {},
409
+ // The settings half of the injected identity, verbatim. An absent one
410
+ // is OMITTED rather than sent as null: the agent's own settings stand.
411
+ ...overrides != null ? { config_overrides: overrides } : {},
401
412
  variables
402
413
  },
403
- signal !== void 0 ? { signal } : {}
414
+ {
415
+ ...signal !== void 0 ? { signal } : {},
416
+ ...onText !== void 0 ? { onChunk: onText } : {}
417
+ }
404
418
  );
405
419
  return {
406
420
  capability,
@@ -433,27 +447,26 @@ function createMessagingAi(options) {
433
447
  ...args.since != null ? { unread_since: args.since } : {}
434
448
  };
435
449
  }
450
+ function configured(capability) {
451
+ const identity = options.agents[capability];
452
+ return identity !== void 0 && identity.agentId.length > 0;
453
+ }
436
454
  return {
437
455
  available: () => ["catchUp", "summarize", "actionItems", "draftReply"].filter(
438
- (capability) => {
439
- const id = options.agents[capability];
440
- return typeof id === "string" && id.length > 0;
441
- }
456
+ configured
442
457
  ),
443
- isAvailable(capability) {
444
- const id = options.agents[capability];
445
- return typeof id === "string" && id.length > 0;
446
- },
447
- catchMeUp: (args) => run("catchUp", variablesFor(args), null, args.signal),
448
- summarize: (args) => run("summarize", variablesFor(args), null, args.signal),
449
- extractActionItems: (args) => run("actionItems", variablesFor(args), null, args.signal),
458
+ isAvailable: configured,
459
+ catchMeUp: (args) => run("catchUp", variablesFor(args), null, args.signal, args.onText),
460
+ summarize: (args) => run("summarize", variablesFor(args), null, args.signal, args.onText),
461
+ extractActionItems: (args) => run("actionItems", variablesFor(args), null, args.signal, args.onText),
450
462
  draftReply: (args) => run(
451
463
  "draftReply",
452
464
  variablesFor(args),
453
465
  // The ONE genuine human utterance in this module: what the user asked
454
466
  // the drafter for. Everything else rode `variables`.
455
467
  args.instruction !== void 0 && args.instruction.trim().length > 0 ? args.instruction.trim() : null,
456
- args.signal
468
+ args.signal,
469
+ args.onText
457
470
  )
458
471
  };
459
472
  }
@@ -1895,14 +1908,28 @@ function MessagingRuntime(props) {
1895
1908
  }, [engine]);
1896
1909
  const transport = props.transport;
1897
1910
  const agents = props.agents;
1911
+ const maxTranscriptMessages = props.maxTranscriptMessages;
1912
+ const sourceApp = props.sourceApp;
1913
+ const sourceFeature = props.sourceFeature;
1898
1914
  const ai = (0, import_react.useMemo)(() => {
1899
1915
  if (transport === void 0 || agents === void 0 || !ready) return null;
1900
1916
  return createMessagingAi({
1901
1917
  transport,
1902
1918
  organizationId,
1903
- agents
1919
+ agents,
1920
+ ...maxTranscriptMessages !== void 0 ? { maxTranscriptMessages } : {},
1921
+ ...sourceApp !== void 0 ? { sourceApp } : {},
1922
+ ...sourceFeature !== void 0 ? { sourceFeature } : {}
1904
1923
  });
1905
- }, [transport, agents, ready, organizationId]);
1924
+ }, [
1925
+ transport,
1926
+ agents,
1927
+ ready,
1928
+ organizationId,
1929
+ maxTranscriptMessages,
1930
+ sourceApp,
1931
+ sourceFeature
1932
+ ]);
1906
1933
  const renderers = props.actionRenderers;
1907
1934
  const rendererMap = (0, import_react.useMemo)(() => {
1908
1935
  const map = /* @__PURE__ */ new Map();
@@ -2230,6 +2257,10 @@ function useMessagingAi(conversationId) {
2230
2257
  const host = useMessagingHost();
2231
2258
  const conversation = useConversation(conversationId);
2232
2259
  const [isRunning, setRunning] = (0, import_react3.useState)(false);
2260
+ const [partialText, setPartialText] = (0, import_react3.useState)("");
2261
+ const [activeCapability, setActiveCapability] = (0, import_react3.useState)(
2262
+ null
2263
+ );
2233
2264
  const [result, setResult] = (0, import_react3.useState)(null);
2234
2265
  const [error, setError] = (0, import_react3.useState)(null);
2235
2266
  const abortRef = (0, import_react3.useRef)(null);
@@ -2243,11 +2274,15 @@ function useMessagingAi(conversationId) {
2243
2274
  return {
2244
2275
  available: ai?.available() ?? [],
2245
2276
  isRunning,
2277
+ partialText,
2278
+ activeCapability,
2246
2279
  result,
2247
2280
  error,
2248
2281
  clear: () => {
2249
2282
  setResult(null);
2250
2283
  setError(null);
2284
+ setPartialText("");
2285
+ setActiveCapability(null);
2251
2286
  },
2252
2287
  run: (capability, args = {}) => {
2253
2288
  if (ai === null || conversationId === null) return;
@@ -2256,22 +2291,32 @@ function useMessagingAi(conversationId) {
2256
2291
  abortRef.current = controller;
2257
2292
  setRunning(true);
2258
2293
  setError(null);
2294
+ setResult(null);
2295
+ setPartialText("");
2296
+ setActiveCapability(capability);
2259
2297
  const base = {
2260
2298
  conversationId,
2261
2299
  messages: conversation.messages,
2262
2300
  participants: conversation.participants,
2263
- signal: controller.signal
2301
+ signal: controller.signal,
2302
+ // Live progress. Aborted runs stop painting immediately — a cancelled
2303
+ // answer must not keep growing on screen.
2304
+ onText: (text) => {
2305
+ if (!controller.signal.aborted) setPartialText(text);
2306
+ }
2264
2307
  };
2265
2308
  const call = () => {
2266
2309
  switch (capability) {
2267
- case "catchUp": {
2268
- const summary = conversation.summary;
2269
- const self = summary?.participants.find(
2270
- (participant) => participant.userId === host?.identity.userId
2271
- );
2272
- void self;
2273
- return ai.catchMeUp({ ...base, since: null });
2274
- }
2310
+ case "catchUp":
2311
+ return ai.catchMeUp({
2312
+ ...base,
2313
+ // "What did I miss" is about the UNREAD tail. Sending the whole
2314
+ // window made this capability a second Summarize.
2315
+ since: unreadCutoff(
2316
+ conversation.messages,
2317
+ conversation.summary?.unreadCount ?? 0
2318
+ )
2319
+ });
2275
2320
  case "summarize":
2276
2321
  return ai.summarize(base);
2277
2322
  case "actionItems":
@@ -2794,7 +2839,16 @@ function ConversationView(props) {
2794
2839
  },
2795
2840
  capability
2796
2841
  )) }) : null,
2797
- ai.isRunning ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mx-msg__ai-output", "aria-busy": "true", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "mx-msg__skeleton", style: { display: "block", height: 11, width: "72%" } }) }) : ai.error !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mx-msg__ai-output", style: { color: "var(--mx-msg-danger)" }, children: ai.error }) : ai.result !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "mx-msg__ai-output", children: [
2842
+ ai.isRunning ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mx-msg__ai-output", "aria-busy": "true", "aria-live": "polite", children: ai.partialText.length > 0 ? ai.partialText : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
2843
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "mx-msg__ai-output-label", children: ai.activeCapability !== null ? `${AI_LABELS[ai.activeCapability]}\u2026` : "Working\u2026" }),
2844
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2845
+ "span",
2846
+ {
2847
+ className: "mx-msg__skeleton",
2848
+ style: { display: "block", height: 11, width: "72%" }
2849
+ }
2850
+ )
2851
+ ] }) }) : ai.error !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mx-msg__ai-output", style: { color: "var(--mx-msg-danger)" }, children: ai.error }) : ai.result !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "mx-msg__ai-output", children: [
2798
2852
  ai.result.text,
2799
2853
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2800
2854
  "button",