@ai-matrx/messaging 0.3.0 → 0.4.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 CHANGED
@@ -1,5 +1,41 @@
1
1
  # Changelog — `@ai-matrx/messaging`
2
2
 
3
+ ## 0.4.0 — 2026-09-07
4
+
5
+ **A ```matrx fence written in the PLATFORM's dialect was being deleted on render.** Found the
6
+ moment the first consumer's real messages went through this package's bubble. Third and last
7
+ release of the adoption session.
8
+
9
+ ### Fixed
10
+
11
+ - **A fence this package cannot parse is never dropped.** `splitText` advanced past an
12
+ unrecognized fence and emitted nothing for it, so a message that named a note or a task lost
13
+ that paragraph between the sender and the reader — silently, with the surrounding prose intact
14
+ so nothing looked wrong. This package understands exactly one fence dialect (its own array of
15
+ `{entityType, entityId}`); Matrx's real fences are two-key `__kind` directive shells whose
16
+ items are typed per noun and resolvable only by the app's kind registry, i.e. every real
17
+ reference on the platform hit this path.
18
+ - **The regression test that should have caught it was hollow** — it asserted
19
+ `segments.every(s => s.type === "text")` on what was, after the drop, an EMPTY array. Replaced
20
+ with one that asserts the fence survives, and one for the platform's own dialect.
21
+ - **An unreadable fence collapses to the label `Reference` in previews and notifications**, never
22
+ to nothing. Collapsing to nothing is how a reference-only message becomes an inbox row reading
23
+ "No messages yet".
24
+
25
+ ### Added
26
+
27
+ - **`renderFence`** — the host draws ```matrx fences with its own renderer, every fence, parsed
28
+ or not. A platform's fence dialect is the platform's business.
29
+ - `TextSegment` gains `{ type: "fence", body }`. With no host renderer the bubble draws an inert
30
+ card labeled "Reference" whose title says what is missing and how to wire it — drawn, never
31
+ deleted, never a code block of JSON.
32
+
33
+ ### Consumer action (C28)
34
+
35
+ - **A host on the Matrx reference-fence protocol MUST pass `renderFence`** or its references
36
+ render as the generic inert card. matrx-frontend passes `MatrxEnvelopeBlock`.
37
+ - Code that switches on `TextSegment.type` gains a third case. TypeScript will point at it.
38
+
3
39
  ## 0.3.0 — 2026-09-07
4
40
 
5
41
  **Three more seams the first adoption needed** — each one a case where the host
package/README.md CHANGED
@@ -262,6 +262,11 @@ its own registry, hands those in rather than rebuilding the thread:
262
262
  package's card — deliberately, because an app that draws references everywhere else must not
263
263
  draw them two ways.
264
264
 
265
+ **If your platform has its own ```matrx fence dialect, pass `renderFence` too.** This package
266
+ parses one shape (an array of `{entityType, entityId}`); anything else reaches your renderer
267
+ verbatim as the fence body. Without it those fences draw an honest inert "Reference" card — they
268
+ are never deleted, and never shown to a reader as JSON.
269
+
265
270
  ### Theming
266
271
 
267
272
  Structural CSS ships in the package; the token **contract** is enforced; token **values** are
package/dist/index.cjs CHANGED
@@ -319,21 +319,32 @@ function splitText(content) {
319
319
  if (start > cursor) {
320
320
  segments.push({ type: "text", value: content.slice(cursor, start) });
321
321
  }
322
- parseFenceBody(match[1] ?? "").forEach((reference) => {
323
- segments.push({ type: "reference", reference });
324
- });
322
+ const body = match[1] ?? "";
323
+ const references = parseFenceBody(body);
324
+ if (references.length === 0) {
325
+ segments.push({ type: "fence", body });
326
+ } else {
327
+ references.forEach((reference) => {
328
+ segments.push({ type: "reference", reference });
329
+ });
330
+ }
325
331
  cursor = start + match[0].length;
326
332
  }
327
333
  if (cursor < content.length) {
328
334
  segments.push({ type: "text", value: content.slice(cursor) });
329
335
  }
330
336
  return segments.filter(
331
- (segment) => segment.type === "reference" || segment.value.trim().length > 0
337
+ (segment) => segment.type !== "text" || segment.value.trim().length > 0
332
338
  );
333
339
  }
334
340
  function summarizeText(content, maxLength = 140) {
335
341
  const parts = splitText(content).map(
336
- (segment) => segment.type === "text" ? segment.value : segment.reference.label
342
+ (segment) => segment.type === "text" ? segment.value : segment.type === "reference" ? segment.reference.label : (
343
+ // A fence we cannot read still SAYS something. Collapsing it to
344
+ // nothing is how a reference-only message becomes an inbox row that
345
+ // reads "No messages yet" — a screen telling a lie.
346
+ "Reference"
347
+ )
337
348
  );
338
349
  const flattened = parts.join(" ").replace(/\s+/g, " ").trim();
339
350
  if (flattened.length <= maxLength) return flattened;