@ai-matrx/messaging 0.1.2 → 0.2.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 +49 -0
- package/README.md +32 -0
- package/dist/react.cjs +36 -4
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +37 -2
- package/dist/react.d.ts +37 -2
- package/dist/react.js +41 -5
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,54 @@
|
|
|
1
1
|
# Changelog — `@ai-matrx/messaging`
|
|
2
2
|
|
|
3
|
+
## 0.2.0 — 2026-09-07
|
|
4
|
+
|
|
5
|
+
**Both fixes came from the first real adoption** (matrx-frontend, the C9 full-elimination swap).
|
|
6
|
+
Each is a case of the package forcing the host to keep something it should not have had to keep.
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- **The provider no longer builds a SECOND realtime manager under a host that already has one.**
|
|
11
|
+
`<MessagingProvider>` mounted `<RealtimeProvider>` unconditionally. An app that wires
|
|
12
|
+
`@ai-matrx/realtime` has exactly ONE provider on purpose — the manager owns the write ledger
|
|
13
|
+
that makes an optimistic write's own echo recognizable — so a second manager beneath it gave
|
|
14
|
+
this package its own ledger and its own channels: our sends looked remote to the host's
|
|
15
|
+
realtime consumers, and both sides subscribed to the same rooms twice. The provider now asks
|
|
16
|
+
`useIsRealtimeProviderMounted()` (`@ai-matrx/realtime` 0.3.0) and rides the host's manager when
|
|
17
|
+
there is one; an app that wires none still gets a provider for free, so R7 is unchanged.
|
|
18
|
+
Probing `useRealtimeManager() !== null` would NOT have worked — it is also null while a mounted
|
|
19
|
+
host provider waits for its Supabase client.
|
|
20
|
+
- **The React context moved into the `globalSlot`.** Module-scope `createContext` splits in two
|
|
21
|
+
under dual ESM/CJS evaluation, exactly like every other piece of module-level state this
|
|
22
|
+
package already slots.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- **`actionRenderers` — a host surface for an action kind whose answer is a CARD, not a chip.**
|
|
27
|
+
Chips answer a question ("Approve" / "Decline"); a shared-resource card, a link out to a
|
|
28
|
+
report, an app-icon reminder are app-shaped surfaces only the host can draw. Without this seam
|
|
29
|
+
the first consumer had to keep its own message renderer beside this package's — a package plus
|
|
30
|
+
a surviving custom copy, which is the failure this package exists to prevent. The
|
|
31
|
+
forward-compatibility rule stays the PACKAGE's: an unknown kind, or a known kind at a version
|
|
32
|
+
the renderer does not list, renders nothing.
|
|
33
|
+
Types: `MessageActionRenderer`, `MessageActionRenderProps` from `@ai-matrx/messaging/react`.
|
|
34
|
+
- `src/react/host-integration.test.tsx` — four tests: the host's manager is the one messaging
|
|
35
|
+
uses (fails against 0.1.3), a provider-less app still gets one, a host renderer draws inside
|
|
36
|
+
the bubble, and an unlisted version renders nothing.
|
|
37
|
+
|
|
38
|
+
### Consumer action (C28)
|
|
39
|
+
|
|
40
|
+
- **Requires `@ai-matrx/realtime` >= 0.3.0**, picked up automatically by the `"latest"` spec.
|
|
41
|
+
- Nothing to change in existing wiring. If your app mounts its own `<RealtimeProvider>`, messaging
|
|
42
|
+
now rides it — that is the fix, and it is what you wanted.
|
|
43
|
+
- Hosts with action kinds that render a card rather than a decision: pass them as
|
|
44
|
+
`actionRenderers={[{ kind, versions, render }]}` and DELETE the host-side bubble renderer.
|
|
45
|
+
|
|
46
|
+
## 0.1.3
|
|
47
|
+
|
|
48
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
49
|
+
`git diff npm/messaging/v0.1.2..npm/messaging/v0.1.3 -- apps/shared/messaging`).
|
|
50
|
+
No source changes intended and no consumer action required.
|
|
51
|
+
|
|
3
52
|
## 0.1.2 — 2026-08-31
|
|
4
53
|
|
|
5
54
|
**Typing, presence and message broadcast work.** The root defect was in `@ai-matrx/realtime`
|
package/README.md
CHANGED
|
@@ -184,6 +184,19 @@ that fails when pressed.
|
|
|
184
184
|
Omit it and reference cards render as labeled, non-interactive cards with a title explaining why —
|
|
185
185
|
honest, never a button that does nothing.
|
|
186
186
|
|
|
187
|
+
### Mounting it in an app that already wires realtime
|
|
188
|
+
|
|
189
|
+
Nothing to do. `<MessagingProvider>` rides the host's `<RealtimeProvider>` when one is mounted
|
|
190
|
+
above it, and mounts its own only when there is none. That is not a convenience — an app has ONE
|
|
191
|
+
realtime manager on purpose (it owns the write ledger that makes an optimistic write's own echo
|
|
192
|
+
recognizable), and a second one under it makes each manager's writes look remote to the other.
|
|
193
|
+
|
|
194
|
+
```tsx
|
|
195
|
+
<RealtimeProvider client={supabase} actorId={user.id}> {/* the app's ONE mount */}
|
|
196
|
+
<MessagingProvider client={supabase} userId={user.id} organizationId={org.id}>
|
|
197
|
+
…
|
|
198
|
+
```
|
|
199
|
+
|
|
187
200
|
### Registering an actionable message kind
|
|
188
201
|
|
|
189
202
|
```tsx
|
|
@@ -209,6 +222,25 @@ honest, never a button that does nothing.
|
|
|
209
222
|
This is the seam `@ai-matrx/meet` uses: a call invitation is just a message with a structured
|
|
210
223
|
payload.
|
|
211
224
|
|
|
225
|
+
### An action kind whose answer is a card, not a chip
|
|
226
|
+
|
|
227
|
+
Chips answer a question. A shared-resource card, a link out to a report, a reminder with an app
|
|
228
|
+
icon are app-shaped surfaces only the host can draw — so the host supplies the surface and the
|
|
229
|
+
package still owns the gate:
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
232
|
+
<MessagingProvider … actionRenderers={[{
|
|
233
|
+
kind: "resource_shared",
|
|
234
|
+
versions: [1],
|
|
235
|
+
render: ({ payload, isOwn }) => <ResourceSharedCard payload={payload} isOwn={isOwn} />,
|
|
236
|
+
}]}>
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
A kind with a renderer draws it instead of chips. Everything else is unchanged, including the
|
|
240
|
+
rule that matters: an unknown kind — or a known kind at a version this build does not list —
|
|
241
|
+
renders NOTHING. Use `actions` for decisions, `actionRenderers` for surfaces; a kind may have
|
|
242
|
+
both a handler (for `summarize`, used by previews) and a renderer.
|
|
243
|
+
|
|
212
244
|
### Theming
|
|
213
245
|
|
|
214
246
|
Structural CSS ships in the package; the token **contract** is enforced; token **values** are
|
package/dist/react.cjs
CHANGED
|
@@ -1780,7 +1780,12 @@ function createMessagingRepository(options) {
|
|
|
1780
1780
|
|
|
1781
1781
|
// src/react/provider.tsx
|
|
1782
1782
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
1783
|
-
|
|
1783
|
+
function messagingContext() {
|
|
1784
|
+
return globalSlot(
|
|
1785
|
+
"react-context",
|
|
1786
|
+
() => (0, import_react.createContext)(null)
|
|
1787
|
+
);
|
|
1788
|
+
}
|
|
1784
1789
|
function defaultDiagnostics(event) {
|
|
1785
1790
|
const line = `[@ai-matrx/messaging] ${event.message}${event.remedy !== void 0 ? `
|
|
1786
1791
|
\u2192 ${event.remedy}` : ""}`;
|
|
@@ -1791,6 +1796,8 @@ function defaultDiagnostics(event) {
|
|
|
1791
1796
|
function MessagingProvider(props) {
|
|
1792
1797
|
const { client, userId, organizationId } = props;
|
|
1793
1798
|
const ready = client != null && typeof userId === "string" && userId.length > 0 && typeof organizationId === "string" && organizationId.length > 0;
|
|
1799
|
+
const hostProvidesRealtime = (0, import_react2.useIsRealtimeProviderMounted)();
|
|
1800
|
+
if (hostProvidesRealtime) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MessagingRuntime, { ...props });
|
|
1794
1801
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.RealtimeProvider, { client: ready ? client : null, actorId: userId ?? void 0, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MessagingRuntime, { ...props }) });
|
|
1795
1802
|
}
|
|
1796
1803
|
function MessagingRuntime(props) {
|
|
@@ -1859,6 +1866,14 @@ function MessagingRuntime(props) {
|
|
|
1859
1866
|
agents
|
|
1860
1867
|
});
|
|
1861
1868
|
}, [transport, agents, ready, organizationId]);
|
|
1869
|
+
const renderers = props.actionRenderers;
|
|
1870
|
+
const rendererMap = (0, import_react.useMemo)(() => {
|
|
1871
|
+
const map = /* @__PURE__ */ new Map();
|
|
1872
|
+
(renderers ?? []).forEach((renderer) => {
|
|
1873
|
+
map.set(renderer.kind, renderer);
|
|
1874
|
+
});
|
|
1875
|
+
return map;
|
|
1876
|
+
}, [renderers]);
|
|
1862
1877
|
const host = (0, import_react.useMemo)(() => {
|
|
1863
1878
|
if (engine === null) return null;
|
|
1864
1879
|
return {
|
|
@@ -1866,13 +1881,15 @@ function MessagingRuntime(props) {
|
|
|
1866
1881
|
actions: actionRegistry,
|
|
1867
1882
|
ai,
|
|
1868
1883
|
identity: engine.identity,
|
|
1869
|
-
openReference: referenceRef.current ?? null
|
|
1884
|
+
openReference: referenceRef.current ?? null,
|
|
1885
|
+
actionRenderers: rendererMap
|
|
1870
1886
|
};
|
|
1871
|
-
}, [engine, actionRegistry, ai]);
|
|
1887
|
+
}, [engine, actionRegistry, ai, rendererMap]);
|
|
1888
|
+
const MessagingContext = messagingContext();
|
|
1872
1889
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MessagingContext.Provider, { value: host, children });
|
|
1873
1890
|
}
|
|
1874
1891
|
function useMessagingHost() {
|
|
1875
|
-
return (0, import_react.useContext)(
|
|
1892
|
+
return (0, import_react.useContext)(messagingContext());
|
|
1876
1893
|
}
|
|
1877
1894
|
function useRequiredMessagingHost() {
|
|
1878
1895
|
const host = useMessagingHost();
|
|
@@ -2884,7 +2901,22 @@ function MessageBubble(props) {
|
|
|
2884
2901
|
] });
|
|
2885
2902
|
}
|
|
2886
2903
|
function MessageActionChips(props) {
|
|
2904
|
+
const host = useRequiredMessagingHost();
|
|
2887
2905
|
const action = useMessageAction(props.message);
|
|
2906
|
+
const declared = props.message.action;
|
|
2907
|
+
const renderer = declared === null ? void 0 : host.actionRenderers.get(declared.kind);
|
|
2908
|
+
if (declared !== null && renderer !== void 0) {
|
|
2909
|
+
if (!renderer.versions.includes(declared.version)) return null;
|
|
2910
|
+
const Render = renderer.render;
|
|
2911
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2912
|
+
Render,
|
|
2913
|
+
{
|
|
2914
|
+
message: props.message,
|
|
2915
|
+
payload: declared.payload,
|
|
2916
|
+
isOwn: props.message.senderId === host.identity.userId
|
|
2917
|
+
}
|
|
2918
|
+
);
|
|
2919
|
+
}
|
|
2888
2920
|
if (action.receipt !== null) {
|
|
2889
2921
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "mx-msg__receipt", children: [
|
|
2890
2922
|
action.receipt.label,
|