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