@ai-matrx/agents 0.59.259 → 0.59.261

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,9 +1,14 @@
1
1
  # Changelog
2
2
 
3
- ## 0.59.259
4
-
5
- UNVERIFIED: this release has no Unreleased entry and the source comparison could not be made (the caller supplied no source comparison). Whether source changed is unknown; consumer action is unknown.
6
- Check the package's gitHead on npm against this version's gitHead: `git diff <previous gitHead> <this gitHead> -- apps/shared/matrx-agents/src`.
3
+ ## 0.59.261
4
+ ### the pinned read asks the server for those agents only (FW-015)
5
+
6
+ - `ensureFavoriteRows()` / `readFavoriteAgentRows` now read the starred agents through
7
+ `agent.agx_get_by_ids(p_ids)` (same columns and access words as `agx_get_list_full`) instead of
8
+ `agx_get_list_full` filtered by id, which made the server build the caller's whole catalog first.
9
+ Measured on live: ordinary account 161 → 41 ms, admin 390 → 53 ms, identical rows.
10
+ - The narrow read needs a client with `schema()` (supabase-js has it); without one the store falls back to the
11
+ whole catalog and reports `pinned_read_unsupported`, as before. Consumer action: none.
7
12
 
8
13
  ## 0.59.257
9
14
 
@@ -398,6 +398,9 @@ async function readFavoriteAgentRows(client, favorites) {
398
398
  if (typeof favorites.list !== "function") {
399
399
  throw new AgentPinnedReadUnsupported("the favorites port has no list()");
400
400
  }
401
+ if (typeof client.schema !== "function") {
402
+ throw new AgentPinnedReadUnsupported("the client has no schema()");
403
+ }
401
404
  const listed = await favorites.list("favorite");
402
405
  if (!listed.ok) {
403
406
  throw new AgentCatalogReadError(
@@ -416,13 +419,9 @@ async function readFavoriteAgentRows(client, favorites) {
416
419
  const rows = [];
417
420
  for (let at = 0; at < ids.length; at += PINNED_ID_CHUNK) {
418
421
  const chunk = ids.slice(at, at + PINNED_ID_CHUNK);
419
- const call = client.rpc("agx_get_list_full", {});
420
- if (typeof call.in !== "function") {
421
- throw new AgentPinnedReadUnsupported("the client's rpc builder has no in()");
422
- }
423
- const { data, error } = await call.in("id", chunk);
422
+ const { data, error } = await client.schema("agent").rpc("agx_get_by_ids", { p_ids: chunk });
424
423
  if (error) {
425
- throw new AgentCatalogReadError("agx_get_list_full (pinned)", pgDetail(error));
424
+ throw new AgentCatalogReadError("agent.agx_get_by_ids (pinned)", pgDetail(error));
426
425
  }
427
426
  for (const item of Array.isArray(data) ? data : []) {
428
427
  const summary = toAgentSummary(item);
@@ -814,10 +814,11 @@ declare function getRegisteredAgentCatalog(catalogId?: string): AgentCatalog | u
814
814
  * set this module returns to the store.
815
815
  * 5. ONE write: the caller's star, `ues_set` through the same port. The
816
816
  * retired `agent.definition.is_favorite` column is never read or written.
817
- * 6. THE PINNED READ (2026-10-09): the caller's starred ids (`ues_list`
818
- * through the favorites port), then `agx_get_list_full` filtered to
819
- * exactly those ids (`?id=in.(…)`). A sidebar's Pinned section shows a
820
- * handful of rows; it never pays for the whole catalog to find them.
817
+ * 6. THE PINNED READ (2026-10-09; by-ids 2026-10-23): the caller's starred
818
+ * ids (`ues_list` through the favorites port), then
819
+ * `agent.agx_get_by_ids(p_ids)` for exactly those ids. A sidebar's Pinned
820
+ * section shows a handful of rows; it never pays for the whole catalog,
821
+ * in the browser or on the server, to find them.
821
822
  *
822
823
  * 🚨 THE LIST READ IS COMPLETE OR IT THROWS. PostgREST caps every response at
823
824
  * `db-max-rows` (1000 on Matrx Main) and reports success — matrx-frontend's
@@ -814,10 +814,11 @@ declare function getRegisteredAgentCatalog(catalogId?: string): AgentCatalog | u
814
814
  * set this module returns to the store.
815
815
  * 5. ONE write: the caller's star, `ues_set` through the same port. The
816
816
  * retired `agent.definition.is_favorite` column is never read or written.
817
- * 6. THE PINNED READ (2026-10-09): the caller's starred ids (`ues_list`
818
- * through the favorites port), then `agx_get_list_full` filtered to
819
- * exactly those ids (`?id=in.(…)`). A sidebar's Pinned section shows a
820
- * handful of rows; it never pays for the whole catalog to find them.
817
+ * 6. THE PINNED READ (2026-10-09; by-ids 2026-10-23): the caller's starred
818
+ * ids (`ues_list` through the favorites port), then
819
+ * `agent.agx_get_by_ids(p_ids)` for exactly those ids. A sidebar's Pinned
820
+ * section shows a handful of rows; it never pays for the whole catalog,
821
+ * in the browser or on the server, to find them.
821
822
  *
822
823
  * 🚨 THE LIST READ IS COMPLETE OR IT THROWS. PostgREST caps every response at
823
824
  * `db-max-rows` (1000 on Matrx Main) and reports success — matrx-frontend's
@@ -292,6 +292,9 @@ async function readFavoriteAgentRows(client, favorites) {
292
292
  if (typeof favorites.list !== "function") {
293
293
  throw new AgentPinnedReadUnsupported("the favorites port has no list()");
294
294
  }
295
+ if (typeof client.schema !== "function") {
296
+ throw new AgentPinnedReadUnsupported("the client has no schema()");
297
+ }
295
298
  const listed = await favorites.list("favorite");
296
299
  if (!listed.ok) {
297
300
  throw new AgentCatalogReadError(
@@ -310,13 +313,9 @@ async function readFavoriteAgentRows(client, favorites) {
310
313
  const rows = [];
311
314
  for (let at = 0; at < ids.length; at += PINNED_ID_CHUNK) {
312
315
  const chunk = ids.slice(at, at + PINNED_ID_CHUNK);
313
- const call = client.rpc("agx_get_list_full", {});
314
- if (typeof call.in !== "function") {
315
- throw new AgentPinnedReadUnsupported("the client's rpc builder has no in()");
316
- }
317
- const { data, error } = await call.in("id", chunk);
316
+ const { data, error } = await client.schema("agent").rpc("agx_get_by_ids", { p_ids: chunk });
318
317
  if (error) {
319
- throw new AgentCatalogReadError("agx_get_list_full (pinned)", pgDetail(error));
318
+ throw new AgentCatalogReadError("agent.agx_get_by_ids (pinned)", pgDetail(error));
320
319
  }
321
320
  for (const item of Array.isArray(data) ? data : []) {
322
321
  const summary = toAgentSummary(item);
@@ -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.259",
3
+ "version": "0.59.261",
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": "c3bd77d32ac3ab256c4339fc885dd3d148a069ae"
565
+ "gitHead": "c006aa4394ac233c23c923f57d06b49f769037ab"
566
566
  }