@ai-matrx/agents 0.59.260 → 0.59.262

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,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.59.262
4
+ ### no second star read after a catalog or search read (FW-014)
5
+
6
+ - `ensureLoaded()` and `searchServer()` no longer follow each read with `ues_get_bulk('agent', every id)`.
7
+ Every catalog read (`agx_get_list_full`, `agx_search`, `agent.agx_get_by_ids`) already returns
8
+ `is_favorite` computed server-side from `platform.user_entity_state` (`platform.my_favorite`), so
9
+ `toAgentSummary` now reads it. Saves one sequential request uploading every agent id (673 ids for an
10
+ ordinary account, 1,478 for admin) on every picker load and search.
11
+ - Removed: `overlayAgentFavorites` (internal) and the `favorite_read_failed` error code. The favorites
12
+ port's `getBulk` is no longer called by the catalog. Consumer action: none.
13
+
14
+ ## 0.59.261
15
+
16
+ Automatic changed-only republish (docs/metadata drift since the last published version; verified: `git diff 9a4fd3053c c006aa4394 -- apps/shared/matrx-agents/src`
17
+ has no shipped-source changes outside tests, fixtures and docs). No consumer action required.
18
+
3
19
  ## 0.59.260
4
20
  ### the pinned read asks the server for those agents only (FW-015)
5
21
 
@@ -10,6 +26,16 @@
10
26
  - The narrow read needs a client with `schema()` (supabase-js has it); without one the store falls back to the
11
27
  whole catalog and reports `pinned_read_unsupported`, as before. Consumer action: none.
12
28
 
29
+ ## 0.59.259
30
+
31
+ Automatic changed-only republish (docs/metadata drift since the last published version; verified: `git diff 48dc0c53e7 c3bd77d32a -- apps/shared/matrx-agents/src`
32
+ has no shipped-source changes outside tests, fixtures and docs). No consumer action required.
33
+
34
+ ## 0.59.258
35
+
36
+ Automatic changed-only republish (docs/metadata drift since the last published version; verified: `git diff 0a2b8c9356 48dc0c53e7 -- apps/shared/matrx-agents/src`
37
+ has no shipped-source changes outside tests, fixtures and docs). No consumer action required.
38
+
13
39
  ## 0.59.257
14
40
 
15
41
  Automatic changed-only republish (docs/metadata drift since the last published version; verified: `git diff 76b41cb76b 0a2b8c9356 -- apps/shared/matrx-agents/src`
@@ -239,9 +239,11 @@ function toAgentSummary(raw) {
239
239
  modelId: str(raw["model_id"]),
240
240
  isActive: bool(raw["is_active"]),
241
241
  isArchived: bool(raw["is_archived"]),
242
- // Stars are per-person (platform.user_entity_state) and are overlaid by
243
- // `overlayAgentFavorites` — a list row never carries the caller's star.
244
- isFavorite: false,
242
+ // The caller's star. Every read this module maps (agx_get_list_full,
243
+ // agx_search, agent.agx_get_by_ids) computes it server-side with
244
+ // platform.my_favorite — platform.user_entity_state for auth.uid(), the
245
+ // same table the favorites port reads — so no second read is needed (FW-014).
246
+ isFavorite: bool(raw["is_favorite"]),
245
247
  createdBy: str(raw["created_by"]),
246
248
  organizationId: str(raw["organization_id"]),
247
249
  taskId: str(raw["task_id"]),
@@ -354,29 +356,6 @@ async function readSingleAgentRow(client, agentId) {
354
356
  return null;
355
357
  }
356
358
  var AGENT_FAVORITE_ENTITY_TYPE = "agent";
357
- async function overlayAgentFavorites(favorites, rows, onError) {
358
- if (rows.length === 0) return rows;
359
- let starred = null;
360
- try {
361
- const result = await favorites.getBulk(
362
- AGENT_FAVORITE_ENTITY_TYPE,
363
- rows.map((r) => r.id)
364
- );
365
- if (result.ok) {
366
- starred = new Set(
367
- result.data.items.filter((s) => s.isFavorite).map((s) => s.entityId)
368
- );
369
- } else {
370
- onError(result.error.message);
371
- }
372
- } catch (e) {
373
- onError(e instanceof Error ? e.message : String(e));
374
- }
375
- return rows.map((r) => {
376
- const isFavorite2 = starred?.has(r.id) ?? false;
377
- return r.isFavorite === isFavorite2 ? r : { ...r, isFavorite: isFavorite2 };
378
- });
379
- }
380
359
  async function writeAgentFavorite(favorites, agentId, isFavorite2) {
381
360
  const result = await favorites.setFavorite(
382
361
  AGENT_FAVORITE_ENTITY_TYPE,
@@ -832,13 +811,6 @@ function createAgentCatalog(config) {
832
811
  identity,
833
812
  errorSink
834
813
  }).favorites;
835
- function reportFavoriteReadFailure(message) {
836
- errorSink({
837
- code: "favorite_read_failed",
838
- message: `your starred agents could not be read (${message}); every star is shown unset until the next catalog load`,
839
- context: { catalogId }
840
- });
841
- }
842
814
  const labels = {
843
815
  ...DEFAULT_AGENT_CATALOG_LABELS,
844
816
  ...config.labels ?? {}
@@ -910,11 +882,7 @@ function createAgentCatalog(config) {
910
882
  }
911
883
  publish({ ...state, status: "loading", error: null });
912
884
  try {
913
- const rows = await overlayAgentFavorites(
914
- favorites,
915
- await readAgentCatalogRows(client),
916
- reportFavoriteReadFailure
917
- );
885
+ const rows = await readAgentCatalogRows(client);
918
886
  replaceRows(rows);
919
887
  publish({ ...state, status: "succeeded", error: null, favoriteRowsStatus: "succeeded" });
920
888
  globalState.listFetchedAt.set(catalogId, Date.now());
@@ -1064,13 +1032,7 @@ function createAgentCatalog(config) {
1064
1032
  }
1065
1033
  async function searchServer(query, deep = false) {
1066
1034
  const result = await searchAgentsOnServer(client, { query, deep });
1067
- mergeRows(
1068
- await overlayAgentFavorites(
1069
- favorites,
1070
- result.rows,
1071
- reportFavoriteReadFailure
1072
- )
1073
- );
1035
+ mergeRows(result.rows);
1074
1036
  return result.ids;
1075
1037
  }
1076
1038
  async function setFavorite(agentId, isFavorite2) {
@@ -809,9 +809,10 @@ declare function getRegisteredAgentCatalog(catalogId?: string): AgentCatalog | u
809
809
  * 2. `agx_search(p_query, p_deep, p_limit, p_offset)` — tier-2 server search.
810
810
  * 3. `agx_resolve_agent_address(p_ids)` — the single-agent name read, used
811
811
  * when a mandate's Holder is not in the caller's own catalog.
812
- * 4. The caller's stars: `ues_get_bulk('agent', ids)` through the
813
- * favorites port (`platform.user_entity_state`), overlaid on every row
814
- * set this module returns to the store.
812
+ * 4. The caller's stars arrive ON the rows: every read above computes
813
+ * `is_favorite` server-side from `platform.user_entity_state`
814
+ * (`platform.my_favorite`). There is no second star read (FW-014; the
815
+ * `ues_get_bulk` overlay over every id was retired 2026-10-23).
815
816
  * 5. ONE write: the caller's star, `ues_set` through the same port. The
816
817
  * retired `agent.definition.is_favorite` column is never read or written.
817
818
  * 6. THE PINNED READ (2026-10-09; by-ids 2026-10-23): the caller's starred
@@ -809,9 +809,10 @@ declare function getRegisteredAgentCatalog(catalogId?: string): AgentCatalog | u
809
809
  * 2. `agx_search(p_query, p_deep, p_limit, p_offset)` — tier-2 server search.
810
810
  * 3. `agx_resolve_agent_address(p_ids)` — the single-agent name read, used
811
811
  * when a mandate's Holder is not in the caller's own catalog.
812
- * 4. The caller's stars: `ues_get_bulk('agent', ids)` through the
813
- * favorites port (`platform.user_entity_state`), overlaid on every row
814
- * set this module returns to the store.
812
+ * 4. The caller's stars arrive ON the rows: every read above computes
813
+ * `is_favorite` server-side from `platform.user_entity_state`
814
+ * (`platform.my_favorite`). There is no second star read (FW-014; the
815
+ * `ues_get_bulk` overlay over every id was retired 2026-10-23).
815
816
  * 5. ONE write: the caller's star, `ues_set` through the same port. The
816
817
  * retired `agent.definition.is_favorite` column is never read or written.
817
818
  * 6. THE PINNED READ (2026-10-09; by-ids 2026-10-23): the caller's starred
@@ -133,9 +133,11 @@ function toAgentSummary(raw) {
133
133
  modelId: str(raw["model_id"]),
134
134
  isActive: bool(raw["is_active"]),
135
135
  isArchived: bool(raw["is_archived"]),
136
- // Stars are per-person (platform.user_entity_state) and are overlaid by
137
- // `overlayAgentFavorites` — a list row never carries the caller's star.
138
- isFavorite: false,
136
+ // The caller's star. Every read this module maps (agx_get_list_full,
137
+ // agx_search, agent.agx_get_by_ids) computes it server-side with
138
+ // platform.my_favorite — platform.user_entity_state for auth.uid(), the
139
+ // same table the favorites port reads — so no second read is needed (FW-014).
140
+ isFavorite: bool(raw["is_favorite"]),
139
141
  createdBy: str(raw["created_by"]),
140
142
  organizationId: str(raw["organization_id"]),
141
143
  taskId: str(raw["task_id"]),
@@ -248,29 +250,6 @@ async function readSingleAgentRow(client, agentId) {
248
250
  return null;
249
251
  }
250
252
  var AGENT_FAVORITE_ENTITY_TYPE = "agent";
251
- async function overlayAgentFavorites(favorites, rows, onError) {
252
- if (rows.length === 0) return rows;
253
- let starred = null;
254
- try {
255
- const result = await favorites.getBulk(
256
- AGENT_FAVORITE_ENTITY_TYPE,
257
- rows.map((r) => r.id)
258
- );
259
- if (result.ok) {
260
- starred = new Set(
261
- result.data.items.filter((s) => s.isFavorite).map((s) => s.entityId)
262
- );
263
- } else {
264
- onError(result.error.message);
265
- }
266
- } catch (e) {
267
- onError(e instanceof Error ? e.message : String(e));
268
- }
269
- return rows.map((r) => {
270
- const isFavorite2 = starred?.has(r.id) ?? false;
271
- return r.isFavorite === isFavorite2 ? r : { ...r, isFavorite: isFavorite2 };
272
- });
273
- }
274
253
  async function writeAgentFavorite(favorites, agentId, isFavorite2) {
275
254
  const result = await favorites.setFavorite(
276
255
  AGENT_FAVORITE_ENTITY_TYPE,
@@ -726,13 +705,6 @@ function createAgentCatalog(config) {
726
705
  identity,
727
706
  errorSink
728
707
  }).favorites;
729
- function reportFavoriteReadFailure(message) {
730
- errorSink({
731
- code: "favorite_read_failed",
732
- message: `your starred agents could not be read (${message}); every star is shown unset until the next catalog load`,
733
- context: { catalogId }
734
- });
735
- }
736
708
  const labels = {
737
709
  ...DEFAULT_AGENT_CATALOG_LABELS,
738
710
  ...config.labels ?? {}
@@ -804,11 +776,7 @@ function createAgentCatalog(config) {
804
776
  }
805
777
  publish({ ...state, status: "loading", error: null });
806
778
  try {
807
- const rows = await overlayAgentFavorites(
808
- favorites,
809
- await readAgentCatalogRows(client),
810
- reportFavoriteReadFailure
811
- );
779
+ const rows = await readAgentCatalogRows(client);
812
780
  replaceRows(rows);
813
781
  publish({ ...state, status: "succeeded", error: null, favoriteRowsStatus: "succeeded" });
814
782
  globalState.listFetchedAt.set(catalogId, Date.now());
@@ -958,13 +926,7 @@ function createAgentCatalog(config) {
958
926
  }
959
927
  async function searchServer(query, deep = false) {
960
928
  const result = await searchAgentsOnServer(client, { query, deep });
961
- mergeRows(
962
- await overlayAgentFavorites(
963
- favorites,
964
- result.rows,
965
- reportFavoriteReadFailure
966
- )
967
- );
929
+ mergeRows(result.rows);
968
930
  return result.ids;
969
931
  }
970
932
  async function setFavorite(agentId, isFavorite2) {
@@ -43,11 +43,13 @@ __export(react_exports, {
43
43
  InlineMaxInput: () => InlineMaxInput,
44
44
  PAGE_LEVEL: () => PAGE_LEVEL,
45
45
  buildContextLevels: () => buildContextLevels,
46
+ buildContextTree: () => buildContextTree,
46
47
  includeChanges: () => includeChanges,
47
48
  inheritedInclude: () => inheritedInclude,
48
49
  levelRows: () => levelRows,
49
50
  placeByOrigin: () => placeByOrigin,
50
- summarizeContextRows: () => summarizeContextRows
51
+ summarizeContextRows: () => summarizeContextRows,
52
+ treeLevels: () => treeLevels
51
53
  });
52
54
  module.exports = __toCommonJS(react_exports);
53
55
 
@@ -203,6 +205,41 @@ function buildContextLevels(rows, hierarchy) {
203
205
  for (const level of out) level.groups = stableSort(level.groups, (g) => g.ref);
204
206
  return stableSort(out, (level) => level.ref);
205
207
  }
208
+ function buildContextTree(levels) {
209
+ const roots = [];
210
+ const byId = /* @__PURE__ */ new Map();
211
+ const rank = /* @__PURE__ */ new Map();
212
+ for (const level of levels) {
213
+ const labels = level.ref.path.length > 0 ? level.ref.path : [""];
214
+ let siblings = roots;
215
+ let last = null;
216
+ const ids = [];
217
+ for (let i = 0; i < labels.length; i++) {
218
+ ids.push(labels[i].trim().toLowerCase());
219
+ const id = ids.join("");
220
+ let hit = byId.get(id);
221
+ if (!hit) {
222
+ hit = { id, label: labels[i], path: labels.slice(0, i + 1), level: null, children: [] };
223
+ byId.set(id, hit);
224
+ siblings.push(hit);
225
+ }
226
+ const order = level.ref.order ?? 0;
227
+ rank.set(hit, Math.min(rank.get(hit) ?? order, order));
228
+ last = hit;
229
+ siblings = hit.children;
230
+ }
231
+ if (last) last.level = level;
232
+ }
233
+ const sort = (list) => {
234
+ const sorted = stableSort(list, (n) => ({ order: rank.get(n) ?? 0 }));
235
+ for (const n of sorted) n.children = sort(n.children);
236
+ return sorted;
237
+ };
238
+ return sort(roots);
239
+ }
240
+ function treeLevels(node) {
241
+ return [...node.level ? [node.level] : [], ...node.children.flatMap(treeLevels)];
242
+ }
206
243
  function levelRows(level) {
207
244
  return [...level.rows, ...level.groups.flatMap((g) => g.rows)];
208
245
  }
@@ -463,16 +500,6 @@ function useNarrow(ref) {
463
500
  }
464
501
  var INDENT = ["", "pl-2", "pl-4", "pl-6"];
465
502
  var indent = (depth) => INDENT[Math.min(depth, INDENT.length - 1)];
466
- function sectionsOf(levels) {
467
- const out = [];
468
- for (const level of levels) {
469
- const label = level.ref.path.length > 1 ? level.ref.path[0] : null;
470
- const last = out[out.length - 1];
471
- if (label !== null && last && last.label === label) last.levels.push(level);
472
- else out.push({ id: label === null ? `level:${level.ref.id}` : `section:${label}`, label, levels: [level] });
473
- }
474
- return out;
475
- }
476
503
  function ContextRulesTable({
477
504
  rows,
478
505
  cap,
@@ -517,14 +544,13 @@ function ContextRulesTable({
517
544
  rowId(row)
518
545
  );
519
546
  const heading = (props) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Heading, { ...props, grid, narrow, comfortable, readOnly }, props.id);
520
- const renderLevel = (level, depth) => {
547
+ const renderLevelBody = (level, depth, label) => {
521
548
  const all = levelRows(level);
522
- const name = level.ref.path[level.ref.path.length - 1] ?? "";
523
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "rowgroup", "aria-label": headed ? level.ref.path.join(" \u203A ") : void 0, "data-level": level.ref.id, children: [
549
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
524
550
  headed ? heading({
525
551
  id: `h:${level.ref.id}`,
526
552
  kind: "level",
527
- label: name,
553
+ label,
528
554
  switchName: level.ref.path.join(" \u203A "),
529
555
  depth,
530
556
  rows: all,
@@ -544,7 +570,51 @@ function ContextRulesTable({
544
570
  }),
545
571
  group.rows.map((row) => renderRow(row, depth + 2))
546
572
  ] }, group.ref.id))
547
- ] }, level.ref.id);
573
+ ] });
574
+ };
575
+ const renderNode = (node, depth) => {
576
+ const { level } = node;
577
+ const below = node.children.map((child) => renderNode(child, depth + 1));
578
+ if (level) {
579
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
580
+ "div",
581
+ {
582
+ role: "rowgroup",
583
+ "aria-label": headed ? level.ref.path.join(" \u203A ") : void 0,
584
+ "data-level": level.ref.id,
585
+ children: [
586
+ renderLevelBody(level, depth, node.label),
587
+ below
588
+ ]
589
+ },
590
+ level.ref.id
591
+ );
592
+ }
593
+ const covered = treeLevels(node);
594
+ const all = covered.flatMap(levelRows);
595
+ const several = covered.length > 1;
596
+ const toggleSection = several && !readOnly ? (next) => {
597
+ const changes = [];
598
+ for (const lv of covered) {
599
+ if (lv.master) lv.master.onToggle?.(next);
600
+ else changes.push(...bulkChanges(levelRows(lv), next));
601
+ }
602
+ if (changes.length > 0) onSetInclude?.(changes);
603
+ } : void 0;
604
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "rowgroup", "aria-label": node.label, "data-section": node.label, children: [
605
+ heading({
606
+ id: `h:section:${node.id}`,
607
+ kind: "section",
608
+ label: node.label,
609
+ switchName: node.path.join(" \u203A "),
610
+ depth,
611
+ rows: all,
612
+ checked: covered.some((l) => l.master ? l.master.on : levelRows(l).some((r) => r.include)),
613
+ onToggle: toggleSection,
614
+ noSwitch: !several
615
+ }),
616
+ below
617
+ ] }, `section:${node.id}`);
548
618
  };
549
619
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_design_system3.TooltipProvider, { delayDuration: 300, titleTooltips: false, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
550
620
  "div",
@@ -569,33 +639,7 @@ function ContextRulesTable({
569
639
  ]
570
640
  }
571
641
  ),
572
- levels.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "px-2 py-3 text-center text-muted-foreground", children: "None" }) : sectionsOf(levels).map((section) => {
573
- if (section.label === null || !headed) return renderLevel(section.levels[0], 0);
574
- const all = section.levels.flatMap(levelRows);
575
- const several = section.levels.length > 1;
576
- const toggleSection = several && !readOnly ? (next) => {
577
- const changes = [];
578
- for (const level of section.levels) {
579
- if (level.master) level.master.onToggle?.(next);
580
- else changes.push(...bulkChanges(levelRows(level), next));
581
- }
582
- if (changes.length > 0) onSetInclude?.(changes);
583
- } : void 0;
584
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "rowgroup", "aria-label": section.label, "data-section": section.label, children: [
585
- heading({
586
- id: `h:${section.id}`,
587
- kind: "section",
588
- label: section.label,
589
- switchName: section.label,
590
- depth: 0,
591
- rows: all,
592
- checked: section.levels.some((l) => l.master ? l.master.on : levelRows(l).some((r) => r.include)),
593
- onToggle: toggleSection,
594
- noSwitch: !several
595
- }),
596
- section.levels.map((level) => renderLevel(level, 1))
597
- ] }, section.id);
598
- })
642
+ levels.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "px-2 py-3 text-center text-muted-foreground", children: "None" }) : headed ? buildContextTree(levels).map((node) => renderNode(node, 0)) : levels.map((level) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { role: "rowgroup", "data-level": level.ref.id, children: renderLevelBody(level, 0, level.ref.path[level.ref.path.length - 1] ?? "") }, level.ref.id))
599
643
  ]
600
644
  }
601
645
  ) });
@@ -238,6 +238,31 @@ declare const PAGE_LEVEL: ContextLevelRef;
238
238
  declare function placeByOrigin(row: ResolvedContextRow): ContextRowPlace;
239
239
  /** Rows → levels → groups, in the host's order (stable within equal orders). */
240
240
  declare function buildContextLevels(rows: readonly ResolvedContextRow[], hierarchy?: ContextHierarchy | null): ContextLevelNode[];
241
+ /**
242
+ * A heading of the table: one label of a level's path. A node with a `level`
243
+ * IS that level (its rows, groups, switch); a node without one is a section
244
+ * that only names the labels above deeper levels.
245
+ */
246
+ interface ContextTreeNode {
247
+ /** The path up to and including this label, lower-cased: one node per distinct path. */
248
+ id: string;
249
+ /** This node's own label (the last one of its path). */
250
+ label: string;
251
+ /** The full heading path to this node, root first. */
252
+ path: readonly string[];
253
+ level: ContextLevelNode | null;
254
+ children: ContextTreeNode[];
255
+ }
256
+ /**
257
+ * Levels → ONE tree by path (Arman, 2026-10-21: "UNIFY and eliminate" — one
258
+ * AI Matrx root, whatever the levels' paths): `["AI Matrx"]`,
259
+ * `["AI Matrx", "Marketing"]` and `["AI Matrx", "Marketing", "Brands"]` nest as
260
+ * AI Matrx → Marketing → Brands; a root named by two levels is still one root.
261
+ * Order: a node sorts by the lowest `order` found in it or below it.
262
+ */
263
+ declare function buildContextTree(levels: readonly ContextLevelNode[]): ContextTreeNode[];
264
+ /** Every level at or below a node. */
265
+ declare function treeLevels(node: ContextTreeNode): ContextLevelNode[];
241
266
  /** Every row of a level, groups included. */
242
267
  declare function levelRows(level: ContextLevelNode): ResolvedContextRow[];
243
268
  /** What the row's include would be with no rule of the person's. */
@@ -455,4 +480,4 @@ declare function ContextReceiptBlockDetail({ block, load, className, }: {
455
480
  className?: string;
456
481
  }): React.JSX.Element;
457
482
 
458
- export { AI_MATRX_LEVEL, ATTACHED_LEVEL, ContextDeliveredValue, type ContextGroupNode, type ContextGroupRef, type ContextHierarchy, type ContextLevelMaster, type ContextLevelNode, type ContextLevelRef, ContextReceiptBlockDetail, ContextReceiptBlocks, type ContextReceiptBlocksProps, type ContextRowPlace, type ContextRuleChange, ContextRulesChip, type ContextRulesChipProps, type ContextRulesDensity, ContextRulesPanelBody, type ContextRulesPanelBodyProps, ContextRulesTable, type ContextRulesTableProps, DeliveredSection, InlineMaxInput, type InlineMaxInputProps, PAGE_LEVEL, buildContextLevels, includeChanges, inheritedInclude, levelRows, placeByOrigin, summarizeContextRows };
483
+ export { AI_MATRX_LEVEL, ATTACHED_LEVEL, ContextDeliveredValue, type ContextGroupNode, type ContextGroupRef, type ContextHierarchy, type ContextLevelMaster, type ContextLevelNode, type ContextLevelRef, ContextReceiptBlockDetail, ContextReceiptBlocks, type ContextReceiptBlocksProps, type ContextRowPlace, type ContextRuleChange, ContextRulesChip, type ContextRulesChipProps, type ContextRulesDensity, ContextRulesPanelBody, type ContextRulesPanelBodyProps, ContextRulesTable, type ContextRulesTableProps, type ContextTreeNode, DeliveredSection, InlineMaxInput, type InlineMaxInputProps, PAGE_LEVEL, buildContextLevels, buildContextTree, includeChanges, inheritedInclude, levelRows, placeByOrigin, summarizeContextRows, treeLevels };
@@ -238,6 +238,31 @@ declare const PAGE_LEVEL: ContextLevelRef;
238
238
  declare function placeByOrigin(row: ResolvedContextRow): ContextRowPlace;
239
239
  /** Rows → levels → groups, in the host's order (stable within equal orders). */
240
240
  declare function buildContextLevels(rows: readonly ResolvedContextRow[], hierarchy?: ContextHierarchy | null): ContextLevelNode[];
241
+ /**
242
+ * A heading of the table: one label of a level's path. A node with a `level`
243
+ * IS that level (its rows, groups, switch); a node without one is a section
244
+ * that only names the labels above deeper levels.
245
+ */
246
+ interface ContextTreeNode {
247
+ /** The path up to and including this label, lower-cased: one node per distinct path. */
248
+ id: string;
249
+ /** This node's own label (the last one of its path). */
250
+ label: string;
251
+ /** The full heading path to this node, root first. */
252
+ path: readonly string[];
253
+ level: ContextLevelNode | null;
254
+ children: ContextTreeNode[];
255
+ }
256
+ /**
257
+ * Levels → ONE tree by path (Arman, 2026-10-21: "UNIFY and eliminate" — one
258
+ * AI Matrx root, whatever the levels' paths): `["AI Matrx"]`,
259
+ * `["AI Matrx", "Marketing"]` and `["AI Matrx", "Marketing", "Brands"]` nest as
260
+ * AI Matrx → Marketing → Brands; a root named by two levels is still one root.
261
+ * Order: a node sorts by the lowest `order` found in it or below it.
262
+ */
263
+ declare function buildContextTree(levels: readonly ContextLevelNode[]): ContextTreeNode[];
264
+ /** Every level at or below a node. */
265
+ declare function treeLevels(node: ContextTreeNode): ContextLevelNode[];
241
266
  /** Every row of a level, groups included. */
242
267
  declare function levelRows(level: ContextLevelNode): ResolvedContextRow[];
243
268
  /** What the row's include would be with no rule of the person's. */
@@ -455,4 +480,4 @@ declare function ContextReceiptBlockDetail({ block, load, className, }: {
455
480
  className?: string;
456
481
  }): React.JSX.Element;
457
482
 
458
- export { AI_MATRX_LEVEL, ATTACHED_LEVEL, ContextDeliveredValue, type ContextGroupNode, type ContextGroupRef, type ContextHierarchy, type ContextLevelMaster, type ContextLevelNode, type ContextLevelRef, ContextReceiptBlockDetail, ContextReceiptBlocks, type ContextReceiptBlocksProps, type ContextRowPlace, type ContextRuleChange, ContextRulesChip, type ContextRulesChipProps, type ContextRulesDensity, ContextRulesPanelBody, type ContextRulesPanelBodyProps, ContextRulesTable, type ContextRulesTableProps, DeliveredSection, InlineMaxInput, type InlineMaxInputProps, PAGE_LEVEL, buildContextLevels, includeChanges, inheritedInclude, levelRows, placeByOrigin, summarizeContextRows };
483
+ export { AI_MATRX_LEVEL, ATTACHED_LEVEL, ContextDeliveredValue, type ContextGroupNode, type ContextGroupRef, type ContextHierarchy, type ContextLevelMaster, type ContextLevelNode, type ContextLevelRef, ContextReceiptBlockDetail, ContextReceiptBlocks, type ContextReceiptBlocksProps, type ContextRowPlace, type ContextRuleChange, ContextRulesChip, type ContextRulesChipProps, type ContextRulesDensity, ContextRulesPanelBody, type ContextRulesPanelBodyProps, ContextRulesTable, type ContextRulesTableProps, type ContextTreeNode, DeliveredSection, InlineMaxInput, type InlineMaxInputProps, PAGE_LEVEL, buildContextLevels, buildContextTree, includeChanges, inheritedInclude, levelRows, placeByOrigin, summarizeContextRows, treeLevels };
@@ -167,6 +167,41 @@ function buildContextLevels(rows, hierarchy) {
167
167
  for (const level of out) level.groups = stableSort(level.groups, (g) => g.ref);
168
168
  return stableSort(out, (level) => level.ref);
169
169
  }
170
+ function buildContextTree(levels) {
171
+ const roots = [];
172
+ const byId = /* @__PURE__ */ new Map();
173
+ const rank = /* @__PURE__ */ new Map();
174
+ for (const level of levels) {
175
+ const labels = level.ref.path.length > 0 ? level.ref.path : [""];
176
+ let siblings = roots;
177
+ let last = null;
178
+ const ids = [];
179
+ for (let i = 0; i < labels.length; i++) {
180
+ ids.push(labels[i].trim().toLowerCase());
181
+ const id = ids.join("");
182
+ let hit = byId.get(id);
183
+ if (!hit) {
184
+ hit = { id, label: labels[i], path: labels.slice(0, i + 1), level: null, children: [] };
185
+ byId.set(id, hit);
186
+ siblings.push(hit);
187
+ }
188
+ const order = level.ref.order ?? 0;
189
+ rank.set(hit, Math.min(rank.get(hit) ?? order, order));
190
+ last = hit;
191
+ siblings = hit.children;
192
+ }
193
+ if (last) last.level = level;
194
+ }
195
+ const sort = (list) => {
196
+ const sorted = stableSort(list, (n) => ({ order: rank.get(n) ?? 0 }));
197
+ for (const n of sorted) n.children = sort(n.children);
198
+ return sorted;
199
+ };
200
+ return sort(roots);
201
+ }
202
+ function treeLevels(node) {
203
+ return [...node.level ? [node.level] : [], ...node.children.flatMap(treeLevels)];
204
+ }
170
205
  function levelRows(level) {
171
206
  return [...level.rows, ...level.groups.flatMap((g) => g.rows)];
172
207
  }
@@ -403,7 +438,7 @@ function summarizeContextRows(rows) {
403
438
  }
404
439
 
405
440
  // context/react/ContextRulesTable.tsx
406
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
441
+ import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
407
442
  var CONTEXT_RULES_GRID = {
408
443
  compact: "grid grid-cols-[minmax(0,1fr)_2.75rem_4rem_6.5rem] items-center gap-x-2 px-2",
409
444
  comfortable: "grid grid-cols-[minmax(0,1fr)_2.75rem_3.5rem_6.75rem] items-center gap-x-1.5 px-3"
@@ -433,16 +468,6 @@ function useNarrow(ref) {
433
468
  }
434
469
  var INDENT = ["", "pl-2", "pl-4", "pl-6"];
435
470
  var indent = (depth) => INDENT[Math.min(depth, INDENT.length - 1)];
436
- function sectionsOf(levels) {
437
- const out = [];
438
- for (const level of levels) {
439
- const label = level.ref.path.length > 1 ? level.ref.path[0] : null;
440
- const last = out[out.length - 1];
441
- if (label !== null && last && last.label === label) last.levels.push(level);
442
- else out.push({ id: label === null ? `level:${level.ref.id}` : `section:${label}`, label, levels: [level] });
443
- }
444
- return out;
445
- }
446
471
  function ContextRulesTable({
447
472
  rows,
448
473
  cap,
@@ -487,14 +512,13 @@ function ContextRulesTable({
487
512
  rowId(row)
488
513
  );
489
514
  const heading = (props) => /* @__PURE__ */ jsx3(Heading, { ...props, grid, narrow, comfortable, readOnly }, props.id);
490
- const renderLevel = (level, depth) => {
515
+ const renderLevelBody = (level, depth, label) => {
491
516
  const all = levelRows(level);
492
- const name = level.ref.path[level.ref.path.length - 1] ?? "";
493
- return /* @__PURE__ */ jsxs3("div", { role: "rowgroup", "aria-label": headed ? level.ref.path.join(" \u203A ") : void 0, "data-level": level.ref.id, children: [
517
+ return /* @__PURE__ */ jsxs3(Fragment, { children: [
494
518
  headed ? heading({
495
519
  id: `h:${level.ref.id}`,
496
520
  kind: "level",
497
- label: name,
521
+ label,
498
522
  switchName: level.ref.path.join(" \u203A "),
499
523
  depth,
500
524
  rows: all,
@@ -514,7 +538,51 @@ function ContextRulesTable({
514
538
  }),
515
539
  group.rows.map((row) => renderRow(row, depth + 2))
516
540
  ] }, group.ref.id))
517
- ] }, level.ref.id);
541
+ ] });
542
+ };
543
+ const renderNode = (node, depth) => {
544
+ const { level } = node;
545
+ const below = node.children.map((child) => renderNode(child, depth + 1));
546
+ if (level) {
547
+ return /* @__PURE__ */ jsxs3(
548
+ "div",
549
+ {
550
+ role: "rowgroup",
551
+ "aria-label": headed ? level.ref.path.join(" \u203A ") : void 0,
552
+ "data-level": level.ref.id,
553
+ children: [
554
+ renderLevelBody(level, depth, node.label),
555
+ below
556
+ ]
557
+ },
558
+ level.ref.id
559
+ );
560
+ }
561
+ const covered = treeLevels(node);
562
+ const all = covered.flatMap(levelRows);
563
+ const several = covered.length > 1;
564
+ const toggleSection = several && !readOnly ? (next) => {
565
+ const changes = [];
566
+ for (const lv of covered) {
567
+ if (lv.master) lv.master.onToggle?.(next);
568
+ else changes.push(...bulkChanges(levelRows(lv), next));
569
+ }
570
+ if (changes.length > 0) onSetInclude?.(changes);
571
+ } : void 0;
572
+ return /* @__PURE__ */ jsxs3("div", { role: "rowgroup", "aria-label": node.label, "data-section": node.label, children: [
573
+ heading({
574
+ id: `h:section:${node.id}`,
575
+ kind: "section",
576
+ label: node.label,
577
+ switchName: node.path.join(" \u203A "),
578
+ depth,
579
+ rows: all,
580
+ checked: covered.some((l) => l.master ? l.master.on : levelRows(l).some((r) => r.include)),
581
+ onToggle: toggleSection,
582
+ noSwitch: !several
583
+ }),
584
+ below
585
+ ] }, `section:${node.id}`);
518
586
  };
519
587
  return /* @__PURE__ */ jsx3(TooltipProvider2, { delayDuration: 300, titleTooltips: false, children: /* @__PURE__ */ jsxs3(
520
588
  "div",
@@ -539,33 +607,7 @@ function ContextRulesTable({
539
607
  ]
540
608
  }
541
609
  ),
542
- levels.length === 0 ? /* @__PURE__ */ jsx3("div", { className: "px-2 py-3 text-center text-muted-foreground", children: "None" }) : sectionsOf(levels).map((section) => {
543
- if (section.label === null || !headed) return renderLevel(section.levels[0], 0);
544
- const all = section.levels.flatMap(levelRows);
545
- const several = section.levels.length > 1;
546
- const toggleSection = several && !readOnly ? (next) => {
547
- const changes = [];
548
- for (const level of section.levels) {
549
- if (level.master) level.master.onToggle?.(next);
550
- else changes.push(...bulkChanges(levelRows(level), next));
551
- }
552
- if (changes.length > 0) onSetInclude?.(changes);
553
- } : void 0;
554
- return /* @__PURE__ */ jsxs3("div", { role: "rowgroup", "aria-label": section.label, "data-section": section.label, children: [
555
- heading({
556
- id: `h:${section.id}`,
557
- kind: "section",
558
- label: section.label,
559
- switchName: section.label,
560
- depth: 0,
561
- rows: all,
562
- checked: section.levels.some((l) => l.master ? l.master.on : levelRows(l).some((r) => r.include)),
563
- onToggle: toggleSection,
564
- noSwitch: !several
565
- }),
566
- section.levels.map((level) => renderLevel(level, 1))
567
- ] }, section.id);
568
- })
610
+ levels.length === 0 ? /* @__PURE__ */ jsx3("div", { className: "px-2 py-3 text-center text-muted-foreground", children: "None" }) : headed ? buildContextTree(levels).map((node) => renderNode(node, 0)) : levels.map((level) => /* @__PURE__ */ jsx3("div", { role: "rowgroup", "data-level": level.ref.id, children: renderLevelBody(level, 0, level.ref.path[level.ref.path.length - 1] ?? "") }, level.ref.id))
569
611
  ]
570
612
  }
571
613
  ) });
@@ -1438,9 +1480,11 @@ export {
1438
1480
  InlineMaxInput,
1439
1481
  PAGE_LEVEL,
1440
1482
  buildContextLevels,
1483
+ buildContextTree,
1441
1484
  includeChanges,
1442
1485
  inheritedInclude,
1443
1486
  levelRows,
1444
1487
  placeByOrigin,
1445
- summarizeContextRows
1488
+ summarizeContextRows,
1489
+ treeLevels
1446
1490
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-matrx/agents",
3
- "version": "0.59.260",
3
+ "version": "0.59.262",
4
4
  "description": "Portable AI Matrx agent stream protocol, event projection, and safe result-presentation primitives.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -562,5 +562,5 @@
562
562
  "verify:tarball": "node ./scripts/verify-tarball.mjs",
563
563
  "typegen:self-test": "node scripts/typegen/drop-guard.self-test.mjs && node scripts/typegen/check-api-types-fresh.mjs --self-test"
564
564
  },
565
- "gitHead": "9a4fd3053c05194032de663989ec2f851ab0db6c"
565
+ "gitHead": "de2b95873473d1fd1878b0e3b06ce75570190a1b"
566
566
  }