@ai-matrx/messaging 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +71 -0
- package/README.md +23 -4
- package/dist/index.cjs +37 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +108 -36
- package/dist/index.d.ts +108 -36
- package/dist/index.js +37 -23
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +89 -35
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +120 -41
- package/dist/react.d.ts +120 -41
- package/dist/react.js +89 -35
- package/dist/react.js.map +1 -1
- package/dist/styles.css +10 -0
- package/package.json +1 -1
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
|
|
378
|
-
const
|
|
379
|
-
if (
|
|
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
|
|
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
|
|
393
|
+
return identity;
|
|
387
394
|
}
|
|
388
|
-
async function run(capability, variables, userInput, signal) {
|
|
389
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
439
|
-
const id = options.agents[capability];
|
|
440
|
-
return typeof id === "string" && id.length > 0;
|
|
441
|
-
}
|
|
456
|
+
configured
|
|
442
457
|
),
|
|
443
|
-
isAvailable
|
|
444
|
-
|
|
445
|
-
|
|
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
|
}
|
|
@@ -1479,12 +1492,13 @@ function requireOrg(organizationId, operation) {
|
|
|
1479
1492
|
return organizationId;
|
|
1480
1493
|
}
|
|
1481
1494
|
function createMessagingRepository(options) {
|
|
1482
|
-
const {
|
|
1495
|
+
const { identity } = options;
|
|
1483
1496
|
const org = requireOrg(identity.organizationId, "createMessagingRepository");
|
|
1484
1497
|
const userCache = createReadCache({
|
|
1485
1498
|
ttlMs: options.userTtlMs ?? 5 * 6e4,
|
|
1486
1499
|
...options.now !== void 0 ? { now: options.now } : {}
|
|
1487
1500
|
});
|
|
1501
|
+
const client = options.client;
|
|
1488
1502
|
const db = () => client.schema(MESSAGING_SCHEMA);
|
|
1489
1503
|
const rpcDb = () => client.schema(MESSAGING_RPC_SCHEMA);
|
|
1490
1504
|
async function withSessionRetry(operation, run) {
|
|
@@ -1812,7 +1826,14 @@ function MessagingProvider(props) {
|
|
|
1812
1826
|
const ready = client != null && typeof userId === "string" && userId.length > 0 && typeof organizationId === "string" && organizationId.length > 0;
|
|
1813
1827
|
const hostProvidesRealtime = (0, import_react2.useIsRealtimeProviderMounted)();
|
|
1814
1828
|
if (hostProvidesRealtime) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MessagingRuntime, { ...props });
|
|
1815
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1829
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1830
|
+
import_react2.RealtimeProvider,
|
|
1831
|
+
{
|
|
1832
|
+
client: ready ? client : null,
|
|
1833
|
+
actorId: userId ?? void 0,
|
|
1834
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MessagingRuntime, { ...props })
|
|
1835
|
+
}
|
|
1836
|
+
);
|
|
1816
1837
|
}
|
|
1817
1838
|
function MessagingRuntime(props) {
|
|
1818
1839
|
const { client, userId, organizationId, children } = props;
|
|
@@ -1841,6 +1862,10 @@ function MessagingRuntime(props) {
|
|
|
1841
1862
|
(diagnosticRef.current ?? defaultDiagnostics)(event);
|
|
1842
1863
|
};
|
|
1843
1864
|
const repository = createMessagingRepository({
|
|
1865
|
+
// THE ONE NARROWING, and it lives INSIDE the package. The prop is shallow
|
|
1866
|
+
// so a host's fully-typed client is assignable at all (see
|
|
1867
|
+
// supabase-shape.ts); everything below this line works against the rich
|
|
1868
|
+
// contract, which `supabase-shape.test.ts` proves a real client honors.
|
|
1844
1869
|
client,
|
|
1845
1870
|
identity,
|
|
1846
1871
|
...props.resolveSession !== void 0 ? { resolveSession: props.resolveSession } : {}
|
|
@@ -1883,14 +1908,16 @@ function MessagingRuntime(props) {
|
|
|
1883
1908
|
}, [engine]);
|
|
1884
1909
|
const transport = props.transport;
|
|
1885
1910
|
const agents = props.agents;
|
|
1911
|
+
const maxTranscriptMessages = props.maxTranscriptMessages;
|
|
1886
1912
|
const ai = (0, import_react.useMemo)(() => {
|
|
1887
1913
|
if (transport === void 0 || agents === void 0 || !ready) return null;
|
|
1888
1914
|
return createMessagingAi({
|
|
1889
1915
|
transport,
|
|
1890
1916
|
organizationId,
|
|
1891
|
-
agents
|
|
1917
|
+
agents,
|
|
1918
|
+
...maxTranscriptMessages !== void 0 ? { maxTranscriptMessages } : {}
|
|
1892
1919
|
});
|
|
1893
|
-
}, [transport, agents, ready, organizationId]);
|
|
1920
|
+
}, [transport, agents, ready, organizationId, maxTranscriptMessages]);
|
|
1894
1921
|
const renderers = props.actionRenderers;
|
|
1895
1922
|
const rendererMap = (0, import_react.useMemo)(() => {
|
|
1896
1923
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -2218,6 +2245,10 @@ function useMessagingAi(conversationId) {
|
|
|
2218
2245
|
const host = useMessagingHost();
|
|
2219
2246
|
const conversation = useConversation(conversationId);
|
|
2220
2247
|
const [isRunning, setRunning] = (0, import_react3.useState)(false);
|
|
2248
|
+
const [partialText, setPartialText] = (0, import_react3.useState)("");
|
|
2249
|
+
const [activeCapability, setActiveCapability] = (0, import_react3.useState)(
|
|
2250
|
+
null
|
|
2251
|
+
);
|
|
2221
2252
|
const [result, setResult] = (0, import_react3.useState)(null);
|
|
2222
2253
|
const [error, setError] = (0, import_react3.useState)(null);
|
|
2223
2254
|
const abortRef = (0, import_react3.useRef)(null);
|
|
@@ -2231,11 +2262,15 @@ function useMessagingAi(conversationId) {
|
|
|
2231
2262
|
return {
|
|
2232
2263
|
available: ai?.available() ?? [],
|
|
2233
2264
|
isRunning,
|
|
2265
|
+
partialText,
|
|
2266
|
+
activeCapability,
|
|
2234
2267
|
result,
|
|
2235
2268
|
error,
|
|
2236
2269
|
clear: () => {
|
|
2237
2270
|
setResult(null);
|
|
2238
2271
|
setError(null);
|
|
2272
|
+
setPartialText("");
|
|
2273
|
+
setActiveCapability(null);
|
|
2239
2274
|
},
|
|
2240
2275
|
run: (capability, args = {}) => {
|
|
2241
2276
|
if (ai === null || conversationId === null) return;
|
|
@@ -2244,22 +2279,32 @@ function useMessagingAi(conversationId) {
|
|
|
2244
2279
|
abortRef.current = controller;
|
|
2245
2280
|
setRunning(true);
|
|
2246
2281
|
setError(null);
|
|
2282
|
+
setResult(null);
|
|
2283
|
+
setPartialText("");
|
|
2284
|
+
setActiveCapability(capability);
|
|
2247
2285
|
const base = {
|
|
2248
2286
|
conversationId,
|
|
2249
2287
|
messages: conversation.messages,
|
|
2250
2288
|
participants: conversation.participants,
|
|
2251
|
-
signal: controller.signal
|
|
2289
|
+
signal: controller.signal,
|
|
2290
|
+
// Live progress. Aborted runs stop painting immediately — a cancelled
|
|
2291
|
+
// answer must not keep growing on screen.
|
|
2292
|
+
onText: (text) => {
|
|
2293
|
+
if (!controller.signal.aborted) setPartialText(text);
|
|
2294
|
+
}
|
|
2252
2295
|
};
|
|
2253
2296
|
const call = () => {
|
|
2254
2297
|
switch (capability) {
|
|
2255
|
-
case "catchUp":
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2298
|
+
case "catchUp":
|
|
2299
|
+
return ai.catchMeUp({
|
|
2300
|
+
...base,
|
|
2301
|
+
// "What did I miss" is about the UNREAD tail. Sending the whole
|
|
2302
|
+
// window made this capability a second Summarize.
|
|
2303
|
+
since: unreadCutoff(
|
|
2304
|
+
conversation.messages,
|
|
2305
|
+
conversation.summary?.unreadCount ?? 0
|
|
2306
|
+
)
|
|
2307
|
+
});
|
|
2263
2308
|
case "summarize":
|
|
2264
2309
|
return ai.summarize(base);
|
|
2265
2310
|
case "actionItems":
|
|
@@ -2782,7 +2827,16 @@ function ConversationView(props) {
|
|
|
2782
2827
|
},
|
|
2783
2828
|
capability
|
|
2784
2829
|
)) }) : null,
|
|
2785
|
-
ai.isRunning ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mx-msg__ai-output", "aria-busy": "true",
|
|
2830
|
+
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: [
|
|
2831
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "mx-msg__ai-output-label", children: ai.activeCapability !== null ? `${AI_LABELS[ai.activeCapability]}\u2026` : "Working\u2026" }),
|
|
2832
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2833
|
+
"span",
|
|
2834
|
+
{
|
|
2835
|
+
className: "mx-msg__skeleton",
|
|
2836
|
+
style: { display: "block", height: 11, width: "72%" }
|
|
2837
|
+
}
|
|
2838
|
+
)
|
|
2839
|
+
] }) }) : 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: [
|
|
2786
2840
|
ai.result.text,
|
|
2787
2841
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2788
2842
|
"button",
|