@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/CHANGELOG.md +54 -0
- package/README.md +29 -4
- package/dist/index.cjs +35 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +73 -35
- package/dist/index.d.ts +73 -35
- package/dist/index.js +35 -22
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +87 -33
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +97 -36
- package/dist/react.d.ts +97 -36
- package/dist/react.js +87 -33
- package/dist/react.js.map +1 -1
- package/dist/styles.css +10 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
# Changelog — `@ai-matrx/messaging`
|
|
2
2
|
|
|
3
|
+
## 0.10.1 — 2026-09-07
|
|
4
|
+
|
|
5
|
+
**Every AI call would have been refused by the real server.** `<MessagingProvider>` never forwarded
|
|
6
|
+
`sourceApp` / `sourceFeature` to the core, so every run went out stamped with this package's
|
|
7
|
+
placeholder `ai-matrx` / `messaging.<capability>` — and the AI Matrx server keeps a REGISTER of
|
|
8
|
+
legal producers and answers an unregistered one with a 422. Found by putting a real request on the
|
|
9
|
+
wire, not by reading the code; the same shape as the 0.7.0 wrong-schema defect.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **`<MessagingProvider sourceApp sourceFeature>`**, forwarded to `createMessagingAi`. Only the
|
|
14
|
+
host knows its registered slugs, so the host passes them; the package's defaults are documented
|
|
15
|
+
as placeholders that will not survive a real server.
|
|
16
|
+
|
|
17
|
+
## 0.10.0 — 2026-09-07
|
|
18
|
+
|
|
19
|
+
**The four AI-native intelligences rendered nothing in the first host that shipped them, and
|
|
20
|
+
would have shown a skeleton instead of an answer if they had.** Three defects, all found by
|
|
21
|
+
wiring the package to a real switching layer rather than by reading it.
|
|
22
|
+
|
|
23
|
+
### Changed (breaking, one line at the call site)
|
|
24
|
+
|
|
25
|
+
- **`agents` now takes an IDENTITY per capability, not a bare id.**
|
|
26
|
+
`{ summarize: "id" }` becomes `{ summarize: { agentId: "id", configOverrides } }`. A host that
|
|
27
|
+
resolves a capability's agent from a switching layer resolves a PAIR — which agent, and the
|
|
28
|
+
settings that layer decided for it. Taking only the id silently dropped the settings half on
|
|
29
|
+
every call. `configOverrides` rides to the server as the turn's `config_overrides`, verbatim;
|
|
30
|
+
omitted, the field is not sent at all and the agent's own settings stand.
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- **AI runs STREAM.** Every capability takes `onText`, and `useMessagingAi` exposes
|
|
35
|
+
`partialText` + `activeCapability`. `ConversationView` renders the answer as it arrives; the
|
|
36
|
+
skeleton survives only for the gap before the first chunk, where it now names the job that is
|
|
37
|
+
running. A placeholder that sat until a run finished was a spinner standing in for an answer.
|
|
38
|
+
- **`<MessagingProvider maxTranscriptMessages>`** — the provider never forwarded the core's
|
|
39
|
+
transcript cap, so the 200 in this package was unreachable taste. A host with an
|
|
40
|
+
organization-configurable knob passes the resolved number.
|
|
41
|
+
- **`unreadCutoff(messages, unreadCount)`**, exported from the framework-free core.
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- **"Catch me up" was a second "Summarize".** It always passed `since: null`, so it sent the
|
|
46
|
+
whole loaded window and answered the question nobody asked. It now derives the exclusive
|
|
47
|
+
cutoff from the store's own unread count — the message just before the first unread one — and
|
|
48
|
+
passes `null` only when there is genuinely no honest cutoff (nothing unread, or a loaded
|
|
49
|
+
window that does not reach back that far). Never a guessed timestamp.
|
|
50
|
+
|
|
51
|
+
### Consumer action (C28)
|
|
52
|
+
|
|
53
|
+
Change `agents={{ summarize: id }}` to `agents={{ summarize: { agentId: id } }}` and pass
|
|
54
|
+
`configOverrides` when your resolution layer produces them. If you render the AI output
|
|
55
|
+
yourself, render `partialText` while `isRunning`.
|
|
56
|
+
|
|
3
57
|
## 0.9.0 — 2026-09-07
|
|
4
58
|
|
|
5
59
|
**`<MessagingProvider client={supabase}>` did not typecheck for a host with generated Supabase
|
package/README.md
CHANGED
|
@@ -166,14 +166,39 @@ messages, keyset pagination, and the whole polished UI. There is nothing else to
|
|
|
166
166
|
organizationId={org.id}
|
|
167
167
|
transport={matrxTransport} // from @ai-matrx/agents/matrx
|
|
168
168
|
agents={{
|
|
169
|
-
catchUp:
|
|
169
|
+
catchUp: { agentId: "…", configOverrides: { … } },
|
|
170
|
+
summarize: { agentId: "…" },
|
|
171
|
+
actionItems: { agentId: "…" },
|
|
172
|
+
draftReply: { agentId: "…" },
|
|
170
173
|
}}
|
|
174
|
+
maxTranscriptMessages={200} // your knob, not our taste
|
|
175
|
+
sourceApp="matrx-frontend" // YOUR registered producer slugs
|
|
176
|
+
sourceFeature="messages"
|
|
171
177
|
>
|
|
172
178
|
```
|
|
173
179
|
|
|
174
|
-
Agent definitions live in the **database**, never in this package — the
|
|
175
|
-
host
|
|
176
|
-
that fails when pressed.
|
|
180
|
+
Agent definitions live in the **database**, never in this package — the injected **identity** is
|
|
181
|
+
the only part a host supplies. A capability with no identity **does not render at all**; it is
|
|
182
|
+
never a dead button that fails when pressed.
|
|
183
|
+
|
|
184
|
+
**An identity is both halves.** If your app resolves a capability's agent from a switching layer
|
|
185
|
+
that also decides settings for the job, pass `configOverrides` with the id — it is sent verbatim
|
|
186
|
+
as the turn's `config_overrides`. Passing the id alone silently drops the settings half. Omit it
|
|
187
|
+
and nothing is sent, so the agent's own settings stand.
|
|
188
|
+
|
|
189
|
+
**A run the user watches streams.** `useMessagingAi` reports `partialText` after every chunk and
|
|
190
|
+
`ConversationView` renders it; the skeleton appears only in the gap before the first chunk, and
|
|
191
|
+
names the job it is waiting on. If you render the output yourself, render `partialText` while
|
|
192
|
+
`isRunning` — a placeholder that sits until the run finishes is a spinner standing in for an
|
|
193
|
+
answer.
|
|
194
|
+
|
|
195
|
+
🚨 **`sourceApp` / `sourceFeature` are not analytics garnish.** The AI Matrx server keeps a register
|
|
196
|
+
of legal producers and answers an unregistered one with a **422** — so every AI call fails until you
|
|
197
|
+
pass slugs your server knows. This package's defaults are placeholders; only you know yours.
|
|
198
|
+
|
|
199
|
+
**"Catch me up" means the unread tail**, derived from the store's own unread count. Nothing
|
|
200
|
+
unread, or a loaded window that does not reach back to the read mark, means there is no honest
|
|
201
|
+
cutoff and the whole window applies — never a guessed timestamp.
|
|
177
202
|
|
|
178
203
|
### Making references open
|
|
179
204
|
|
package/dist/index.cjs
CHANGED
|
@@ -63,7 +63,8 @@ __export(src_exports, {
|
|
|
63
63
|
projectUserSummary: () => projectUserSummary,
|
|
64
64
|
resolveActor: () => resolveActor,
|
|
65
65
|
splitText: () => splitText,
|
|
66
|
-
summarizeText: () => summarizeText
|
|
66
|
+
summarizeText: () => summarizeText,
|
|
67
|
+
unreadCutoff: () => unreadCutoff
|
|
67
68
|
});
|
|
68
69
|
module.exports = __toCommonJS(src_exports);
|
|
69
70
|
|
|
@@ -357,6 +358,12 @@ ${JSON.stringify(references, null, 2)}
|
|
|
357
358
|
}
|
|
358
359
|
|
|
359
360
|
// src/core/ai.ts
|
|
361
|
+
function unreadCutoff(messages, unreadCount) {
|
|
362
|
+
if (unreadCount <= 0) return null;
|
|
363
|
+
const index = messages.length - unreadCount - 1;
|
|
364
|
+
if (index < 0) return null;
|
|
365
|
+
return messages[index]?.createdAt ?? null;
|
|
366
|
+
}
|
|
360
367
|
function nameOf(participants, senderId) {
|
|
361
368
|
return participants.find((p) => p.userId === senderId)?.displayName ?? senderId;
|
|
362
369
|
}
|
|
@@ -377,22 +384,23 @@ function buildTranscript(messages, participants, limit, since) {
|
|
|
377
384
|
}
|
|
378
385
|
function createMessagingAi(options) {
|
|
379
386
|
const limit = options.maxTranscriptMessages ?? 200;
|
|
380
|
-
function
|
|
381
|
-
const
|
|
382
|
-
if (
|
|
387
|
+
function identityFor(capability) {
|
|
388
|
+
const identity = options.agents[capability];
|
|
389
|
+
if (identity === void 0 || identity.agentId.length === 0) {
|
|
383
390
|
throw new MessagingError(
|
|
384
391
|
"misconfigured",
|
|
385
392
|
`Messaging AI capability "${capability}" has no agent configured`,
|
|
386
|
-
`Pass agents={{ ${capability}: "<agent-id>" }} to <MessagingProvider>. Agent definitions live in the database, never in this package \u2014 the
|
|
393
|
+
`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.`
|
|
387
394
|
);
|
|
388
395
|
}
|
|
389
|
-
return
|
|
396
|
+
return identity;
|
|
390
397
|
}
|
|
391
|
-
async function run(capability, variables, userInput, signal) {
|
|
392
|
-
const
|
|
398
|
+
async function run(capability, variables, userInput, signal, onText) {
|
|
399
|
+
const identity = identityFor(capability);
|
|
400
|
+
const overrides = identity.configOverrides;
|
|
393
401
|
const completed = await (0, import_matrx.runAgentToCompletion)(
|
|
394
402
|
options.transport,
|
|
395
|
-
agentId,
|
|
403
|
+
identity.agentId,
|
|
396
404
|
{
|
|
397
405
|
...(0, import_matrx.newEphemeralConversationStart)(),
|
|
398
406
|
organization_id: options.organizationId,
|
|
@@ -401,9 +409,15 @@ function createMessagingAi(options) {
|
|
|
401
409
|
initiation: "user",
|
|
402
410
|
// THE USER-INPUT LAW: structured content is NEVER here.
|
|
403
411
|
...userInput !== null ? { user_input: userInput } : {},
|
|
412
|
+
// The settings half of the injected identity, verbatim. An absent one
|
|
413
|
+
// is OMITTED rather than sent as null: the agent's own settings stand.
|
|
414
|
+
...overrides != null ? { config_overrides: overrides } : {},
|
|
404
415
|
variables
|
|
405
416
|
},
|
|
406
|
-
|
|
417
|
+
{
|
|
418
|
+
...signal !== void 0 ? { signal } : {},
|
|
419
|
+
...onText !== void 0 ? { onChunk: onText } : {}
|
|
420
|
+
}
|
|
407
421
|
);
|
|
408
422
|
return {
|
|
409
423
|
capability,
|
|
@@ -436,27 +450,26 @@ function createMessagingAi(options) {
|
|
|
436
450
|
...args.since != null ? { unread_since: args.since } : {}
|
|
437
451
|
};
|
|
438
452
|
}
|
|
453
|
+
function configured(capability) {
|
|
454
|
+
const identity = options.agents[capability];
|
|
455
|
+
return identity !== void 0 && identity.agentId.length > 0;
|
|
456
|
+
}
|
|
439
457
|
return {
|
|
440
458
|
available: () => ["catchUp", "summarize", "actionItems", "draftReply"].filter(
|
|
441
|
-
|
|
442
|
-
const id = options.agents[capability];
|
|
443
|
-
return typeof id === "string" && id.length > 0;
|
|
444
|
-
}
|
|
459
|
+
configured
|
|
445
460
|
),
|
|
446
|
-
isAvailable
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
catchMeUp: (args) => run("catchUp", variablesFor(args), null, args.signal),
|
|
451
|
-
summarize: (args) => run("summarize", variablesFor(args), null, args.signal),
|
|
452
|
-
extractActionItems: (args) => run("actionItems", variablesFor(args), null, args.signal),
|
|
461
|
+
isAvailable: configured,
|
|
462
|
+
catchMeUp: (args) => run("catchUp", variablesFor(args), null, args.signal, args.onText),
|
|
463
|
+
summarize: (args) => run("summarize", variablesFor(args), null, args.signal, args.onText),
|
|
464
|
+
extractActionItems: (args) => run("actionItems", variablesFor(args), null, args.signal, args.onText),
|
|
453
465
|
draftReply: (args) => run(
|
|
454
466
|
"draftReply",
|
|
455
467
|
variablesFor(args),
|
|
456
468
|
// The ONE genuine human utterance in this module: what the user asked
|
|
457
469
|
// the drafter for. Everything else rode `variables`.
|
|
458
470
|
args.instruction !== void 0 && args.instruction.trim().length > 0 ? args.instruction.trim() : null,
|
|
459
|
-
args.signal
|
|
471
|
+
args.signal,
|
|
472
|
+
args.onText
|
|
460
473
|
)
|
|
461
474
|
};
|
|
462
475
|
}
|