@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.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
|
|
285
|
-
const
|
|
286
|
-
if (
|
|
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
|
|
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
|
|
299
|
+
return identity;
|
|
294
300
|
}
|
|
295
|
-
async function run(capability, variables, userInput, signal) {
|
|
296
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
346
|
-
const id = options.agents[capability];
|
|
347
|
-
return typeof id === "string" && id.length > 0;
|
|
348
|
-
}
|
|
362
|
+
configured
|
|
349
363
|
),
|
|
350
|
-
isAvailable
|
|
351
|
-
|
|
352
|
-
|
|
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
|
}
|
|
@@ -1388,12 +1400,13 @@ function requireOrg(organizationId, operation) {
|
|
|
1388
1400
|
return organizationId;
|
|
1389
1401
|
}
|
|
1390
1402
|
function createMessagingRepository(options) {
|
|
1391
|
-
const {
|
|
1403
|
+
const { identity } = options;
|
|
1392
1404
|
const org = requireOrg(identity.organizationId, "createMessagingRepository");
|
|
1393
1405
|
const userCache = createReadCache({
|
|
1394
1406
|
ttlMs: options.userTtlMs ?? 5 * 6e4,
|
|
1395
1407
|
...options.now !== void 0 ? { now: options.now } : {}
|
|
1396
1408
|
});
|
|
1409
|
+
const client = options.client;
|
|
1397
1410
|
const db = () => client.schema(MESSAGING_SCHEMA);
|
|
1398
1411
|
const rpcDb = () => client.schema(MESSAGING_RPC_SCHEMA);
|
|
1399
1412
|
async function withSessionRetry(operation, run) {
|
|
@@ -1721,7 +1734,14 @@ function MessagingProvider(props) {
|
|
|
1721
1734
|
const ready = client != null && typeof userId === "string" && userId.length > 0 && typeof organizationId === "string" && organizationId.length > 0;
|
|
1722
1735
|
const hostProvidesRealtime = useIsRealtimeProviderMounted();
|
|
1723
1736
|
if (hostProvidesRealtime) return /* @__PURE__ */ jsx(MessagingRuntime, { ...props });
|
|
1724
|
-
return /* @__PURE__ */ jsx(
|
|
1737
|
+
return /* @__PURE__ */ jsx(
|
|
1738
|
+
RealtimeProvider,
|
|
1739
|
+
{
|
|
1740
|
+
client: ready ? client : null,
|
|
1741
|
+
actorId: userId ?? void 0,
|
|
1742
|
+
children: /* @__PURE__ */ jsx(MessagingRuntime, { ...props })
|
|
1743
|
+
}
|
|
1744
|
+
);
|
|
1725
1745
|
}
|
|
1726
1746
|
function MessagingRuntime(props) {
|
|
1727
1747
|
const { client, userId, organizationId, children } = props;
|
|
@@ -1750,6 +1770,10 @@ function MessagingRuntime(props) {
|
|
|
1750
1770
|
(diagnosticRef.current ?? defaultDiagnostics)(event);
|
|
1751
1771
|
};
|
|
1752
1772
|
const repository = createMessagingRepository({
|
|
1773
|
+
// THE ONE NARROWING, and it lives INSIDE the package. The prop is shallow
|
|
1774
|
+
// so a host's fully-typed client is assignable at all (see
|
|
1775
|
+
// supabase-shape.ts); everything below this line works against the rich
|
|
1776
|
+
// contract, which `supabase-shape.test.ts` proves a real client honors.
|
|
1753
1777
|
client,
|
|
1754
1778
|
identity,
|
|
1755
1779
|
...props.resolveSession !== void 0 ? { resolveSession: props.resolveSession } : {}
|
|
@@ -1792,14 +1816,16 @@ function MessagingRuntime(props) {
|
|
|
1792
1816
|
}, [engine]);
|
|
1793
1817
|
const transport = props.transport;
|
|
1794
1818
|
const agents = props.agents;
|
|
1819
|
+
const maxTranscriptMessages = props.maxTranscriptMessages;
|
|
1795
1820
|
const ai = useMemo(() => {
|
|
1796
1821
|
if (transport === void 0 || agents === void 0 || !ready) return null;
|
|
1797
1822
|
return createMessagingAi({
|
|
1798
1823
|
transport,
|
|
1799
1824
|
organizationId,
|
|
1800
|
-
agents
|
|
1825
|
+
agents,
|
|
1826
|
+
...maxTranscriptMessages !== void 0 ? { maxTranscriptMessages } : {}
|
|
1801
1827
|
});
|
|
1802
|
-
}, [transport, agents, ready, organizationId]);
|
|
1828
|
+
}, [transport, agents, ready, organizationId, maxTranscriptMessages]);
|
|
1803
1829
|
const renderers = props.actionRenderers;
|
|
1804
1830
|
const rendererMap = useMemo(() => {
|
|
1805
1831
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -2127,6 +2153,10 @@ function useMessagingAi(conversationId) {
|
|
|
2127
2153
|
const host = useMessagingHost();
|
|
2128
2154
|
const conversation = useConversation(conversationId);
|
|
2129
2155
|
const [isRunning, setRunning] = useState(false);
|
|
2156
|
+
const [partialText, setPartialText] = useState("");
|
|
2157
|
+
const [activeCapability, setActiveCapability] = useState(
|
|
2158
|
+
null
|
|
2159
|
+
);
|
|
2130
2160
|
const [result, setResult] = useState(null);
|
|
2131
2161
|
const [error, setError] = useState(null);
|
|
2132
2162
|
const abortRef = useRef2(null);
|
|
@@ -2140,11 +2170,15 @@ function useMessagingAi(conversationId) {
|
|
|
2140
2170
|
return {
|
|
2141
2171
|
available: ai?.available() ?? [],
|
|
2142
2172
|
isRunning,
|
|
2173
|
+
partialText,
|
|
2174
|
+
activeCapability,
|
|
2143
2175
|
result,
|
|
2144
2176
|
error,
|
|
2145
2177
|
clear: () => {
|
|
2146
2178
|
setResult(null);
|
|
2147
2179
|
setError(null);
|
|
2180
|
+
setPartialText("");
|
|
2181
|
+
setActiveCapability(null);
|
|
2148
2182
|
},
|
|
2149
2183
|
run: (capability, args = {}) => {
|
|
2150
2184
|
if (ai === null || conversationId === null) return;
|
|
@@ -2153,22 +2187,32 @@ function useMessagingAi(conversationId) {
|
|
|
2153
2187
|
abortRef.current = controller;
|
|
2154
2188
|
setRunning(true);
|
|
2155
2189
|
setError(null);
|
|
2190
|
+
setResult(null);
|
|
2191
|
+
setPartialText("");
|
|
2192
|
+
setActiveCapability(capability);
|
|
2156
2193
|
const base = {
|
|
2157
2194
|
conversationId,
|
|
2158
2195
|
messages: conversation.messages,
|
|
2159
2196
|
participants: conversation.participants,
|
|
2160
|
-
signal: controller.signal
|
|
2197
|
+
signal: controller.signal,
|
|
2198
|
+
// Live progress. Aborted runs stop painting immediately — a cancelled
|
|
2199
|
+
// answer must not keep growing on screen.
|
|
2200
|
+
onText: (text) => {
|
|
2201
|
+
if (!controller.signal.aborted) setPartialText(text);
|
|
2202
|
+
}
|
|
2161
2203
|
};
|
|
2162
2204
|
const call = () => {
|
|
2163
2205
|
switch (capability) {
|
|
2164
|
-
case "catchUp":
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2206
|
+
case "catchUp":
|
|
2207
|
+
return ai.catchMeUp({
|
|
2208
|
+
...base,
|
|
2209
|
+
// "What did I miss" is about the UNREAD tail. Sending the whole
|
|
2210
|
+
// window made this capability a second Summarize.
|
|
2211
|
+
since: unreadCutoff(
|
|
2212
|
+
conversation.messages,
|
|
2213
|
+
conversation.summary?.unreadCount ?? 0
|
|
2214
|
+
)
|
|
2215
|
+
});
|
|
2172
2216
|
case "summarize":
|
|
2173
2217
|
return ai.summarize(base);
|
|
2174
2218
|
case "actionItems":
|
|
@@ -2691,7 +2735,16 @@ function ConversationView(props) {
|
|
|
2691
2735
|
},
|
|
2692
2736
|
capability
|
|
2693
2737
|
)) }) : null,
|
|
2694
|
-
ai.isRunning ? /* @__PURE__ */ jsx4("p", { className: "mx-msg__ai-output", "aria-busy": "true",
|
|
2738
|
+
ai.isRunning ? /* @__PURE__ */ jsx4("p", { className: "mx-msg__ai-output", "aria-busy": "true", "aria-live": "polite", children: ai.partialText.length > 0 ? ai.partialText : /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
2739
|
+
/* @__PURE__ */ jsx4("span", { className: "mx-msg__ai-output-label", children: ai.activeCapability !== null ? `${AI_LABELS[ai.activeCapability]}\u2026` : "Working\u2026" }),
|
|
2740
|
+
/* @__PURE__ */ jsx4(
|
|
2741
|
+
"span",
|
|
2742
|
+
{
|
|
2743
|
+
className: "mx-msg__skeleton",
|
|
2744
|
+
style: { display: "block", height: 11, width: "72%" }
|
|
2745
|
+
}
|
|
2746
|
+
)
|
|
2747
|
+
] }) }) : ai.error !== null ? /* @__PURE__ */ jsx4("p", { className: "mx-msg__ai-output", style: { color: "var(--mx-msg-danger)" }, children: ai.error }) : ai.result !== null ? /* @__PURE__ */ jsxs2("p", { className: "mx-msg__ai-output", children: [
|
|
2695
2748
|
ai.result.text,
|
|
2696
2749
|
/* @__PURE__ */ jsx4(
|
|
2697
2750
|
"button",
|
|
@@ -3094,6 +3147,7 @@ export {
|
|
|
3094
3147
|
resolveActor,
|
|
3095
3148
|
splitText,
|
|
3096
3149
|
summarizeText,
|
|
3150
|
+
unreadCutoff,
|
|
3097
3151
|
useComposer,
|
|
3098
3152
|
useConversation,
|
|
3099
3153
|
useConversations,
|