@ai-matrx/agents 0.6.2 → 0.7.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,199 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.0 — 2026-09-08
4
+
5
+ **THE ONE AGENT PICKER moves into the package.** Two new entries — `./catalog`
6
+ (pure) and `./catalog/react` (client-stamped) — carry the whole of
7
+ matrx-frontend's canonical agent picker: the complete-list read, filter / sort /
8
+ search / tabs / counts, the favourite write, the mandate-resolved default row
9
+ with its drift screamer, and both picker shells. Owner ruling (Arman,
10
+ 2026-09-08): the picker and ALL its logic live in the package; an app provides
11
+ identity and a Supabase client and nothing else, and the host's only contract
12
+ is `onSelect(agentId)`.
13
+
14
+ Every existing entry is untouched. This release is purely additive.
15
+
16
+ ### Consumer action (C28)
17
+
18
+ 1. **Install nothing new.** `@ai-matrx/design-system` is now an ordinary
19
+ dependency of this package (`"latest"`, per THE LATEST LAW).
20
+
21
+ 2. **Create the catalog ONCE and mount the provider ONCE:**
22
+
23
+ ```ts
24
+ // lib/agents/catalog.ts
25
+ import { createAgentCatalog } from "@ai-matrx/agents/catalog";
26
+
27
+ export const agentCatalog = createAgentCatalog({
28
+ client: supabase, // REQUIRED
29
+ identity: { requireUserId: () => requireUserId() }, // REQUIRED (throws when signed out)
30
+ transport, // REQUIRED only if a picker passes `defaultMandateKey`
31
+ errorSink, // optional — default: a tagged console.error naming the remedy
32
+ notifier, // optional — the drift banner is the primary scream either way
33
+ storage, // optional — default: localStorage when it answers, else memory
34
+ labels, // optional — every user-visible string
35
+ });
36
+ ```
37
+
38
+ ```tsx
39
+ // app providers
40
+ import "@ai-matrx/agents/catalog/styles.css";
41
+ import { AgentCatalogProvider } from "@ai-matrx/agents/catalog/react";
42
+
43
+ <AgentCatalogProvider
44
+ catalog={agentCatalog}
45
+ LinkComponent={Link} // optional — default: a plain <a> (cmd-click works)
46
+ navigate={router.push} // optional — default: window.location.assign
47
+ renderModelRef={renderModelRef} // optional — default: the model id as text
48
+ openPeek={openSneakPeek} // ABSENT → the peek affordance is hidden, never dead
49
+ popoverContainer={dialogContainer}
50
+ >
51
+ {children}
52
+ </AgentCatalogProvider>
53
+ ```
54
+
55
+ 3. **Register this package's build with your Tailwind source scan**, exactly as
56
+ you already do for `@ai-matrx/design-system` — the picker's layout uses
57
+ ordinary utilities and your Tailwind only generates what it can see:
58
+
59
+ ```css
60
+ @import "tailwindcss";
61
+ @source "../node_modules/@ai-matrx/agents/dist";
62
+ ```
63
+
64
+ 4. **Swap the imports; the prop surface is unchanged.** `AgentListDropdown`
65
+ and `AgentListInlinePicker` keep every prop they had (`onSelect`,
66
+ `navigateTo`, `activeAgentId`, `contentSide`, `consumerId`, `initialTab`,
67
+ `includeSystemInAll`, `visibleTabs`, `systemTabLabel`, `resolveAgentHref`,
68
+ `showPinnedAgent`, `excludeAgentIds`, `triggerSlot`, `noBorder`, `compact`)
69
+ and gain `defaultMandateKey`.
70
+
71
+ 5. **Files a host can DELETE once it adopts** (matrx-frontend paths; the same
72
+ shapes exist in matrx-extend and matrx-local):
73
+ - `features/agents/redux/agent-consumers/slice.ts`
74
+ - `features/agents/redux/agent-consumers/selectors.ts`
75
+ - `features/agents/search/score.ts`
76
+ - `features/agents/constants/agent-list-labels.ts`
77
+ - `features/agents/hooks/useAgentConsumer.ts`
78
+ - `features/agents/hooks/useServerAgentSearch.ts`
79
+ - `features/agents/components/agent-listings/AgentListDropdown.tsx`
80
+ - `features/agents/components/agent-listings/AgentListInlinePicker.tsx`
81
+ - `features/agents/components/agent-listings/useAgentListCore.ts`
82
+ - `features/agents/components/agent-listings/core/*` (AgentListContent,
83
+ AgentListTabs, AgentFilterBar, AgentRow, AgentDetailCard, AgentSortPanel,
84
+ AgentCategoriesPanel, AgentTagsPanel, AgentMobileSubView, primitives, types)
85
+ - `features/agents/components/agent-listings/FavoriteAgentButton.tsx`
86
+ - the `agentConsumers` reducer registration in the store
87
+ - matrx-extend `PilotAgentPicker`, `AgentPicker`, `scope.ts`, the Settings
88
+ default-agent `PillSelect`; matrx-local `AgentPicker.tsx` and its
89
+ `fetchCloudAgents` sort — plus every hardcoded default-agent NAME.
90
+
91
+ ### `@ai-matrx/agents/catalog` — the headless kernel (pure, no banner)
92
+
93
+ - `createAgentCatalog(config)` — required ports `client` (a structural subset
94
+ of `SupabaseClient`: `rpc`, `schema().from()`) and `identity.requireUserId()`;
95
+ a missing one throws `AgentCatalogConfigError` with a remedy. Optional
96
+ `errorSink`, `notifier`, `storage`, `transport`, `labels`, `catalogId`, each
97
+ with a working default and a documented degradation.
98
+ - The complete-list read (`agx_get_list_full`), the tier-2 server search
99
+ (`agx_search`), the single-agent name read (`agx_resolve_agent_address`), and
100
+ the ONE write (`agent.definition.is_favorite`, optimistic with rollback).
101
+ - Per-consumer view state keyed by `consumerId` (tabs, sort, search, favourite
102
+ / archive / access filters, category and tag inclusion, paging, server-search
103
+ results) plus every selector: `filterUserTypeAgents`,
104
+ `filterBuiltinTypeAgents`, `sortFilteredAgents`, `applyAgentSortComparator`,
105
+ `makeSelectFilteredAgents`, the five count selectors, the four split
106
+ category/tag option selectors, `AGENT_NONE_SENTINEL`, and the scorer.
107
+ - `assertAgentCatalogSchema(client)` — the demanded-schema probe, with a
108
+ `selfTest` leg that refuses a probe which cannot fail.
109
+ - Branded `AgentId`; strict types throughout (`exactOptionalPropertyTypes`,
110
+ `noUncheckedIndexedAccess`, zero `any`).
111
+ - No module-level mutable state: the catalog registry, the in-flight list
112
+ promise, the freshness stamp and the default-row cache live on `globalThis`
113
+ under `Symbol.for("ai-matrx.agents.catalog")`.
114
+
115
+ ### `@ai-matrx/agents/catalog/react` — the picker (client-stamped)
116
+
117
+ `AgentCatalogProvider`, `useAgentCatalog`, `useAgentConsumer`,
118
+ `useAgentListCore`, `AgentListDropdown`, `AgentListInlinePicker`,
119
+ `AgentListContent`, `AgentListTabs`, `AgentFilterBar`, `AgentRow`,
120
+ `AgentDetailCard`, `AgentSortPanel`, `AgentCategoriesPanel`, `AgentTagsPanel`,
121
+ `AgentMobileSubView`, `FavoriteAgentButton`, the primitives, and
122
+ `./catalog/styles.css` (structural CSS + a default token sheet; no hardcoded
123
+ colour in any component). React stays an optional peer; the ~20 Lucide glyphs
124
+ are inlined SVGs (C19 — no icon dependency).
125
+
126
+ ### The default row, and THE DRIFT SCREAM (design D3)
127
+
128
+ A picker instance may pass `defaultMandateKey`. The catalog resolves the
129
+ Holder through the aidream door `GET /mandates/{mandate_key}/resolution`
130
+ (verified against `aidream/api/routers/mandate_bindings.py`
131
+ `get_mandate_resolution`, mounted at prefix `/mandates`) using the injected
132
+ `transport` — 🚨 clients NEVER walk the mandate ladder themselves (D-R1), so a
133
+ `defaultMandateKey` with no transport throws a typed config error. The row
134
+ renders the Holder's REAL name and description; its id stays
135
+ `mandate:<key>`, byte-identical to the ref shape matrx-extend and matrx-local
136
+ already hand their hosts. The last resolution is cached for first paint, and
137
+ whenever cached and live disagree on holder id or holder name the picker shows
138
+ a PERSISTENT in-picker banner and calls the `notifier` — never console-only.
139
+ This kills the class that made matrx-local's row say "Matrx Desktop Agent"
140
+ while `local.cloud_chat` resolved to General Chat.
141
+
142
+ ### Behavioural deltas from the matrx-frontend original (recorded, never silent)
143
+
144
+ 1. **The list read is now COMPLETE OR LOUD.** `fetchAgentsListFull` issued a
145
+ bare `.rpc("agx_get_list_full")`, which PostgREST silently caps at
146
+ `db-max-rows` (1000). The package reads through `@ai-matrx/data`'s
147
+ `readAllRows` with `{ count: "exact" }` and a stable `.order("id")`, so a
148
+ truncated catalogue is an `IncompleteReadError`, not a short picker. Row
149
+ ORDER from the DB was already irrelevant — every row is re-sorted client
150
+ side.
151
+ 2. **"Clear (n)" now clears n.** `AgentCategoriesPanel`, `AgentTagsPanel` and
152
+ `AgentMobileSubView` ran `includedCats.forEach(toggleCategory)`; each toggle
153
+ computes its next array from the same render's snapshot, so three toggles in
154
+ one tick removed exactly ONE category. matrx-frontend's own
155
+ `useAgentConsumer` documents the hazard and names `setIncludedCats` as the
156
+ fix; the call sites never adopted it. They do here.
157
+ 3. **`AGENT_PUBLIC_BADGE_LABEL` was dead.** The frontend declared it as the
158
+ builtin row's badge label ("Public") and `AgentRow` rendered the literal
159
+ `system`; nothing ever read the constant. The badge is now the
160
+ `labels.systemBadge` port, defaulting to `"system"` — the string that
161
+ actually shipped.
162
+ 4. **No app-wide "active agent" store.** By ruling D1 that is host state, so
163
+ the pinned row comes from the `activeAgentId` prop alone; the frontend's
164
+ fallback to a Redux `activeAgentId` has no package twin.
165
+ 5. **A click never goes nowhere.** A call site passing neither `onSelect` nor
166
+ `navigateTo` used to `dispatch(setActiveAgentId(...))`; here it reports to
167
+ the `errorSink` instead of silently doing nothing.
168
+ 6. **`isVersion` filtering is structural, not conditional.** The frontend's
169
+ `selectLiveAgents` strips version snapshots out of a store that also holds
170
+ them; this catalog only ever holds list rows, so the filter is a no-op and
171
+ is not ported. Every ordering, tie-break and count is unchanged.
172
+ 7. **Three class names are package-owned.** `scrollbar-none`,
173
+ `animate-in fade-in-0` and `animate-spin` came from the host's own globals
174
+ or a Tailwind plugin; a package never invents a class name and expects the
175
+ consumer to generate it, so they ship here as `matrx-agent-scrollbar-none`,
176
+ `matrx-agent-fade-in` and `matrx-agent-spin`. The amber favourite star and
177
+ the drift banner are `--matrx-agent-*` tokens with default values.
178
+ 8. **Server search is folded into `useAgentConsumer`.** The frontend's separate
179
+ `useServerAgentSearch` hook was wired in exactly one place — here — so it
180
+ is one hook, with the same debounce (250ms), the same 2-character floor and
181
+ the same stale-response guard.
182
+
183
+ ### Parity proof
184
+
185
+ `matrx-frontend/scripts/agent-picker-parity-fixture.ts` runs THE ORIGINAL
186
+ matrx-frontend selectors over 66 realistic `agx_get_list_full` rows (owned,
187
+ directly-shared, org-shared, builtin, favourites, archived, null names, null
188
+ categories, untagged rows, unicode, duplicate names, colliding timestamps)
189
+ across a 295-case matrix — every tab × every sort × favoritesFirst × favourite
190
+ filter × archive filter × access filter × category and tag inclusion including
191
+ the none-sentinel × ten search queries × server-matched ids × the admin blended
192
+ "All" — and writes the expected ordered id lists, counts and option lists to
193
+ `catalog/__tests__/parity-expected.json`. `catalog/__tests__/parity.test.ts`
194
+ asserts the ported selectors reproduce all of it exactly, and carries a
195
+ falsifiability leg that requires a perturbed row to turn the matrix red.
196
+
3
197
  ## 0.6.2
4
198
 
5
199
  Automatic changed-only republish (docs/metadata drift since the last tag — see
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # @ai-matrx/agents
2
2
 
3
3
  Portable client-side primitives for AI Matrx agent applications. The package
4
- standardizes the stream wire, pure request/workflow projection, and safe
5
- Creator-facing result boundaries without importing React, Redux, Next.js, or
6
- application code.
4
+ standardizes the stream wire, pure request/workflow projection, safe
5
+ Creator-facing result boundaries, and THE agent picker — without importing
6
+ Redux, Next.js, or application code.
7
7
 
8
8
  **This package talks to the fully assembled AI Dream brain. It does not run the
9
9
  provider/tool loop locally; use `@ai-matrx/agent-engine` when you need to run a
@@ -146,12 +146,72 @@ const { phase, projection, start, cancel } = useAgentRun({ transport });
146
146
  `useFollowRuntimeOperation({ transport, executionId, onEvent })` is the
147
147
  reconnect follower as a hook — production stall/retry/cursor policy built in.
148
148
 
149
+ ## THE agent picker (`./catalog` + `./catalog/react`)
150
+
151
+ There is ONE agent picker on this platform and it ships here. An app provides a
152
+ Supabase client and who the user is; the picker's contract back is
153
+ `onSelect(agentId)`.
154
+
155
+ ```ts
156
+ // once, anywhere
157
+ import { createAgentCatalog } from "@ai-matrx/agents/catalog";
158
+
159
+ export const agentCatalog = createAgentCatalog({
160
+ client: supabase, // REQUIRED
161
+ identity: { requireUserId: () => requireUserId() }, // REQUIRED
162
+ transport, // only when a picker passes `defaultMandateKey`
163
+ });
164
+ ```
165
+
166
+ ```tsx
167
+ // once, at the top of your tree
168
+ import "@ai-matrx/agents/catalog/styles.css";
169
+ import { AgentCatalogProvider, AgentListDropdown } from "@ai-matrx/agents/catalog/react";
170
+
171
+ <AgentCatalogProvider catalog={agentCatalog} LinkComponent={Link}>
172
+ <AgentListDropdown activeAgentId={agentId} onSelect={setAgentId} />
173
+ </AgentCatalogProvider>
174
+ ```
175
+
176
+ You get, on every surface and in every app: the complete catalogue read (owned
177
+ + shared + org-shared + platform agents), Mine / Shared / All / Public tabs
178
+ with live counts, sort, favourites (with the write), category and tag filters,
179
+ local relevance scoring plus server-side search, the current agent pinned on
180
+ top, per-row origin badges, a hover detail panel, a mobile drawer, and a footer
181
+ count. `AgentListInlinePicker` is the same core without the popover shell.
182
+
183
+ Register this package's build with your Tailwind source scan so the picker's
184
+ utilities are generated:
185
+
186
+ ```css
187
+ @import "tailwindcss";
188
+ @source "../node_modules/@ai-matrx/agents/dist";
189
+ ```
190
+
191
+ **The default row.** Pass `defaultMandateKey` and the picker offers the
192
+ platform default for that Mandate, resolved LIVE through the server and
193
+ labelled with the Holder's real name — no client ever hardcodes an agent name
194
+ again. Its id is `mandate:<key>`. If the cached name and the live one disagree,
195
+ the picker shows a persistent banner and calls your `notifier`.
196
+
197
+ **Ports.** `client` and `identity` are required and throw when absent. Every
198
+ other port ships a working default: `errorSink` (tagged console), `notifier`
199
+ (the banner is the primary channel), `storage` (localStorage, else memory),
200
+ `labels`, `navigate` (`window.location.assign`), `LinkComponent` (a plain
201
+ `<a>`), `renderModelRef` (plain text) and `openPeek` (absent → the affordance
202
+ is hidden, never dead).
203
+
204
+ `assertAgentCatalogSchema(client)` proves at boot that the database can answer
205
+ `agx_get_list_full`, `agx_search`, `agx_resolve_agent_address` and the
206
+ favourite write.
207
+
149
208
  ## Runtime support
150
209
 
151
210
  The package targets modern browsers, browser-based desktop shells,
152
211
  extensions, Next.js client or server modules, and Node 20+. Its one runtime
153
- dependency is `@ai-matrx/data` (the shared net-resilience + credentials
154
- layer); `react >= 18` is an optional peer needed only by `./react`. No entry
212
+ dependencies are `@ai-matrx/data` (the shared net-resilience + credentials
213
+ layer) and `@ai-matrx/design-system` (the picker's primitives); `react >= 18`
214
+ is an optional peer needed only by `./react` and `./catalog/react`. No entry
155
215
  point performs work at import time.
156
216
 
157
217
  ## Development