@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.
@@ -1,587 +1,5 @@
1
- /**
2
- * `@ai-matrx/agents/catalog` — the row shape and the per-consumer view state.
3
- *
4
- * `AgentSummary` is the 19-column row `agx_get_list_full()` returns, camelCased.
5
- * It is deliberately NOT the editable agent record: dirty tracking, undo,
6
- * messages, tools and `_fetchStatus` stay in whichever host owns the editor.
7
- * A list row is a list row.
8
- *
9
- * The view-state types are a VERBATIM port of matrx-frontend
10
- * `features/agents/redux/agent-consumers/slice.ts` — only the coupling seam
11
- * inverts (Redux reducers become store actions in `./store`).
12
- */
13
- /**
14
- * A branded agent id. Every id that reaches `onSelect` is one of these:
15
- * either a `agent.definition` UUID, or the `mandate:<key>` reference a
16
- * default row carries (byte-identical to the ref shape matrx-extend
17
- * `src/lib/mandates.ts` and matrx-local `desktop/src/lib/mandates.ts`
18
- * already hand back to their hosts).
19
- *
20
- * 🚨 The brand is a STRING LITERAL, never `unique symbol`. This package emits
21
- * `./catalog` and `./catalog/react` as two separate bundles, so each `.d.ts`
22
- * carries its own copy of these declarations. A `unique symbol` brand is
23
- * NOMINAL: two copies of it are unrelated types, so `createAgentCatalog()`
24
- * from `./catalog` was not assignable to `<AgentCatalogProvider catalog=…>`
25
- * from `./catalog/react` — the exact two-import wiring the README, the
26
- * CHANGELOG and every consumer use. A literal brand is structural, so the two
27
- * copies unify, while a bare `string` still cannot be passed where an
28
- * `AgentId` is required (which is the whole point of branding). Any future
29
- * brand in this package follows the same rule.
30
- */
31
- type AgentId = string & {
32
- readonly __agentId: "ai-matrx.agent-id";
33
- };
34
- /** Brand a raw string as an `AgentId`. */
35
- declare function asAgentId(value: string): AgentId;
36
- /** The `mandate:<key>` ref shape every client already speaks. */
37
- declare function mandateAgentId(mandateKey: string): AgentId;
38
- /** True when an id is a mandate default row rather than a stored agent. */
39
- declare function isMandateAgentId(id: string): boolean;
40
- /** `agent_type` verbatim. Only `builtin` changes how a row is grouped. */
41
- type AgentCatalogAgentType = "user" | "builtin";
42
- /**
43
- * `access_level` verbatim from `agx_get_list_full`. Widened over the raw
44
- * string because it is a DB-owned vocabulary: an unknown level renders as
45
- * itself rather than being coerced into a wrong one.
46
- */
47
- type AgentAccessLevel = "owner" | "admin" | "editor" | "viewer" | "system" | (string & {});
48
- /**
49
- * Present on a catalog row ONLY when that agent is an **Orchestra** — a
50
- * conductor that delegates to member agents at run time. Choosing one for a
51
- * step, a chat or a workflow node runs the whole ensemble, so a person has to
52
- * be able to SEE that before choosing, not discover it from the run.
53
- *
54
- * The DB decides. `agx_get_list_full` / `agx_get_list` / `agx_search` build
55
- * this from `platform.associations` (the conductor self-edge plus its member
56
- * edges) in one `agx_orchestra_badges()` join — never a second fetch, and
57
- * never a per-row read. `null` for every ordinary agent.
58
- *
59
- * History: the badge lived only in the Python `GET /agents` route, so every
60
- * client lost it when the pickers moved to the RPC. It is a ROW FIELD now, in
61
- * the one read every client shares.
62
- */
63
- interface OrchestraBadge {
64
- /** How the ensemble runs: `supervisor` | `sequential` | `parallel` | `dag`, or a future mode verbatim. */
65
- mode: string;
66
- /** The composition's one-line description, when its author wrote one. */
67
- tagline: string | null;
68
- /**
69
- * The per-composition delegation-depth budget AS DECLARED, or `null` when
70
- * the composition declares none. Deliberately NOT defaulted here: the
71
- * platform's fallback ceiling is enforced by the server that runs the
72
- * ensemble, and a badge that invented a number would be reporting a promise
73
- * nobody made.
74
- */
75
- depthBudget: number | null;
76
- /** Member edges. From the ROW count, so an unlabelled ensemble still counts. */
77
- memberCount: number;
78
- /** Only REAL role titles — an ensemble with unlabelled edges yields `[]`. */
79
- memberTitles: string[];
80
- }
81
- /**
82
- * One `agx_get_list_full()` row, camelCased. Nineteen columns, no more:
83
- * anything richer needs a different read and is a different type.
84
- */
85
- interface AgentSummary {
86
- id: AgentId;
87
- name: string | null;
88
- description: string | null;
89
- category: string | null;
90
- tags: string[];
91
- agentType: AgentCatalogAgentType;
92
- modelId: string | null;
93
- isActive: boolean;
94
- isArchived: boolean;
95
- isFavorite: boolean;
96
- createdBy: string | null;
97
- organizationId: string | null;
98
- taskId: string | null;
99
- sourceAgentId: string | null;
100
- createdAt: string | null;
101
- updatedAt: string | null;
102
- isOwner: boolean | null;
103
- accessLevel: AgentAccessLevel | null;
104
- sharedByEmail: string | null;
105
- /**
106
- * The Orchestra badge, or `null` for an ordinary agent. Strict: every row
107
- * carries the key, so a surface reads `agent.orchestra` and never has to ask
108
- * whether the column existed.
109
- */
110
- orchestra: OrchestraBadge | null;
111
- /**
112
- * Set only on a mandate default row — the key it resolved through, so a
113
- * host can tell "the default" from a stored agent without parsing the id.
114
- */
115
- mandateKey?: string;
116
- }
117
- type AgentSortOption = "updated-desc" | "created-desc" | "name-asc" | "name-desc" | "category-asc";
118
- /**
119
- * The sort choices a picker offers, in order. PURE DATA, so it lives in the
120
- * pure entry: a server-reachable manifest reads it at module scope, and
121
- * importing it from the client-stamped `./catalog/react` bundle dragged React
122
- * into a server graph for the sake of five string pairs. `./catalog/react`
123
- * re-exports it unchanged, so no consumer has to move.
124
- *
125
- * (Written "client-stamped", never with the literal directive: this comment is
126
- * emitted into `dist/catalog/index.d.ts`, and the tarball canary rightly
127
- * refuses that directive anywhere in a PURE entry.)
128
- */
129
- declare const SORT_OPTIONS: {
130
- value: AgentSortOption;
131
- label: string;
132
- }[];
133
- /** Which ownership tab is active in the agent list. */
134
- type AgentTab = "mine" | "shared" | "all" | "system";
135
- /** Favorite filter. */
136
- type AgentFavFilter = "all" | "yes" | "no";
137
- /** Archive filter. */
138
- type AgentArchFilter = "active" | "archived" | "both";
139
- /**
140
- * Access level filter.
141
- * 'any' = no restriction (default).
142
- * 'owned' = only agents the user owns (isOwner = true).
143
- * 'shared' = only agents shared with the user (isOwner = false).
144
- * 'editable' = owner + admin + editor.
145
- */
146
- type AgentAccessFilter = "any" | "owned" | "shared" | "editable";
147
- /** Sentinel meaning "include uncategorized / untagged" items. */
148
- declare const AGENT_NONE_SENTINEL = "__none__";
149
- interface AgentConsumerState {
150
- tab: AgentTab;
151
- sortBy: AgentSortOption;
152
- searchTerm: string;
153
- /** INCLUSION model: empty = show all; non-empty = only matching. */
154
- includedCats: string[];
155
- /** INCLUSION model: empty = show all; non-empty = only matching. */
156
- includedTags: string[];
157
- favFilter: AgentFavFilter;
158
- archFilter: AgentArchFilter;
159
- accessFilter: AgentAccessFilter;
160
- favoritesFirst: boolean;
161
- /** Current page for owned-agent list items (after the card section). */
162
- listPage: number;
163
- /** Current page for shared-agent list items. */
164
- sharedPage: number;
165
- /**
166
- * Ids returned by the last server-side search (`agx_search`), in server rank
167
- * order. Additive: an agent in this list survives the search filter even
168
- * when the local scorer gives it 0.
169
- *
170
- * That is load-bearing for tier 2. A deep search matches an agent's prompt
171
- * content, which the client never loads — so the local scorer cannot see it.
172
- * Without this list the server would return prompt matches and the UI would
173
- * immediately filter them back out.
174
- *
175
- * Server-only matches sort BELOW every locally-scored match, in server rank
176
- * order, so obvious matches always come first.
177
- */
178
- serverMatchedIds: string[];
179
- /** True while a server search is in flight — drives the search spinner. */
180
- isServerSearching: boolean;
181
- /** Tier 2: also search agent prompt content. Opt-in, per consumer. */
182
- deepSearch: boolean;
183
- }
184
- declare const DEFAULT_AGENT_CONSUMER_STATE: AgentConsumerState;
185
- /** The subset of consumer state a caller may patch (paging is derived). */
186
- type AgentConsumerPatch = Partial<Omit<AgentConsumerState, "listPage" | "sharedPage">>;
187
- /**
188
- * Thrown by `createAgentCatalog` when a REQUIRED port is missing or an
189
- * optional feature was asked for without the port it needs. Never a warning,
190
- * never a degraded catalog: a picker that cannot read is not a picker.
191
- */
192
- declare class AgentCatalogConfigError extends Error {
193
- readonly name = "AgentCatalogConfigError";
194
- /** Machine code, for a host that routes config failures. */
195
- readonly code: string;
196
- /** What the host must do, in a sentence a person can act on. */
197
- readonly remedy: string;
198
- constructor(args: {
199
- code: string;
200
- message: string;
201
- remedy: string;
202
- });
203
- }
204
-
205
- /**
206
- * `@ai-matrx/agents/catalog` — THE PORTS.
207
- *
208
- * Two are required (`client`, `identity`); every other one ships a working
209
- * default with a DOCUMENTED degradation, per THE ALL-INCLUSIVE LAW. A host
210
- * injects identity and a Supabase client and nothing else; the picker's only
211
- * contract back to the host is `onSelect(agentId)`.
212
- *
213
- * React appears nowhere in this file — it is import-inert and legal in any
214
- * graph, server components included.
215
- */
216
-
217
- /** A PostgREST error, structurally. */
218
- interface PgErrorLike {
219
- message: string;
220
- code?: string;
221
- details?: string | null;
222
- hint?: string | null;
223
- }
224
- /** What every supabase-js call resolves to, structurally. */
225
- interface PgResultLike {
226
- data: unknown;
227
- error: PgErrorLike | null;
228
- count?: number | null;
229
- }
230
- /**
231
- * The thenable builder supabase-js returns from `.rpc()`. `range` is what
232
- * makes a COMPLETE list read possible (`readAllRows`); `order` is what makes
233
- * that paging stable. Both are the real builder's own methods — this is a
234
- * structural subset, never a re-implementation.
235
- */
236
- interface AgentCatalogRpcCall extends PromiseLike<PgResultLike> {
237
- range(from: number, to: number): PromiseLike<PgResultLike>;
238
- order(column: string, options?: {
239
- ascending?: boolean;
240
- }): AgentCatalogRpcCall;
241
- }
242
- /** The one write this package performs: `agent.definition.is_favorite`. */
243
- interface AgentCatalogTableQuery {
244
- update(patch: Record<string, unknown>): {
245
- eq(column: string, value: string): PromiseLike<{
246
- data?: unknown;
247
- error: PgErrorLike | null;
248
- }>;
249
- };
250
- }
251
- /**
252
- * REQUIRED. A structural subset of `SupabaseClient` — anything that can answer
253
- * `agx_get_list_full`, `agx_search`, `agx_resolve_agent_address` and write one
254
- * column on `agent.definition`. A real supabase-js client satisfies it as-is.
255
- *
256
- * Absent → `createAgentCatalog` throws `AgentCatalogConfigError`. There is no
257
- * degraded mode: the package IS the catalogue read.
258
- */
259
- interface AgentCatalogClient {
260
- rpc(fn: string, args?: Record<string, unknown>, options?: {
261
- count?: "exact" | "planned" | "estimated";
262
- }): AgentCatalogRpcCall;
263
- schema(name: string): {
264
- from(table: string): AgentCatalogTableQuery;
265
- };
266
- }
267
- /**
268
- * REQUIRED. Who is asking. Needed for the "zero owned agents → open on the
269
- * public tab" heuristic and for the favorite write.
270
- *
271
- * `requireUserId` THROWS when there is no session — never a silent anonymous
272
- * read. The catalog catches that throw in exactly ONE place (the tab-default
273
- * heuristic, where a signed-out visitor legitimately lands on the public
274
- * catalogue) and reports it to the `errorSink` the first time; every other
275
- * caller lets it propagate.
276
- */
277
- interface AgentCatalogIdentity {
278
- requireUserId(): string;
279
- }
280
- /**
281
- * Every degraded path, every failed read, every drift detection reports here.
282
- * DEFAULT: a tagged `console.error` that announces ONCE that no sink is bound
283
- * and names the remedy.
284
- */
285
- type AgentCatalogErrorSink = (event: {
286
- code: string;
287
- message: string;
288
- context?: object;
289
- }) => void;
290
- /**
291
- * The drift scream's second channel. DEFAULT: none — and that is safe
292
- * ONLY because the in-picker persistent banner is the primary scream and
293
- * always renders. A drift is never console-only.
294
- */
295
- type AgentCatalogNotifier = (event: {
296
- level: "warning" | "error";
297
- title: string;
298
- message: string;
299
- }) => void;
300
- /**
301
- * First-paint cache for resolved default rows. DEFAULT: `localStorage` when
302
- * present, else an in-memory map (so SSR and a Chrome service worker both
303
- * work). Absent storage degrades to "no first paint of the default row until
304
- * the mandate resolves" — never a wrong name on screen.
305
- */
306
- interface AgentCatalogStorage {
307
- getItem(key: string): string | null;
308
- setItem(key: string, value: string): void;
309
- removeItem(key: string): void;
310
- }
311
- /** Every user-visible string the picker renders, overridable per host. */
312
- interface AgentCatalogLabels {
313
- /** User-facing label for the `system` ownership tab. */
314
- publicTab: string;
315
- /**
316
- * Badge label on a builtin row. Default `"system"` — the string
317
- * `AgentRow` actually rendered in matrx-frontend. (The frontend also
318
- * declared `AGENT_PUBLIC_BADGE_LABEL = "Public"` for this badge and NOTHING
319
- * ever read it — a constant that named the badge without being the badge.
320
- * Recorded in the CHANGELOG and dropped rather than ported.)
321
- */
322
- systemBadge: string;
323
- searchPlaceholder: string;
324
- currentAgentHeading: string;
325
- emptyDefault: string;
326
- emptyShared: string;
327
- emptySystem: string;
328
- loading: string;
329
- triggerFallback: string;
330
- triggerResolving: string;
331
- triggerUnnameable: string;
332
- hoverHint: string;
333
- }
334
- declare const DEFAULT_AGENT_CATALOG_LABELS: AgentCatalogLabels;
335
- interface AgentCatalogConfig {
336
- /** REQUIRED — the Supabase client. */
337
- client: AgentCatalogClient;
338
- /** REQUIRED — who is asking. */
339
- identity: AgentCatalogIdentity;
340
- /**
341
- * The catalog's registry key on `globalThis`. Two `createAgentCatalog`
342
- * calls with the same id are the same catalog (and the second one screams
343
- * through the errorSink before replacing the first). Default `"default"`.
344
- */
345
- catalogId?: string;
346
- /** OPTIONAL — see `AgentCatalogErrorSink`. */
347
- errorSink?: AgentCatalogErrorSink;
348
- /** OPTIONAL — see `AgentCatalogNotifier`. */
349
- notifier?: AgentCatalogNotifier;
350
- /** OPTIONAL — see `AgentCatalogStorage`. */
351
- storage?: AgentCatalogStorage;
352
- /**
353
- * OPTIONAL — the package's own `MatrxTransport` (`createMatrxTransport`
354
- * from `@ai-matrx/agents/matrx`). REQUIRED the moment any picker instance
355
- * passes a `defaultMandateKey`: THE PLATFORM RULE D-R1 says clients never
356
- * walk the mandate ladder, so the default row can only be resolved through
357
- * the aidream door `GET /mandates/{mandate_key}/resolution`. A
358
- * `defaultMandateKey` with no transport throws `AgentCatalogConfigError`.
359
- */
360
- transport?: AgentCatalogTransport;
361
- /** OPTIONAL — label overrides. */
362
- labels?: Partial<AgentCatalogLabels>;
363
- }
364
- /**
365
- * The transport seam, structurally identical to `MatrxTransport` from
366
- * `@ai-matrx/agents/matrx` (declared structurally so `./catalog` never
367
- * imports the transport module and stays import-inert).
368
- */
369
- interface AgentCatalogTransport {
370
- fetch(path: string, init: {
371
- method: "GET" | "POST";
372
- headers: Record<string, string>;
373
- body?: string;
374
- signal?: AbortSignal;
375
- }): Promise<Response>;
376
- }
377
- /**
378
- * Where a row's href goes when someone cmd-clicks it, and where `navigateTo`
379
- * lands. DEFAULT: `window.location.assign` (so cmd-click and ordinary
380
- * navigation both work with no host wiring at all).
381
- */
382
- type AgentCatalogNavigate = (href: string) => void;
383
- /** Resolve a per-row href. DEFAULT: `/agents/go/<id>` — the always-valid door. */
384
- type AgentCatalogResolveHref = (agent: AgentSummary) => string;
385
- /**
386
- * The sneak-peek affordance. Absent → the affordance is HIDDEN, never a dead
387
- * button (nothing fails silently, and nothing looks alive when it is not).
388
- */
389
- type AgentCatalogOpenPeek = (agent: AgentSummary) => void;
390
-
391
- /**
392
- * catalog/default-row-types.ts — @ai-matrx/agents/catalog
393
- *
394
- * Split out of `default-row.ts` so `global-state.ts` can name the cached
395
- * shape without importing the resolver (and its transport work) — the same
396
- * split-out reflex C8 asks for, at module scale.
397
- */
398
-
399
- /** The wire shape of `GET /mandates/{mandate_key}/resolution`, narrowed. */
400
- interface MandateResolutionVerdict {
401
- mandateKey: string;
402
- holderType: string;
403
- /** The executable id. `null` for a workflow Holder — a picker cannot run one. */
404
- agentId: string | null;
405
- isVersion: boolean;
406
- /** Always the `agent.definition` id, even when `agentId` is a version. */
407
- definitionAgentId: string | null;
408
- /** Which precedence layer answered. Shown so a person can see WHY. */
409
- provenance: string;
410
- }
411
- /** A resolved default row, cached for first paint and compared for drift. */
412
- interface ResolvedDefaultRow {
413
- mandateKey: string;
414
- /** The `agent.definition` id the mandate resolved to. */
415
- holderId: string;
416
- /** The holder's REAL name, read from the catalogue — never hardcoded. */
417
- holderName: string;
418
- holderDescription: string | null;
419
- provenance: string;
420
- /** Epoch ms of this resolution. */
421
- at: number;
422
- }
423
- /** What the picker renders and what it screams, together. */
424
- interface DefaultRowState {
425
- mandateKey: string;
426
- /** The row to render at the top of the list. `null` until resolved. */
427
- row: AgentSummary | null;
428
- /** The last live resolution, or the cached one before the live answer lands. */
429
- resolved: ResolvedDefaultRow | null;
430
- /** True while the resolution is in flight. */
431
- loading: boolean;
432
- /**
433
- * 🚨 THE DRIFT SCREAM. Set when the cached first-paint row and the LIVE
434
- * resolution disagree on holder id or holder name. Rendered as a persistent
435
- * in-picker banner AND sent to the `notifier` port — never console-only.
436
- */
437
- drift: string | null;
438
- /** Set when the mandate could not be resolved at all. Loud, with a remedy. */
439
- error: string | null;
440
- }
441
-
442
- /**
443
- * catalog/schema.ts — @ai-matrx/agents/catalog
444
- *
445
- * THE DEMANDED SCHEMA PROBE (the `@ai-matrx/associations` `assertDemandedSchema`
446
- * precedent). It proves that the database a host pointed this catalog at can
447
- * actually answer the three functions the picker demands.
448
- *
449
- * 🚨 IT MUST BE ABLE TO FAIL. A probe whose green result is unconditional
450
- * proves nothing, so `selfTest: true` also probes a fabricated function name
451
- * and REQUIRES it to come back missing. `catalog/__tests__/schema.test.ts`
452
- * runs the whole probe against a client whose `rpc` answers PostgreSQL 42883
453
- * (`undefined_function`, which PostgREST surfaces as PGRST202) and asserts the
454
- * loud error.
455
- */
456
-
457
- interface AgentCatalogSchemaReport {
458
- ok: boolean;
459
- /** Demanded functions the database cannot answer. */
460
- missing: string[];
461
- /** Functions the probe could not reach (transport/unknown failure). */
462
- unreachable: {
463
- fn: string;
464
- error: unknown;
465
- }[];
466
- /** Functions that answered (success or any non-missing refusal). */
467
- answered: string[];
468
- /** True when `agent.definition.is_favorite` is writable by this caller. */
469
- favoriteWritable: boolean | null;
470
- /**
471
- * Whether `agx_get_list_full` carries the `orchestra` badge column (added to
472
- * the platform 2026-09-08).
473
- *
474
- * NOT a violation when absent — the picker degrades to no conductor marker,
475
- * which is honest — but it is never SILENT either: a `false` here is the one
476
- * signal that tells a host "this database predates the badge" instead of
477
- * leaving someone to wonder why their Orchestra looks like a plain agent.
478
- * `null` means the probe could not tell (the caller can see no agents at
479
- * all), never "fine".
480
- */
481
- orchestraColumn: boolean | null;
482
- }
483
- interface AssertAgentCatalogSchemaOptions {
484
- /**
485
- * Also probe a fabricated function name and REQUIRE it to come back
486
- * missing — proves this probe can fail.
487
- */
488
- selfTest?: boolean;
489
- /** Return the report instead of throwing. The self-test failure ALWAYS throws. */
490
- throwOnViolation?: boolean;
491
- /**
492
- * Also probe the ONE write (`agent.definition.is_favorite`) against an
493
- * impossible id, so a 42501/permission refusal is seen at boot rather than
494
- * on a user's first click. Default: on.
495
- */
496
- probeWrite?: boolean;
497
- }
498
- /**
499
- * Probe every demanded function. Throws an `Error` naming each missing one
500
- * (unless `throwOnViolation: false`), and ALWAYS throws when the probe itself
501
- * cannot be trusted (unreachable database, failed self-test).
502
- */
503
- declare function assertAgentCatalogSchema(client: AgentCatalogClient, options?: AssertAgentCatalogSchemaOptions): Promise<AgentCatalogSchemaReport>;
504
-
505
- /**
506
- * catalog/store.ts — @ai-matrx/agents/catalog
507
- *
508
- * `createAgentCatalog(config)` — the headless kernel. Everything the picker
509
- * knows lives here: the row registry, the complete-list read with its
510
- * session-shared in-flight dedupe and freshness window, the tier-2 server
511
- * search, the per-consumer view state, the ONE write, and the mandate default
512
- * row with its drift screamer.
513
- *
514
- * The host injects a Supabase client and who the user is. Nothing else.
515
- *
516
- * The consumer reducers below are a VERBATIM port of matrx-frontend
517
- * `features/agents/redux/agent-consumers/slice.ts`; only the seam inverts
518
- * (a Redux reducer becomes a store method, and Immer's draft mutation becomes
519
- * an explicit copy so the store can publish a new reference).
520
- */
521
-
522
- type AgentCatalogStatus = "idle" | "loading" | "succeeded" | "failed";
523
- interface AgentCatalogState {
524
- /** Every row, one stable array reference per change (memoization depends on it). */
525
- rows: AgentSummary[];
526
- byId: Record<string, AgentSummary>;
527
- status: AgentCatalogStatus;
528
- error: string | null;
529
- consumers: Record<string, AgentConsumerState>;
530
- /** Keyed by mandate key. */
531
- defaultRows: Record<string, DefaultRowState>;
532
- }
533
- interface AgentCatalog {
534
- readonly catalogId: string;
535
- readonly labels: AgentCatalogLabels;
536
- readonly errorSink: AgentCatalogErrorSink;
537
- readonly notifier: AgentCatalogNotifier | undefined;
538
- /** True when this catalog was given a transport and can resolve mandates. */
539
- readonly canResolveMandates: boolean;
540
- getState(): AgentCatalogState;
541
- subscribe(listener: () => void): () => void;
542
- getAgent(agentId: string): AgentSummary | undefined;
543
- /**
544
- * The signed-in user id, or `null` when there is no session. The ONE place
545
- * `identity.requireUserId()`'s throw is caught — a signed-out visitor
546
- * legitimately lands on the public catalogue — and it is reported to the
547
- * errorSink the first time so it is never silent.
548
- */
549
- getUserIdOrNull(): string | null;
550
- /** Load the catalogue if it is not fresh. Safe to call on every mount. */
551
- ensureLoaded(options?: {
552
- force?: boolean;
553
- }): Promise<void>;
554
- /** True when the last load is inside the 15-minute TTL. */
555
- isFresh(): boolean;
556
- /** True when the last load is older than 4 hours (tab-restore threshold). */
557
- isStale(): boolean;
558
- getConsumer(consumerId: string): AgentConsumerState;
559
- registerConsumer(consumerId: string, initial?: AgentConsumerPatch): void;
560
- unregisterConsumer(consumerId: string): void;
561
- setConsumerFilter(consumerId: string, patch: AgentConsumerPatch): void;
562
- setConsumerPage(consumerId: string, which: "list" | "shared", page: number): void;
563
- setConsumerServerSearch(consumerId: string, args: {
564
- matchedIds?: string[];
565
- isSearching?: boolean;
566
- }): void;
567
- resetConsumerFilters(consumerId: string): void;
568
- /** Tier-2 search. Purely additive; merges rows into the registry. */
569
- searchServer(query: string, deep?: boolean): Promise<string[]>;
570
- /** THE ONE WRITE. Optimistic, rolled back on failure, loud either way. */
571
- setFavorite(agentId: string, isFavorite: boolean): Promise<void>;
572
- /** Resolve (once) the default row for a mandate key. Idempotent. */
573
- ensureDefaultRow(mandateKey: string): void;
574
- getDefaultRow(mandateKey: string): DefaultRowState | undefined;
575
- /** A person acknowledged the drift banner. The notifier already fired. */
576
- dismissDefaultRowDrift(mandateKey: string): void;
577
- assertSchema(options?: AssertAgentCatalogSchemaOptions): Promise<AgentCatalogSchemaReport>;
578
- }
579
- declare function createAgentCatalog(config: AgentCatalogConfig): AgentCatalog;
580
- /**
581
- * The catalog registered under `catalogId`, across loader graphs. Returns
582
- * `undefined` when none has been created yet — never a silent stub.
583
- */
584
- declare function getRegisteredAgentCatalog(catalogId?: string): AgentCatalog | undefined;
1
+ import { A as AgentSummary, a as AgentCatalogClient, b as AgentCatalogTransport, c as AgentCatalogStorageLike, d as AgentCatalogErrorSink, R as ResolvedDefaultRow, e as AgentCatalogStorage, M as MandateResolutionVerdict, f as AgentTab, g as AgentCatalogLabels, h as AgentConsumerState, i as AgentArchFilter, j as AgentSortOption } from '../orchestra-CID-rsu8.cjs';
2
+ export { k as AGENT_NONE_SENTINEL, l as AgentAccessFilter, m as AgentAccessLevel, n as AgentCatalog, o as AgentCatalogAgentType, p as AgentCatalogAsyncStorage, q as AgentCatalogConfig, r as AgentCatalogConfigError, s as AgentCatalogIdentity, t as AgentCatalogNavigate, u as AgentCatalogNotifier, v as AgentCatalogOpenPeek, w as AgentCatalogResolveHref, x as AgentCatalogRpcCall, y as AgentCatalogSchemaReport, z as AgentCatalogState, B as AgentCatalogStatus, C as AgentCatalogTableQuery, D as AgentConsumerPatch, E as AgentFavFilter, F as AgentId, G as AssertAgentCatalogSchemaOptions, H as DEFAULT_AGENT_CATALOG_LABELS, I as DEFAULT_AGENT_CONSUMER_STATE, J as DefaultRowState, O as OrchestraBadge, P as PgErrorLike, K as PgResultLike, S as SORT_OPTIONS, L as asAgentId, N as assertAgentCatalogSchema, Q as createAgentCatalog, T as getRegisteredAgentCatalog, U as isMandateAgentId, V as mandateAgentId, W as orchestraDelegatesLine, X as orchestraDepthLine, Y as orchestraLabel, Z as orchestraModeLabel } from '../orchestra-CID-rsu8.cjs';
585
3
 
586
4
  /**
587
5
  * catalog/data.ts — @ai-matrx/agents/catalog
@@ -680,35 +98,6 @@ declare function writeAgentFavorite(client: AgentCatalogClient, agentId: string,
680
98
  */
681
99
  declare function toAgentSummary(raw: unknown): AgentSummary | null;
682
100
 
683
- /**
684
- * catalog/orchestra.ts — @ai-matrx/agents/catalog
685
- *
686
- * Creator words for an Orchestra badge. A VERBATIM port of the helpers the
687
- * Workflow Studio's hand-rolled picker carried (`apps/workflow-studio/src/
688
- * hooks/use-agents.ts` before `c0f958b43`), lifted here so every client says
689
- * the same sentence about the same ensemble.
690
- *
691
- * Pure — no React, no DOM. The row shape is `OrchestraBadge` in `./types`.
692
- */
693
-
694
- declare function orchestraModeLabel(mode: string): string;
695
- /** Human label for a badge: "Orchestra · supervisor · 3 members" (singular-safe). */
696
- declare function orchestraLabel(badge: OrchestraBadge): string;
697
- /**
698
- * The member-call ceiling in Creator language ("Team members can call helpers
699
- * up to 2 levels deep"), or `null` when the composition declared none. A badge
700
- * never invents the platform default: the server that runs the ensemble owns
701
- * that number, and printing a promise nobody made is worse than printing
702
- * nothing.
703
- */
704
- declare function orchestraDepthLine(badge: OrchestraBadge): string | null;
705
- /**
706
- * One visible line naming who the Orchestra delegates to, collapsing past
707
- * `limit` into "+N more". `null` when no member carries a real role title —
708
- * "Member, Member, Member" is noise that reads like data.
709
- */
710
- declare function orchestraDelegatesLine(badge: OrchestraBadge, limit?: number): string | null;
711
-
712
101
  /**
713
102
  * catalog/default-row.ts — @ai-matrx/agents/catalog
714
103
  *
@@ -765,6 +154,13 @@ declare function createMemoryStorage(): AgentCatalogStorage;
765
154
  declare function defaultStorage(): AgentCatalogStorage;
766
155
  declare function readCachedDefaultRow(storage: AgentCatalogStorage, mandateKey: string): ResolvedDefaultRow | null;
767
156
  declare function writeCachedDefaultRow(storage: AgentCatalogStorage, resolved: ResolvedDefaultRow): void;
157
+ /**
158
+ * Read the cached row from EITHER storage shape. A store that throws or answers
159
+ * with a rejected promise costs a first paint, never correctness.
160
+ */
161
+ declare function readCachedDefaultRowAsync(storage: AgentCatalogStorageLike, mandateKey: string): Promise<ResolvedDefaultRow | null>;
162
+ /** Write the cached row to EITHER storage shape. */
163
+ declare function writeCachedDefaultRowAsync(storage: AgentCatalogStorageLike, resolved: ResolvedDefaultRow): Promise<void>;
768
164
  /** Build the `mandate:<key>` row from a resolved Holder. Never hardcodes text. */
769
165
  declare function defaultRowFromResolution(resolved: ResolvedDefaultRow): AgentSummary;
770
166
  /**
@@ -776,7 +172,7 @@ interface ResolveDefaultRowArgs {
776
172
  mandateKey: string;
777
173
  transport: AgentCatalogTransport;
778
174
  client: AgentCatalogClient;
779
- storage: AgentCatalogStorage;
175
+ storage: AgentCatalogStorageLike;
780
176
  errorSink: AgentCatalogErrorSink;
781
177
  /** Look the Holder up in the rows this catalog already holds. */
782
178
  lookupRow: (agentId: string) => AgentSummary | undefined;
@@ -861,7 +257,18 @@ declare function agentMatchesSearch(agent: AgentSearchable, query: string): bool
861
257
  * frontend's `selectLiveAgents` filters `isVersion` snapshots out of a store
862
258
  * that also holds them. This catalog only ever holds list rows — a version
863
259
  * snapshot cannot enter it — so that filter is a no-op here and is not
864
- * ported. Every ordering, tie-break and count below is byte-identical.
260
+ * ported.
261
+ *
262
+ * 🚨 TWO DELIBERATE DEPARTURES FROM THE ORIGINAL, both defects the port
263
+ * inherited and 0.9.1 fixes at the class (see CHANGELOG 0.9.1):
264
+ *
265
+ * F1 — every sort now ends on `id` ASC, the same final key `agx_get_list`
266
+ * ends on, so the order of TIED rows is defined by the rows instead of
267
+ * by whichever transport the host used to fetch them.
268
+ * F5 — the tab-badge counts honour the archive filter, so the badge, the
269
+ * footer and the rendered rows are the same number.
270
+ *
271
+ * Everything else below is byte-identical to the original.
865
272
  */
866
273
 
867
274
  declare const AGENT_CARDS_LIMIT_DESKTOP = 8;
@@ -877,6 +284,31 @@ declare const AGENT_LIST_ITEMS_PER_PAGE = 20;
877
284
  declare function memoize1<A, R>(fn: (a: A) => R): (a: A) => R;
878
285
  declare function memoize2<A, B, R>(fn: (a: A, b: B) => R): (a: A, b: B) => R;
879
286
  declare function memoize3<A, B, C, R>(fn: (a: A, b: B, c: C) => R): (a: A, b: B, c: C) => R;
287
+ /**
288
+ * 🚨 THE FINAL TIEBREAKER. `agx_get_list` orders
289
+ * `is_favorite DESC, updated_at DESC, id` — `id` ASCENDING is the unique key
290
+ * that makes the SERVER's order TOTAL. Every sort in this file ends on the
291
+ * same key, so the rendered order is a function of the ROWS and never of the
292
+ * order they happened to arrive in.
293
+ *
294
+ * Why a raw `<` / `>` and not `localeCompare`: Postgres orders `uuid` by its
295
+ * 16 raw bytes. The canonical text form is lowercase hex with dashes at fixed
296
+ * positions, so a codepoint comparison of those strings reproduces the
297
+ * database's order exactly. A locale collator does not — it is free to fold
298
+ * case or ignore punctuation, and would silently diverge from the server on
299
+ * precisely the ties this key exists to settle.
300
+ */
301
+ declare function compareAgentIds(a: AgentSummary, b: AgentSummary): number;
302
+ /**
303
+ * The chosen sort key, then `id`. TOTAL for every option.
304
+ *
305
+ * Before 0.9.1 each branch below returned 0 for tied rows, which left their
306
+ * relative order to whatever `Array.prototype.sort` had been handed — i.e. to
307
+ * the transport. A host that fetched the RPC in one shot kept the server's
308
+ * favourites-first/`updated_at DESC` order for ties; a host that paged it with
309
+ * `order=id.asc` kept id-ASC. Same rows, same filters, two different screens.
310
+ * The key below is the server's own, so both now render the server's answer.
311
+ */
880
312
  declare function applyAgentSortComparator(a: AgentSummary, b: AgentSummary, sortBy: AgentSortOption): number;
881
313
  /** All unique categories across live USER agents, sorted alphabetically. */
882
314
  declare const selectAllAgentCategories: (a: readonly AgentSummary[]) => string[];
@@ -894,6 +326,15 @@ declare const selectAllSystemAgentTags: (a: readonly AgentSummary[]) => string[]
894
326
  declare const selectUserTypeAgents: (a: readonly AgentSummary[]) => AgentSummary[];
895
327
  /** Builtin/system agents only. For chat pickers and full catalogues. */
896
328
  declare const selectBuiltinTypeAgents: (a: readonly AgentSummary[]) => AgentSummary[];
329
+ /**
330
+ * The ONE ordering rule, and it is TOTAL on every path.
331
+ *
332
+ * searching: score DESC → server rank ASC → sort key → id ASC
333
+ * not searching: favourites first (when asked) → sort key → id ASC
334
+ *
335
+ * `id` ASC is the final key `agx_get_list` itself ends on, so two hosts that
336
+ * fetched the same rows through different transports render the same screen.
337
+ */
897
338
  declare function sortFilteredAgents(filtered: AgentSummary[], consumer: AgentConsumerState): AgentSummary[];
898
339
  /** User-type agents — mine / shared / all tabs with full consumer filters. */
899
340
  declare function filterUserTypeAgents(agents: readonly AgentSummary[], consumer: AgentConsumerState): AgentSummary[];
@@ -943,6 +384,35 @@ declare const makeSelectFilteredAgentsCount: () => (a: readonly AgentSummary[],
943
384
  declare const makeSelectFilteredBuiltinAgentsCount: () => (a: readonly AgentSummary[], b: AgentConsumerState) => number;
944
385
  /** Returns whether a consumer has any non-default filters active. */
945
386
  declare function agentConsumerHasActiveFilters(consumer: AgentConsumerState, defaults: AgentConsumerState): boolean;
387
+ /**
388
+ * 🚨 A BADGE COUNTS WHAT THE TAB RENDERS. Nothing else.
389
+ *
390
+ * Before 0.9.1 these five counted every user-type row, archived included,
391
+ * while `filterUserTypeAgents` (archive filter `active`, the default) hid the
392
+ * archived ones. The badge said "Mine 416", the list showed 412 rows and the
393
+ * footer said "412 agents" — one screen, three numbers, one of them a lie
394
+ * about agents the user cannot see or reach from there.
395
+ *
396
+ * `is_archived` is now honoured by the same rule the list uses, so the badge,
397
+ * the footer and the rows agree on every tab and under every archive filter.
398
+ * The system tab is the exception with a reason: `filterBuiltinTypeAgents`
399
+ * applies no archive filter (a builtin belongs to the platform and is never
400
+ * the user's to archive), so its count applies none either — it already
401
+ * counted exactly what it rendered.
402
+ */
403
+ declare function agentMatchesArchiveFilter(agent: AgentSummary, archFilter: AgentArchFilter): boolean;
404
+ /**
405
+ * The four tab badges for a consumer's CURRENT archive filter — the numbers
406
+ * `useAgentListCore` puts on the tab strip. Flip the archive filter to
407
+ * "archived" and the badges follow the list into the archive.
408
+ */
409
+ declare const selectAgentTabCounts: (a: readonly AgentSummary[], b: AgentArchFilter) => {
410
+ mine: number;
411
+ shared: number;
412
+ all: number;
413
+ system: number;
414
+ };
415
+ /** Live user-type agents the default (`active`) list renders. */
946
416
  declare const selectTotalUserAgentsCount: (a: readonly AgentSummary[]) => number;
947
417
  declare const selectTotalOwnedAgentsCount: (a: readonly AgentSummary[]) => number;
948
418
  declare const selectTotalSharedAgentsCount: (a: readonly AgentSummary[]) => number;
@@ -965,4 +435,4 @@ declare const selectTotalFavoriteAgentsCount: (a: readonly AgentSummary[]) => nu
965
435
  /** @internal Test-only: forget every session-shared value. */
966
436
  declare function _resetCatalogGlobalState(): void;
967
437
 
968
- export { AGENT_CARDS_LIMIT_DESKTOP, AGENT_CARDS_LIMIT_MOBILE, AGENT_LIST_ITEMS_PER_PAGE, AGENT_NONE_SENTINEL, AGENT_SEARCH_LIMIT, type AgentAccessFilter, type AgentAccessLevel, type AgentArchFilter, type AgentCatalog, type AgentCatalogAgentType, type AgentCatalogClient, type AgentCatalogConfig, AgentCatalogConfigError, type AgentCatalogErrorSink, type AgentCatalogIdentity, type AgentCatalogLabels, type AgentCatalogNavigate, type AgentCatalogNotifier, type AgentCatalogOpenPeek, AgentCatalogReadError, type AgentCatalogResolveHref, type AgentCatalogRpcCall, type AgentCatalogSchemaReport, type AgentCatalogState, type AgentCatalogStatus, type AgentCatalogStorage, type AgentCatalogTableQuery, type AgentCatalogTransport, type AgentConsumerPatch, type AgentConsumerState, type AgentFavFilter, type AgentId, type AgentListPage, type AgentSearchable, type AgentServerSearchResult, type AgentSortOption, type AgentSummary, type AgentTab, type AssertAgentCatalogSchemaOptions, DEFAULT_AGENT_CATALOG_LABELS, DEFAULT_AGENT_CONSUMER_STATE, type DefaultRowState, MandateDefaultRowError, type MandateResolutionVerdict, type OrchestraBadge, type PgErrorLike, type PgResultLike, type ResolveDefaultRowArgs, type ResolveDefaultRowOutcome, type ResolvedDefaultRow, SORT_OPTIONS, _resetCatalogGlobalState, agentConsumerHasActiveFilters, agentListEmptyLabel, agentMatchesSearch, applyAgentSortComparator, asAgentId, assertAgentCatalogSchema, computeAgentSearchScore, createAgentCatalog, createMemoryStorage, defaultRowFromResolution, defaultStorage, detectDefaultRowDrift, filterBuiltinTypeAgents, filterUserTypeAgents, getRegisteredAgentCatalog, isMandateAgentId, makeSelectFilteredAgents, makeSelectFilteredAgentsCount, makeSelectFilteredBuiltinAgents, makeSelectFilteredBuiltinAgentsCount, makeSelectFilteredOwnedAgents, makeSelectFilteredSharedAgents, makeSelectOwnedAgentCards, makeSelectOwnedAgentListItems, makeSelectSharedAgentCards, makeSelectSharedAgentListItems, mandateAgentId, memoize1, memoize2, memoize3, orchestraDelegatesLine, orchestraDepthLine, orchestraLabel, orchestraModeLabel, parseMandateResolution, readAgentCatalogRows, readCachedDefaultRow, readSingleAgentRow, resolveDefaultRow, searchAgentsOnServer, selectAllAgentCategories, selectAllAgentTags, selectAllSystemAgentCategories, selectAllSystemAgentTags, selectBuiltinTypeAgents, selectTotalBuiltinAgentsCount, selectTotalFavoriteAgentsCount, selectTotalOwnedAgentsCount, selectTotalSharedAgentsCount, selectTotalUserAgentsCount, selectUserTypeAgents, shouldDefaultAgentListToPublicTab, sortFilteredAgents, toAgentSummary, writeAgentFavorite, writeCachedDefaultRow };
438
+ export { AGENT_CARDS_LIMIT_DESKTOP, AGENT_CARDS_LIMIT_MOBILE, AGENT_LIST_ITEMS_PER_PAGE, AGENT_SEARCH_LIMIT, AgentArchFilter, AgentCatalogClient, AgentCatalogErrorSink, AgentCatalogLabels, AgentCatalogReadError, AgentCatalogStorage, AgentCatalogStorageLike, AgentCatalogTransport, AgentConsumerState, type AgentListPage, type AgentSearchable, type AgentServerSearchResult, AgentSortOption, AgentSummary, AgentTab, MandateDefaultRowError, MandateResolutionVerdict, type ResolveDefaultRowArgs, type ResolveDefaultRowOutcome, ResolvedDefaultRow, _resetCatalogGlobalState, agentConsumerHasActiveFilters, agentListEmptyLabel, agentMatchesArchiveFilter, agentMatchesSearch, applyAgentSortComparator, compareAgentIds, computeAgentSearchScore, createMemoryStorage, defaultRowFromResolution, defaultStorage, detectDefaultRowDrift, filterBuiltinTypeAgents, filterUserTypeAgents, makeSelectFilteredAgents, makeSelectFilteredAgentsCount, makeSelectFilteredBuiltinAgents, makeSelectFilteredBuiltinAgentsCount, makeSelectFilteredOwnedAgents, makeSelectFilteredSharedAgents, makeSelectOwnedAgentCards, makeSelectOwnedAgentListItems, makeSelectSharedAgentCards, makeSelectSharedAgentListItems, memoize1, memoize2, memoize3, parseMandateResolution, readAgentCatalogRows, readCachedDefaultRow, readCachedDefaultRowAsync, readSingleAgentRow, resolveDefaultRow, searchAgentsOnServer, selectAgentTabCounts, selectAllAgentCategories, selectAllAgentTags, selectAllSystemAgentCategories, selectAllSystemAgentTags, selectBuiltinTypeAgents, selectTotalBuiltinAgentsCount, selectTotalFavoriteAgentsCount, selectTotalOwnedAgentsCount, selectTotalSharedAgentsCount, selectTotalUserAgentsCount, selectUserTypeAgents, shouldDefaultAgentListToPublicTab, sortFilteredAgents, toAgentSummary, writeAgentFavorite, writeCachedDefaultRow, writeCachedDefaultRowAsync };