@ai-matrx/agents 0.8.0 → 0.9.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 CHANGED
@@ -1,5 +1,248 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.1 — 2026-09-08
4
+
5
+ **TWO SCREENS TOLD THE SAME USER TWO DIFFERENT TRUTHS. BOTH ARE FIXED HERE, IN
6
+ THE PACKAGE, AT THE CLASS.**
7
+
8
+ Independent final verification of the picker unification (2026-09-08, same
9
+ account signed in on both surfaces) found the tab strip, the counts, the
10
+ Mine/Shared/All order, the five sort options, the tri-state favourites control,
11
+ the 42 categories, the 871 tags and four tag-only searches identical between
12
+ `aimatrx.com/chat/new` and `workflows.aimatrx.com`. Two things were not.
13
+
14
+ ### Consumer action: none
15
+
16
+ No export was removed, renamed or re-signed. Three symbols were ADDED —
17
+ `compareAgentIds`, `agentMatchesArchiveFilter`, `selectAgentTabCounts` — and the
18
+ web `AgentRow` gained a `data-testid` matching the native row's `testID`.
19
+ Every host adopts this by taking `latest`; there is nothing to change.
20
+
21
+ ### Behavioural deltas (both deliberate, both were defects)
22
+
23
+ **F1 — every sort is now a TOTAL order, ending on `id` ASC.**
24
+
25
+ The Public tab diverged at index 10. 17 rows tied on
26
+ `updated_at = 2026-09-08T06:52:46.430326+00:00`, and the two hosts fetch the
27
+ same RPC through different transports:
28
+
29
+ | host | request | order of tied rows |
30
+ |---|---|---|
31
+ | matrx-frontend | `POST /rpc/agx_get_list_full` (one shot) | the RPC's own |
32
+ | workflow-studio | `…?order=id.asc&offset=0&limit=1000` (`readAllRows`) | id ASC |
33
+
34
+ Neither client was wrong about the ROWS. The sort was not total: each
35
+ comparator branch returned `0` for tied rows, so `Array.prototype.sort` left
36
+ them wherever the transport had put them. `sortFilteredAgents` and
37
+ `applyAgentSortComparator` now end on the same final key the database itself
38
+ ends on — `public.agx_get_list` is
39
+ `ORDER BY is_favorite DESC, updated_at DESC, id` — giving:
40
+
41
+ searching: score DESC -> server rank ASC -> sort key -> id ASC
42
+ not searching: favourites first (when asked) -> sort key -> id ASC
43
+
44
+ Comparison is a codepoint `<`/`>`, never `localeCompare`: Postgres orders
45
+ `uuid` by its raw bytes and a collator is free to fold case or ignore the
46
+ dashes, which would diverge from the server on exactly these ties. A sort key
47
+ that yields `NaN` (an unparseable timestamp) now collapses to the id key
48
+ instead of defeating it.
49
+
50
+ *What the frontend used to do on ties: whatever order the RPC page came back
51
+ in.* That was never a decision — it was the absence of one, and it happened to
52
+ look stable only until a second host fetched the same rows a different way. The
53
+ order is now DEFINED, is the server's own, and is a function of the rows alone.
54
+
55
+ **F5 — a tab badge counts exactly what the tab renders.**
56
+
57
+ The Mine badge said 416 while the list rendered 412 rows and the footer said
58
+ "412 agents": 4 owned and 2 shared ARCHIVED agents were counted by the badge
59
+ and (correctly) hidden by the list. One screen, three numbers, one of them
60
+ untrue about agents the user cannot see or reach from there. This is "a screen
61
+ never lies", so it is a fix and not a knob.
62
+
63
+ `selectAgentTabCounts(rows, archFilter)` is the new consumer-aware badge
64
+ source used by `useAgentListCore`, and the four plain aggregate counts
65
+ (`selectTotalUserAgentsCount`, `…Owned…`, `…Shared…`, `…Favorite…`) now honour
66
+ the same archive rule the list uses. Flip the archive filter and the badges
67
+ follow the list into the archive. `selectTotalBuiltinAgentsCount` is
68
+ deliberately unchanged: `filterBuiltinTypeAgents` applies no archive filter
69
+ (a builtin belongs to the platform and is never the user's to archive), so it
70
+ already counted exactly what it rendered.
71
+
72
+ ### The proof
73
+
74
+ `catalog/__tests__/parity.test.ts`'s 66-row fixture had NO ties, which is why
75
+ 293 green cases never saw F1. The new fixture does:
76
+ `catalog/__tests__/total-order-fixture.json` — 42 rows, every one on that exact
77
+ live `updated_at`, 26 of them also tied on `created_at`, with name and category
78
+ tie groups on top, ids deliberately uncorrelated with emission order.
79
+
80
+ `total-order-expected.json` is generated **from the RULE** by
81
+ `scripts/generate-total-order-fixture.mjs` — favourites -> key -> id ASC,
82
+ written out independently and importing nothing from `catalog/selectors.ts`.
83
+ Regenerating it by running the shipped comparator would have made the test a
84
+ mirror of the code; it measures the specification instead.
85
+
86
+ 720 cases (tab x sort x favouritesFirst x archive x favourite x access) are
87
+ asserted three times over — kernel (`catalog/__tests__/total-order.test.ts`),
88
+ web shell (`catalog/react/total-order.test.tsx`) and native shell
89
+ (`catalog/native/__tests__/total-order.test.tsx`) — each reading the RENDERED
90
+ row ids, not the selector's return value.
91
+
92
+ Falsifiability, on every leg:
93
+
94
+ - the same rows are fed in four different input orders (identity, two seeded
95
+ shuffles, reversed) and the answer must be byte-identical — this is the live
96
+ defect, reproduced as a test;
97
+ - both shells mount the picker twice with the row array reversed and compare
98
+ screen to screen;
99
+ - deleting the id tiebreaker turns 4 of the 10 kernel tests red (verified
100
+ before shipping), and a key-only sort of the same fixture is asserted to
101
+ disagree with itself under reversal;
102
+ - the F5 legs assert the OLD counting rule produces a DIFFERENT number on this
103
+ fixture, so "badge == rows" cannot pass vacuously.
104
+
105
+ `parity.test.ts`'s count assertion now states the F5 delta explicitly — the
106
+ recorded matrx-frontend number minus the archived rows the list hides — rather
107
+ than reproducing a screen that lies. Its 293-case ordering matrix and the
108
+ native shell's 293-case matrix are UNCHANGED and still green: the old fixture
109
+ had no ties, so F1 moved nothing that was already defined.
110
+
111
+ ## 0.9.0 — 2026-09-08
112
+
113
+ **THE ONE PICKER NOW RENDERS ON REACT NATIVE — `@ai-matrx/agents/catalog/native`.**
114
+
115
+ Matrx Mobile (Expo 54 / RN 0.81) was the last client still hand-rolling its
116
+ agent list. Its sheet showed four HARDCODED agents plus a Supabase read of a
117
+ `prompts` table that **does not exist in the platform database** — a read whose
118
+ PostgREST error was swallowed into an empty array, so the list silently never
119
+ had anything but the four constants. Three of those four constants named the
120
+ WRONG agent: the ids behind "General Chat", "Deep Research" and "Code Helper"
121
+ resolve in `agent.definition` to *Agent Ledger*, *Knowledge Search Test* and
122
+ *Balanced News Analysis*. A label lying on screen, with nothing anywhere that
123
+ could notice — the same class the mandate default row was built to end.
124
+
125
+ React Native could not adopt the picker before this release for two concrete
126
+ reasons, and both are fixed here rather than worked around in the app.
127
+
128
+ ### Consumer action (C28)
129
+
130
+ **Web hosts: none.** `@ai-matrx/agents/catalog` and `/catalog/react` keep every
131
+ export, every name and every behaviour. The provider, the store subscription
132
+ and the two list hooks moved to `catalog/shared/*` so both platforms run one
133
+ implementation; `catalog/react` re-exports all of them unchanged, and the
134
+ `"use client"` banner is still stamped on the react build and still absent from
135
+ the pure ones (the tarball canary now judges eight artefacts instead of four).
136
+
137
+ **React Native hosts:**
138
+
139
+ ```tsx
140
+ import AsyncStorage from "@react-native-async-storage/async-storage";
141
+ import { createAgentCatalog } from "@ai-matrx/agents/catalog";
142
+ import { AgentCatalogProvider, AgentListSheet } from "@ai-matrx/agents/catalog/native";
143
+
144
+ const catalog = createAgentCatalog({
145
+ client: supabase,
146
+ identity: { requireUserId: () => requireUserId() },
147
+ storage: AsyncStorage, // NEW — the async storage seam
148
+ transport, // only when a picker passes defaultMandateKey
149
+ errorSink: reportToDiagnostics,
150
+ });
151
+
152
+ <AgentCatalogProvider catalog={catalog} navigate={(href) => router.push(href)}>
153
+ <AgentListSheet consumerId="chat" onSelect={setAgentId} activeAgentId={agentId} />
154
+ </AgentCatalogProvider>
155
+ ```
156
+
157
+ - `react-native` and `react-native-svg` are OPTIONAL peers. Install
158
+ `react-native-svg` (`npx expo install react-native-svg`) for the real glyphs;
159
+ without it the icons fall back to text glyphs and **say so once** through the
160
+ `errorSink`, with the remedy. Nothing goes blank and nothing goes quiet.
161
+ - Bind `navigate` if your rows should be able to open an agent. Unbound, the
162
+ default is a no-op that REPORTS through the `errorSink` — never a control
163
+ that silently does nothing.
164
+ - `@ai-matrx/agents/catalog/styles.css` is a WEB artefact; the native entry
165
+ needs no stylesheet.
166
+
167
+ ### What is new
168
+
169
+ - **`@ai-matrx/agents/catalog/native`** — the picker in React Native
170
+ primitives over the SAME headless kernel and the SAME hooks:
171
+ `AgentCatalogProvider`, `AgentListSheet` (the body of a bottom sheet — the
172
+ host keeps its own sheet chrome, so no gesture library is forced on anyone),
173
+ `AgentListInlinePicker`, and every part beneath them (`AgentListContent`,
174
+ `AgentListTabs`, `AgentFilterBar`, `AgentRow`, `AgentDetailCard`,
175
+ `AgentSubView`, `FavoriteAgentButton`, the five primitives, `AgentIcon`).
176
+ The shells take the web shells' prop surface name for name: `onSelect`,
177
+ `activeAgentId`, `consumerId`, `initialTab`, `includeSystemInAll`,
178
+ `visibleTabs`, `systemTabLabel`, `showPinnedAgent`, `excludeAgentIds`,
179
+ `defaultMandateKey`, `compact`.
180
+ - **The async storage seam** — `AgentCatalogAsyncStorage` /
181
+ `AgentCatalogStorageLike`, plus `readCachedDefaultRowAsync` and
182
+ `writeCachedDefaultRowAsync`. `createAgentCatalog({ storage })` now accepts an
183
+ `AsyncStorage`-shaped store verbatim. React Native has no `localStorage`, so
184
+ before this a native host could only hand the catalog a store that answered
185
+ `null` synchronously (a lie) or lose the default row's first paint entirely.
186
+ **The web path is unchanged**: `AgentCatalogStorage` is still synchronous,
187
+ `readCachedDefaultRow`/`writeCachedDefaultRow` still have their synchronous
188
+ signatures, `defaultStorage()` still prefers `localStorage`, and the async
189
+ helpers over a synchronous store produce byte-identical cache payloads
190
+ (proven in `catalog/__tests__/storage-seam.test.ts`).
191
+ - **`AgentCatalogNativeTheme`** (C26 on a platform with no CSS) — the native
192
+ twin of the `--matrx-agent-*` sheet: every token ships a DEFAULT VALUE so the
193
+ picker renders correctly unthemed, and a host re-brands by passing `theme` to
194
+ the provider. `DEFAULT_AGENT_CATALOG_NATIVE_THEME` (dark) and
195
+ `LIGHT_AGENT_CATALOG_NATIVE_THEME` ship; `resolveAgentCatalogNativeTheme`
196
+ merges an override and keeps every value it omits. No native component
197
+ contains a colour literal.
198
+ - **`catalog/shared/icon-paths.ts`** — the glyph geometry, once. The web icons
199
+ now read it too, so a shape can never differ between the two platforms; the
200
+ native entry draws the same paths through `react-native-svg` (C19: still no
201
+ icon-library dependency, on either platform).
202
+ - **`AgentListTabCounts` moved** to `catalog/shared/useAgentListCore` beside the
203
+ code that computes it. `catalog/react/AgentListTabs` re-exports it unchanged.
204
+
205
+ ### Behavioural deltas on native, recorded rather than hidden
206
+
207
+ 1. **No hover preview.** The web picker's side panel opens on hover; a touch
208
+ screen has no hover, so the detail card is reached by the row's chevron —
209
+ the same three-view stack (`list → detail → sort/categories/tags`) the web
210
+ `AgentListInlinePicker` already used on mobile widths.
211
+ 2. **No cmd-click link.** `AgentRow`'s anchor exists on the web so a person can
212
+ open an agent in a new tab. React Native has neither, so the row is an
213
+ ordinary `Pressable`; the `LinkComponent` port still exists and still ships
214
+ a working native default.
215
+ 3. **`autoFocusSearch` defaults to `false`** (the web inline picker defaults to
216
+ `true`): a keyboard that opens with the sheet covers the list it was meant
217
+ to filter.
218
+
219
+ ### Proof
220
+
221
+ - **The 293-case ordering matrix, re-measured through the RENDERED native
222
+ list** (`catalog/native/__tests__/parity.test.tsx`): every case's ids, in
223
+ order, read back out of the mounted native tree — not out of the selectors a
224
+ second time. Sharing a kernel is an architecture claim; this is the
225
+ measurement. It carries a falsifiability leg.
226
+ - **The tarball canary imports AND requires `./catalog/native`**, checks every
227
+ documented export, proves the theme override merges over the PACKED build,
228
+ proves the icon degradation announces exactly once with its remedy, and
229
+ compiles a native usage (including an `AsyncStorage`-shaped `storage`) against
230
+ the packed types under `exactOptionalPropertyTypes` + `noUncheckedIndexedAccess`.
231
+ - **The banner canary now judges eight artefacts**: the `"use client"` prologue
232
+ must be present on both `catalog/react` builds and ABSENT from all four
233
+ `catalog/index.*` and all four `catalog/native/index.*` files.
234
+
235
+ ### Known limits, stated plainly
236
+
237
+ There is still **no React Native canary harness** in this workspace (campaign
238
+ decision C7 schedules it for Phase 2), and the real `react-native` package
239
+ cannot be loaded by vitest — it ships Flow-typed source only Metro parses. The
240
+ native tests therefore render against a minimal RN stand-in
241
+ (`catalog/native/__tests__/react-native.stub.tsx`) that maps RN primitives onto
242
+ DOM nodes. That proves rows, order, labels, counts, handlers and every
243
+ announced degradation. It does **not** prove pixel layout, gestures or Hermes —
244
+ those are proven by the consuming Expo app's own build and device smoke.
245
+
3
246
  ## 0.8.0 — 2026-09-08
4
247
 
5
248
  **THE ORCHESTRA BADGE IS BACK, in every client at once.**
@@ -34,10 +34,12 @@ __export(catalog_exports, {
34
34
  _resetCatalogGlobalState: () => _resetCatalogGlobalState,
35
35
  agentConsumerHasActiveFilters: () => agentConsumerHasActiveFilters,
36
36
  agentListEmptyLabel: () => agentListEmptyLabel,
37
+ agentMatchesArchiveFilter: () => agentMatchesArchiveFilter,
37
38
  agentMatchesSearch: () => agentMatchesSearch,
38
39
  applyAgentSortComparator: () => applyAgentSortComparator,
39
40
  asAgentId: () => asAgentId,
40
41
  assertAgentCatalogSchema: () => assertAgentCatalogSchema,
42
+ compareAgentIds: () => compareAgentIds,
41
43
  computeAgentSearchScore: () => computeAgentSearchScore,
42
44
  createAgentCatalog: () => createAgentCatalog,
43
45
  createMemoryStorage: () => createMemoryStorage,
@@ -69,9 +71,11 @@ __export(catalog_exports, {
69
71
  parseMandateResolution: () => parseMandateResolution,
70
72
  readAgentCatalogRows: () => readAgentCatalogRows,
71
73
  readCachedDefaultRow: () => readCachedDefaultRow,
74
+ readCachedDefaultRowAsync: () => readCachedDefaultRowAsync,
72
75
  readSingleAgentRow: () => readSingleAgentRow,
73
76
  resolveDefaultRow: () => resolveDefaultRow,
74
77
  searchAgentsOnServer: () => searchAgentsOnServer,
78
+ selectAgentTabCounts: () => selectAgentTabCounts,
75
79
  selectAllAgentCategories: () => selectAllAgentCategories,
76
80
  selectAllAgentTags: () => selectAllAgentTags,
77
81
  selectAllSystemAgentCategories: () => selectAllSystemAgentCategories,
@@ -87,7 +91,8 @@ __export(catalog_exports, {
87
91
  sortFilteredAgents: () => sortFilteredAgents,
88
92
  toAgentSummary: () => toAgentSummary,
89
93
  writeAgentFavorite: () => writeAgentFavorite,
90
- writeCachedDefaultRow: () => writeCachedDefaultRow
94
+ writeCachedDefaultRow: () => writeCachedDefaultRow,
95
+ writeCachedDefaultRowAsync: () => writeCachedDefaultRowAsync
91
96
  });
92
97
  module.exports = __toCommonJS(catalog_exports);
93
98
 
@@ -312,8 +317,8 @@ async function readSingleAgentRow(client, agentId) {
312
317
  }
313
318
  return null;
314
319
  }
315
- async function writeAgentFavorite(client, agentId, isFavorite) {
316
- const { error } = await client.schema("agent").from("definition").update({ is_favorite: isFavorite }).eq("id", agentId);
320
+ async function writeAgentFavorite(client, agentId, isFavorite2) {
321
+ const { error } = await client.schema("agent").from("definition").update({ is_favorite: isFavorite2 }).eq("id", agentId);
317
322
  if (error) {
318
323
  throw new AgentCatalogReadError(
319
324
  "agent.definition.is_favorite",
@@ -430,6 +435,51 @@ function writeCachedDefaultRow(storage, resolved) {
430
435
  } catch {
431
436
  }
432
437
  }
438
+ function parseCachedDefaultRow(mandateKey, raw) {
439
+ try {
440
+ if (!raw) return null;
441
+ const parsed = JSON.parse(raw);
442
+ if (!isRecord2(parsed)) return null;
443
+ const holderId = parsed["holderId"];
444
+ const holderName = parsed["holderName"];
445
+ if (typeof holderId !== "string" || typeof holderName !== "string") {
446
+ return null;
447
+ }
448
+ const description = parsed["holderDescription"];
449
+ const provenance = parsed["provenance"];
450
+ const at = parsed["at"];
451
+ return {
452
+ mandateKey,
453
+ holderId,
454
+ holderName,
455
+ holderDescription: typeof description === "string" ? description : null,
456
+ provenance: typeof provenance === "string" ? provenance : "system",
457
+ at: typeof at === "number" ? at : 0
458
+ };
459
+ } catch {
460
+ return null;
461
+ }
462
+ }
463
+ async function readCachedDefaultRowAsync(storage, mandateKey) {
464
+ let raw;
465
+ try {
466
+ raw = await storage.getItem(
467
+ CACHE_PREFIX + mandateKey
468
+ );
469
+ } catch {
470
+ return null;
471
+ }
472
+ return parseCachedDefaultRow(mandateKey, raw);
473
+ }
474
+ async function writeCachedDefaultRowAsync(storage, resolved) {
475
+ try {
476
+ await storage.setItem(
477
+ CACHE_PREFIX + resolved.mandateKey,
478
+ JSON.stringify(resolved)
479
+ );
480
+ } catch {
481
+ }
482
+ }
433
483
  function defaultRowFromResolution(resolved) {
434
484
  return {
435
485
  id: mandateAgentId(resolved.mandateKey),
@@ -469,7 +519,7 @@ function detectDefaultRowDrift(cached, live) {
469
519
  }
470
520
  async function resolveDefaultRow(args) {
471
521
  const { mandateKey, transport, client, storage, lookupRow } = args;
472
- const cached = readCachedDefaultRow(storage, mandateKey);
522
+ const cached = await readCachedDefaultRowAsync(storage, mandateKey);
473
523
  const verdict = await askTheServer(transport, mandateKey);
474
524
  if (verdict.holderType !== "agent") {
475
525
  throw new MandateDefaultRowError({
@@ -506,7 +556,7 @@ async function resolveDefaultRow(args) {
506
556
  at: Date.now()
507
557
  };
508
558
  const drift = detectDefaultRowDrift(cached, resolved);
509
- writeCachedDefaultRow(storage, resolved);
559
+ await writeCachedDefaultRowAsync(storage, resolved);
510
560
  return { resolved, row: defaultRowFromResolution(resolved), drift };
511
561
  }
512
562
 
@@ -846,7 +896,7 @@ function createAgentCatalog(config) {
846
896
  mergeRows(result.rows);
847
897
  return result.ids;
848
898
  }
849
- async function setFavorite(agentId, isFavorite) {
899
+ async function setFavorite(agentId, isFavorite2) {
850
900
  const existing = state.byId[agentId];
851
901
  if (!existing) {
852
902
  errorSink({
@@ -855,15 +905,15 @@ function createAgentCatalog(config) {
855
905
  });
856
906
  return;
857
907
  }
858
- mergeRows([{ ...existing, isFavorite }]);
908
+ mergeRows([{ ...existing, isFavorite: isFavorite2 }]);
859
909
  try {
860
- await writeAgentFavorite(client, agentId, isFavorite);
910
+ await writeAgentFavorite(client, agentId, isFavorite2);
861
911
  } catch (e) {
862
912
  mergeRows([existing]);
863
913
  errorSink({
864
914
  code: "favorite_write_failed",
865
915
  message: `the favorite could not be saved: ${errorMessage(e)}`,
866
- context: { agentId, isFavorite }
916
+ context: { agentId, isFavorite: isFavorite2 }
867
917
  });
868
918
  notifier?.({
869
919
  level: "error",
@@ -1142,7 +1192,19 @@ function memoize3(fn) {
1142
1192
  return last;
1143
1193
  };
1144
1194
  }
1195
+ function compareAgentIds(a, b) {
1196
+ const idA = a.id ?? "";
1197
+ const idB = b.id ?? "";
1198
+ if (idA < idB) return -1;
1199
+ if (idA > idB) return 1;
1200
+ return 0;
1201
+ }
1145
1202
  function applyAgentSortComparator(a, b, sortBy) {
1203
+ const primary = compareAgentSortKey(a, b, sortBy);
1204
+ if (!Number.isFinite(primary) || primary === 0) return compareAgentIds(a, b);
1205
+ return primary;
1206
+ }
1207
+ function compareAgentSortKey(a, b, sortBy) {
1146
1208
  switch (sortBy) {
1147
1209
  case "name-asc":
1148
1210
  return (a.name ?? "").localeCompare(b.name ?? "");
@@ -1390,21 +1452,45 @@ var makeSelectFilteredBuiltinAgentsCount = () => {
1390
1452
  function agentConsumerHasActiveFilters(consumer, defaults) {
1391
1453
  return consumer.tab !== defaults.tab || consumer.sortBy !== defaults.sortBy || consumer.searchTerm !== "" || consumer.includedCats.length > 0 || consumer.includedTags.length > 0 || consumer.favFilter !== defaults.favFilter || consumer.archFilter !== defaults.archFilter || consumer.accessFilter !== defaults.accessFilter || consumer.favoritesFirst !== defaults.favoritesFirst;
1392
1454
  }
1455
+ function agentMatchesArchiveFilter(agent, archFilter) {
1456
+ if (archFilter === "active") return !agent.isArchived;
1457
+ if (archFilter === "archived") return Boolean(agent.isArchived);
1458
+ return true;
1459
+ }
1460
+ function countUserRows(agents, archFilter, predicate) {
1461
+ let n = 0;
1462
+ for (const a of agents) {
1463
+ if (a.agentType !== "user") continue;
1464
+ if (!agentMatchesArchiveFilter(a, archFilter)) continue;
1465
+ if (predicate(a)) n += 1;
1466
+ }
1467
+ return n;
1468
+ }
1469
+ var isOwned = (a) => a.isOwner === true;
1470
+ var isShared = (a) => a.isOwner === false && a.accessLevel != null;
1471
+ var isAny = () => true;
1472
+ var isFavorite = (a) => Boolean(a.isFavorite);
1473
+ var selectAgentTabCounts = memoize2(
1474
+ (agents, archFilter) => ({
1475
+ mine: countUserRows(agents, archFilter, isOwned),
1476
+ shared: countUserRows(agents, archFilter, isShared),
1477
+ all: countUserRows(agents, archFilter, isAny),
1478
+ system: selectBuiltinTypeAgents(agents).length
1479
+ })
1480
+ );
1393
1481
  var selectTotalUserAgentsCount = memoize1(
1394
- (agents) => selectUserTypeAgents(agents).length
1482
+ (agents) => countUserRows(agents, "active", isAny)
1395
1483
  );
1396
1484
  var selectTotalOwnedAgentsCount = memoize1(
1397
- (agents) => selectUserTypeAgents(agents).filter((a) => a.isOwner === true).length
1485
+ (agents) => countUserRows(agents, "active", isOwned)
1398
1486
  );
1399
1487
  var selectTotalSharedAgentsCount = memoize1(
1400
- (agents) => selectUserTypeAgents(agents).filter(
1401
- (a) => a.isOwner === false && a.accessLevel != null
1402
- ).length
1488
+ (agents) => countUserRows(agents, "active", isShared)
1403
1489
  );
1404
1490
  var selectTotalBuiltinAgentsCount = memoize1(
1405
1491
  (agents) => selectBuiltinTypeAgents(agents).length
1406
1492
  );
1407
1493
  var selectTotalFavoriteAgentsCount = memoize1(
1408
- (agents) => selectUserTypeAgents(agents).filter((a) => a.isFavorite).length
1494
+ (agents) => countUserRows(agents, "active", isFavorite)
1409
1495
  );
1410
1496
  //# sourceMappingURL=index.cjs.map