@ai-matrx/agents 0.59.264 → 0.59.265
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 +50 -0
- package/dist/catalog/index.cjs +233 -5
- package/dist/catalog/index.d.cts +164 -83
- package/dist/catalog/index.d.ts +164 -83
- package/dist/catalog/index.js +233 -5
- package/dist/catalog/native/index.cjs +123 -24
- package/dist/catalog/native/index.d.cts +150 -42
- package/dist/catalog/native/index.d.ts +150 -42
- package/dist/catalog/native/index.js +120 -21
- package/dist/catalog/react/index.cjs +143 -20
- package/dist/catalog/react/index.d.cts +170 -44
- package/dist/catalog/react/index.d.ts +170 -44
- package/dist/catalog/react/index.js +143 -20
- package/dist/generated/provision-offers.cjs +0 -1
- package/dist/generated/provision-offers.d.cts +53 -23
- package/dist/generated/provision-offers.d.ts +53 -23
- package/dist/generated/provision-offers.js +0 -1
- package/dist/models/index.cjs +48 -1
- package/dist/models/index.d.cts +6 -0
- package/dist/models/index.d.ts +6 -0
- package/dist/models/index.js +48 -1
- package/dist/models/react/index.d.cts +6 -0
- package/dist/models/react/index.d.ts +6 -0
- package/dist/quick/index.d.cts +1 -1
- package/dist/quick/index.d.ts +1 -1
- package/dist/{types-DZhDqrVE.d.cts → types-5pX8AZBM.d.cts} +10 -0
- package/dist/{types-DZhDqrVE.d.ts → types-5pX8AZBM.d.ts} +10 -0
- package/package.json +2 -28
|
@@ -125,6 +125,16 @@ interface AgentSummary {
|
|
|
125
125
|
* host can tell "the default" from a stored agent without parsing the id.
|
|
126
126
|
*/
|
|
127
127
|
mandateKey?: string;
|
|
128
|
+
/**
|
|
129
|
+
* THE THIN ROW (FW-014). `true` when this row came from the picker's page
|
|
130
|
+
* read (`agent.agx_picker_page`), which carries only what a picker row
|
|
131
|
+
* renders: no description, model, offering, task, source or sharer. A
|
|
132
|
+
* consumer that needs those asks `catalog.ensureAgentRows([id])` (hover,
|
|
133
|
+
* open), which replaces it with the full row. Absent = a full row.
|
|
134
|
+
*/
|
|
135
|
+
isThin?: boolean;
|
|
136
|
+
/** When the caller last used this agent (their latest conversation with it). Page reads only. */
|
|
137
|
+
lastUsedAt?: string | null;
|
|
128
138
|
}
|
|
129
139
|
type AgentSortOption = "updated-desc" | "created-desc" | "name-asc" | "name-desc" | "category-asc";
|
|
130
140
|
/**
|
|
@@ -212,45 +222,6 @@ interface AgentConsumerState {
|
|
|
212
222
|
/** The subset of consumer state a caller may patch (paging is derived). */
|
|
213
223
|
type AgentConsumerPatch = Partial<Omit<AgentConsumerState, "listPage" | "sharedPage">>;
|
|
214
224
|
|
|
215
|
-
/**
|
|
216
|
-
* catalog/default-row-types.ts — @ai-matrx/agents/catalog
|
|
217
|
-
*
|
|
218
|
-
* Split out of `default-row.ts` so `global-state.ts` can name the cached
|
|
219
|
-
* shape without importing the resolver (and its transport work) — the same
|
|
220
|
-
* split-out reflex C8 asks for, at module scale.
|
|
221
|
-
*/
|
|
222
|
-
|
|
223
|
-
/** A resolved default row, cached for first paint and compared for drift. */
|
|
224
|
-
interface ResolvedDefaultRow {
|
|
225
|
-
mandateKey: string;
|
|
226
|
-
/** The `agent.definition` id the mandate resolved to. */
|
|
227
|
-
holderId: string;
|
|
228
|
-
/** The holder's REAL name, read from the catalog — never hardcoded. */
|
|
229
|
-
holderName: string;
|
|
230
|
-
holderDescription: string | null;
|
|
231
|
-
provenance: string;
|
|
232
|
-
/** Epoch ms of this resolution. */
|
|
233
|
-
at: number;
|
|
234
|
-
}
|
|
235
|
-
/** What the picker renders and what it screams, together. */
|
|
236
|
-
interface DefaultRowState {
|
|
237
|
-
mandateKey: string;
|
|
238
|
-
/** The row to render at the top of the list. `null` until resolved. */
|
|
239
|
-
row: AgentSummary | null;
|
|
240
|
-
/** The last live resolution, or the cached one before the live answer lands. */
|
|
241
|
-
resolved: ResolvedDefaultRow | null;
|
|
242
|
-
/** True while the resolution is in flight. */
|
|
243
|
-
loading: boolean;
|
|
244
|
-
/**
|
|
245
|
-
* 🚨 THE DRIFT SCREAM. Set when the cached first-paint row and the LIVE
|
|
246
|
-
* resolution disagree on holder id or holder name. Rendered as a persistent
|
|
247
|
-
* in-picker banner AND sent to the `notifier` port — never console-only.
|
|
248
|
-
*/
|
|
249
|
-
drift: string | null;
|
|
250
|
-
/** Set when the mandate could not be resolved at all. Loud, with a remedy. */
|
|
251
|
-
error: string | null;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
225
|
/**
|
|
255
226
|
* `@ai-matrx/agents/catalog` — THE PORTS.
|
|
256
227
|
*
|
|
@@ -326,6 +297,101 @@ type AgentCatalogNavigate = (href: string) => void;
|
|
|
326
297
|
*/
|
|
327
298
|
type AgentCatalogOpenPeek = (agent: AgentSummary) => void;
|
|
328
299
|
|
|
300
|
+
/**
|
|
301
|
+
* catalog/data.ts — @ai-matrx/agents/catalog
|
|
302
|
+
*
|
|
303
|
+
* Every database call the catalog makes, and nothing else. Five of them:
|
|
304
|
+
*
|
|
305
|
+
* 1. `agx_get_list_full()` — the complete catalog (owned + directly-shared
|
|
306
|
+
* + org-shared user agents, unioned with active builtins).
|
|
307
|
+
* 2. `agx_search(p_query, p_deep, p_limit, p_offset)` — tier-2 server search.
|
|
308
|
+
* 3. `agx_resolve_agent_address(p_ids)` — the single-agent name read, used
|
|
309
|
+
* when a mandate's Holder is not in the caller's own catalog.
|
|
310
|
+
* 4. The caller's stars arrive ON the rows: every read above computes
|
|
311
|
+
* `is_favorite` server-side from `platform.user_entity_state`
|
|
312
|
+
* (`platform.my_favorite`). There is no second star read (FW-014; the
|
|
313
|
+
* `ues_get_bulk` overlay over every id was retired 2026-10-23).
|
|
314
|
+
* 5. ONE write: the caller's star, `ues_set` through the same port. The
|
|
315
|
+
* retired `agent.definition.is_favorite` column is never read or written.
|
|
316
|
+
* 6. THE PINNED READ (2026-10-09; by-ids 2026-10-23): the caller's starred
|
|
317
|
+
* ids (`ues_list` through the favorites port), then
|
|
318
|
+
* `agent.agx_get_by_ids(p_ids)` for exactly those ids. A sidebar's Pinned
|
|
319
|
+
* section shows a handful of rows; it never pays for the whole catalog,
|
|
320
|
+
* in the browser or on the server, to find them.
|
|
321
|
+
* 7. THE PICKER PAGE (FW-014, 2026-10-23): `agent.agx_picker_page` — the
|
|
322
|
+
* menu opens with ~50 thin rows (favorites, recently used, name) and the
|
|
323
|
+
* tab counts; scrolling reads the next keyset page. `agent.agx_get_by_ids`
|
|
324
|
+
* fills in a full row on hover/open. The whole-catalog read above remains
|
|
325
|
+
* for surfaces that genuinely list everything, and for a picker whose
|
|
326
|
+
* person narrows by category or tag (the facets need every row).
|
|
327
|
+
*
|
|
328
|
+
* 🚨 THE LIST READ IS COMPLETE OR IT THROWS. PostgREST caps every response at
|
|
329
|
+
* `db-max-rows` (1000 on Matrx Main) and reports success — matrx-frontend's
|
|
330
|
+
* `fetchAgentsListFull` issued a bare `.rpc()` and would silently have
|
|
331
|
+
* returned 1000 rows of a larger catalog (the ~400-builtin truncation
|
|
332
|
+
* incident is the near-miss). Here the read goes through `readAllRows` with
|
|
333
|
+
* `{ count: "exact" }` and a stable `.order("id")`, so a truncation is a
|
|
334
|
+
* loud `IncompleteReadError` instead of a confidently short picker.
|
|
335
|
+
* The DB's own favourites-first/`updated_at` order is irrelevant: every row
|
|
336
|
+
* is re-sorted client-side by `sortFilteredAgents`.
|
|
337
|
+
*/
|
|
338
|
+
|
|
339
|
+
/** Which slice of the caller's catalog a picker page reads. */
|
|
340
|
+
interface AgentPickerPageQuery {
|
|
341
|
+
/** `all_system` = the admin "All" tab that blends system agents in. */
|
|
342
|
+
tab: "mine" | "shared" | "all" | "all_system" | "system";
|
|
343
|
+
archive: "active" | "archived" | "both";
|
|
344
|
+
includeQuick: boolean;
|
|
345
|
+
}
|
|
346
|
+
/** The tab badges, counted server-side over the same reach as the rows. */
|
|
347
|
+
interface AgentPickerCounts {
|
|
348
|
+
mine: number;
|
|
349
|
+
shared: number;
|
|
350
|
+
all: number;
|
|
351
|
+
system: number;
|
|
352
|
+
/** Archived rows the active filter hides behind THIS tab (0 unless archive = active). */
|
|
353
|
+
archivedHidden: number;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* catalog/default-row-types.ts — @ai-matrx/agents/catalog
|
|
358
|
+
*
|
|
359
|
+
* Split out of `default-row.ts` so `global-state.ts` can name the cached
|
|
360
|
+
* shape without importing the resolver (and its transport work) — the same
|
|
361
|
+
* split-out reflex C8 asks for, at module scale.
|
|
362
|
+
*/
|
|
363
|
+
|
|
364
|
+
/** A resolved default row, cached for first paint and compared for drift. */
|
|
365
|
+
interface ResolvedDefaultRow {
|
|
366
|
+
mandateKey: string;
|
|
367
|
+
/** The `agent.definition` id the mandate resolved to. */
|
|
368
|
+
holderId: string;
|
|
369
|
+
/** The holder's REAL name, read from the catalog — never hardcoded. */
|
|
370
|
+
holderName: string;
|
|
371
|
+
holderDescription: string | null;
|
|
372
|
+
provenance: string;
|
|
373
|
+
/** Epoch ms of this resolution. */
|
|
374
|
+
at: number;
|
|
375
|
+
}
|
|
376
|
+
/** What the picker renders and what it screams, together. */
|
|
377
|
+
interface DefaultRowState {
|
|
378
|
+
mandateKey: string;
|
|
379
|
+
/** The row to render at the top of the list. `null` until resolved. */
|
|
380
|
+
row: AgentSummary | null;
|
|
381
|
+
/** The last live resolution, or the cached one before the live answer lands. */
|
|
382
|
+
resolved: ResolvedDefaultRow | null;
|
|
383
|
+
/** True while the resolution is in flight. */
|
|
384
|
+
loading: boolean;
|
|
385
|
+
/**
|
|
386
|
+
* 🚨 THE DRIFT SCREAM. Set when the cached first-paint row and the LIVE
|
|
387
|
+
* resolution disagree on holder id or holder name. Rendered as a persistent
|
|
388
|
+
* in-picker banner AND sent to the `notifier` port — never console-only.
|
|
389
|
+
*/
|
|
390
|
+
drift: string | null;
|
|
391
|
+
/** Set when the mandate could not be resolved at all. Loud, with a remedy. */
|
|
392
|
+
error: string | null;
|
|
393
|
+
}
|
|
394
|
+
|
|
329
395
|
/**
|
|
330
396
|
* catalog/schema.ts — @ai-matrx/agents/catalog
|
|
331
397
|
*
|
|
@@ -394,6 +460,20 @@ interface AssertAgentCatalogSchemaOptions {
|
|
|
394
460
|
*/
|
|
395
461
|
|
|
396
462
|
type AgentCatalogStatus = "idle" | "loading" | "succeeded" | "failed";
|
|
463
|
+
/** One picker slice (tab × archive × quick) read a page at a time (FW-014). */
|
|
464
|
+
interface AgentPickerPageState {
|
|
465
|
+
/** Row ids in server order across every page read so far. */
|
|
466
|
+
ids: string[];
|
|
467
|
+
/** Keyset position of the next page; `null` once the last page is in. */
|
|
468
|
+
nextCursor: unknown;
|
|
469
|
+
done: boolean;
|
|
470
|
+
status: AgentCatalogStatus;
|
|
471
|
+
/** True while a next page (not the first) is in flight. */
|
|
472
|
+
loadingMore: boolean;
|
|
473
|
+
error: string | null;
|
|
474
|
+
counts: AgentPickerCounts | null;
|
|
475
|
+
fetchedAt: number;
|
|
476
|
+
}
|
|
397
477
|
interface AgentCatalogState {
|
|
398
478
|
/** Every row, one stable array reference per change (memoization depends on it). */
|
|
399
479
|
rows: AgentSummary[];
|
|
@@ -409,6 +489,8 @@ interface AgentCatalogState {
|
|
|
409
489
|
* or by a complete catalog load, whichever came first.
|
|
410
490
|
*/
|
|
411
491
|
favoriteRowsStatus: AgentCatalogStatus;
|
|
492
|
+
/** The picker's paged slices, keyed by `agentPickerPageKey` (FW-014). */
|
|
493
|
+
pickerPages: Record<string, AgentPickerPageState>;
|
|
412
494
|
}
|
|
413
495
|
interface AgentCatalog {
|
|
414
496
|
readonly catalogId: string;
|
|
@@ -417,6 +499,8 @@ interface AgentCatalog {
|
|
|
417
499
|
readonly notifier: AgentCatalogNotifier | undefined;
|
|
418
500
|
/** True when this catalog was given a transport and can resolve mandates. */
|
|
419
501
|
readonly canResolveMandates: boolean;
|
|
502
|
+
/** `AgentCatalogConfig.pickerReads`, resolved (default `"page"`). */
|
|
503
|
+
readonly pickerReads: "page" | "catalog";
|
|
420
504
|
/**
|
|
421
505
|
* The consumer state a brand-new consumer starts from, AFTER the host's
|
|
422
506
|
* `defaults` knob is applied. Read it instead of
|
|
@@ -455,6 +539,28 @@ interface AgentCatalog {
|
|
|
455
539
|
* makes no request. Rows that do not parse are skipped.
|
|
456
540
|
*/
|
|
457
541
|
seedFavoriteRows(rawRows: unknown[]): void;
|
|
542
|
+
/**
|
|
543
|
+
* THE PICKER'S OPEN (FW-014): the first page (~50 thin rows) of one slice,
|
|
544
|
+
* plus its tab counts — never the whole catalog. Fresh for the list's TTL;
|
|
545
|
+
* deduped in flight; safe on every open.
|
|
546
|
+
*/
|
|
547
|
+
ensurePickerPage(query: AgentPickerPageQuery): Promise<void>;
|
|
548
|
+
/** The next page of a slice (scroll). No-op when the slice is done or already loading. */
|
|
549
|
+
loadMorePickerPage(query: AgentPickerPageQuery): Promise<void>;
|
|
550
|
+
/**
|
|
551
|
+
* FULL rows for these ids (`agent.agx_get_by_ids`), merged into the registry:
|
|
552
|
+
* a hover card's details, the pinned current agent, a name for an id that is
|
|
553
|
+
* not on page 1. Ids already held as full rows are not asked again; deduped
|
|
554
|
+
* in flight.
|
|
555
|
+
*/
|
|
556
|
+
ensureAgentRows(agentIds: readonly string[]): Promise<void>;
|
|
557
|
+
/**
|
|
558
|
+
* The caller's live agents carrying any of these tags, merged into the
|
|
559
|
+
* registry (`agent.agx_picker_tagged`) — the composer's quick agents and
|
|
560
|
+
* presets, never the catalog. Fresh for the list's TTL; deduped in flight;
|
|
561
|
+
* answered at once when the whole catalog is already fresh.
|
|
562
|
+
*/
|
|
563
|
+
ensureTaggedRows(tags: readonly string[]): Promise<void>;
|
|
458
564
|
/** True when the last load is inside the 15-minute TTL. */
|
|
459
565
|
isFresh(): boolean;
|
|
460
566
|
/** True when the last load is older than 4 hours (tab-restore threshold). */
|
|
@@ -563,10 +669,11 @@ declare function AgentCatalogProvider({ catalog, children, ...ports }: AgentCata
|
|
|
563
669
|
declare function lookUpAgentOnce(catalog: AgentCatalog, agentId: string): Promise<void>;
|
|
564
670
|
/**
|
|
565
671
|
* ONE agent's catalog row — name, description, category, tags, star, access — read straight from
|
|
566
|
-
* the catalog store (the ONE holder of agent identity; no host mirror). When the row is missing
|
|
567
|
-
*
|
|
568
|
-
*
|
|
569
|
-
* id per catalog (the Board's remount law: a woken or remounted tile
|
|
672
|
+
* the catalog store (the ONE holder of agent identity; no host mirror). When the row is missing (or
|
|
673
|
+
* only a thin picker row) that ONE row is read by id (`ensureAgentRows`, deduped) — never the whole
|
|
674
|
+
* catalog (FW-014); an id that read does not return is looked up by server search, which merges it
|
|
675
|
+
* into the catalog — once per id per catalog (the Board's remount law: a woken or remounted tile
|
|
676
|
+
* asks nothing again).
|
|
570
677
|
*
|
|
571
678
|
* Takes the catalog explicitly so a host without `AgentCatalogProvider` (chat, workers) binds it.
|
|
572
679
|
*/
|
|
@@ -661,6 +768,14 @@ declare function useAgentListCore({ consumerId, onSelect, navigateTo, activeAgen
|
|
|
661
768
|
hoveredAgent: AgentSummary | null;
|
|
662
769
|
setHoveredAgent: react.Dispatch<react.SetStateAction<AgentSummary | null>>;
|
|
663
770
|
ensureLoaded: () => void;
|
|
771
|
+
/** FW-014: the list reads a page at a time; true while the slice has more. */
|
|
772
|
+
paged: boolean;
|
|
773
|
+
hasMore: boolean;
|
|
774
|
+
loadingMore: boolean;
|
|
775
|
+
loadMore: () => void;
|
|
776
|
+
/** The tab's total (server count when paged) — the footer's number. */
|
|
777
|
+
totalCount: number;
|
|
778
|
+
ensureFullCatalog: () => void;
|
|
664
779
|
handleSelectAgent: (agent: AgentSummary) => void;
|
|
665
780
|
handleAgentHover: (agent: AgentSummary, panelOpen: boolean) => void;
|
|
666
781
|
handleAgentHoverEnd: (agent: AgentSummary, onClear: () => void) => void;
|
|
@@ -1052,8 +1167,19 @@ interface AgentListContentProps {
|
|
|
1052
1167
|
agentFilterLabel?: string | null | undefined;
|
|
1053
1168
|
/** "Show all" on the host-filter chip. */
|
|
1054
1169
|
onClearAgentFilter?: (() => void) | undefined;
|
|
1170
|
+
/**
|
|
1171
|
+
* FW-014 paging: true while the list has more rows on the server. The list
|
|
1172
|
+
* asks `onReachEnd` for the next page when the scroll nears the end (or when
|
|
1173
|
+
* the rows do not fill the viewport yet).
|
|
1174
|
+
*/
|
|
1175
|
+
hasMore?: boolean | undefined;
|
|
1176
|
+
onReachEnd?: (() => void) | undefined;
|
|
1177
|
+
/** True while the next page is in flight — a spinner row closes the list. */
|
|
1178
|
+
loadingMore?: boolean | undefined;
|
|
1179
|
+
/** The tab's total for the footer (server count when paged); defaults to the rendered rows. */
|
|
1180
|
+
totalCount?: number | undefined;
|
|
1055
1181
|
}
|
|
1056
|
-
declare function AgentListContent({ agents, isLoading, consumer, activeAgentId, allCategories, allTags, inputRef, onSelectAgent, resolveAgentHref, onReset, activeFilterCount, isMobile, hoveredAgent, onAgentHover, onAgentHoverEnd, onDetailPress, onFilterChipClick, rightPanel, tabCounts, visibleTabs, systemTabLabel, pinnedAgent, defaultRow, listOpen, archivedHiddenCount, agentFilterLabel, onClearAgentFilter, }: AgentListContentProps): react.JSX.Element;
|
|
1182
|
+
declare function AgentListContent({ agents, isLoading, consumer, activeAgentId, allCategories, allTags, inputRef, onSelectAgent, resolveAgentHref, onReset, activeFilterCount, isMobile, hoveredAgent, onAgentHover, onAgentHoverEnd, onDetailPress, onFilterChipClick, rightPanel, tabCounts, visibleTabs, systemTabLabel, pinnedAgent, defaultRow, listOpen, archivedHiddenCount, agentFilterLabel, onClearAgentFilter, hasMore, onReachEnd, loadingMore, totalCount, }: AgentListContentProps): react.JSX.Element;
|
|
1057
1183
|
|
|
1058
1184
|
interface AgentFilterBarProps {
|
|
1059
1185
|
consumer: UseAgentConsumerReturn;
|
|
@@ -139,12 +139,14 @@ function useAgentSummary(catalog, agentId) {
|
|
|
139
139
|
() => catalog && agentId ? catalog.getState().byId[agentId] : void 0,
|
|
140
140
|
() => catalog && agentId ? catalog.getState().byId[agentId] : void 0
|
|
141
141
|
);
|
|
142
|
-
const present = row !== void 0;
|
|
142
|
+
const present = row !== void 0 && !row.isThin;
|
|
143
143
|
useEffect(() => {
|
|
144
144
|
if (!catalog || !agentId || present) return;
|
|
145
145
|
let cancelled = false;
|
|
146
|
-
void catalog.ensureLoaded().then(() => {
|
|
147
|
-
|
|
146
|
+
void catalog.ensureAgentRows([agentId]).catch(() => catalog.ensureLoaded()).then(() => {
|
|
147
|
+
const held = catalog.getState().byId[agentId];
|
|
148
|
+
if (cancelled || held && !held.isThin) return;
|
|
149
|
+
if (held) return;
|
|
148
150
|
return lookUpAgentOnce(catalog, agentId);
|
|
149
151
|
}).catch(() => void 0);
|
|
150
152
|
return () => {
|
|
@@ -729,6 +731,14 @@ function agentListFooterLabel(args) {
|
|
|
729
731
|
return archivedHiddenCount > 0 ? `${base} \xB7 ${archivedHiddenCount} archived hidden` : base;
|
|
730
732
|
}
|
|
731
733
|
|
|
734
|
+
// catalog/store.ts
|
|
735
|
+
import { createAssociationsStore } from "@ai-matrx/associations/core";
|
|
736
|
+
var LIST_TTL_MS = 15 * 60 * 1e3;
|
|
737
|
+
var LIST_STALE_MS = 4 * 60 * 60 * 1e3;
|
|
738
|
+
function agentPickerPageKey(query) {
|
|
739
|
+
return `${query.tab}|${query.archive}|${query.includeQuick ? "q" : "-"}`;
|
|
740
|
+
}
|
|
741
|
+
|
|
732
742
|
// catalog/shared/useAgentListCore-react.ts
|
|
733
743
|
function useAgentListCore({
|
|
734
744
|
consumerId,
|
|
@@ -758,19 +768,60 @@ function useAgentListCore({
|
|
|
758
768
|
const allRows = useAgentCatalogRows();
|
|
759
769
|
const rows = useMemo3(() => includeQuickAgents ? allRows : withoutQuickAgents(allRows), [allRows, includeQuickAgents]);
|
|
760
770
|
const userId = catalog.getUserIdOrNull();
|
|
761
|
-
const
|
|
771
|
+
const status = useAgentCatalogState(selectStatus);
|
|
772
|
+
const [setAsideLabel, setSetAsideLabel] = useState2(null);
|
|
773
|
+
const activeAgentFilter = agentFilter && setAsideLabel !== agentFilter.label ? agentFilter : null;
|
|
774
|
+
const [facetsWanted, setFacetsWanted] = useState2(false);
|
|
775
|
+
const needsFullCatalog = facetsWanted || activeAgentFilter !== null || consumer.state.includedCats.length > 0 || consumer.state.includedTags.length > 0 || consumer.state.favFilter !== "all" || consumer.state.accessFilter !== "any" || !consumer.state.favoritesFirst || consumer.state.sortBy !== "updated-desc";
|
|
776
|
+
const fullCatalogReady = status === "succeeded" && catalog.isFresh();
|
|
777
|
+
const pageQuery = {
|
|
778
|
+
tab: includeSystemInAll && consumer.state.tab === "all" ? "all_system" : consumer.state.tab,
|
|
779
|
+
archive: consumer.state.archFilter,
|
|
780
|
+
includeQuick: includeQuickAgents
|
|
781
|
+
};
|
|
782
|
+
const pageKey = agentPickerPageKey(pageQuery);
|
|
783
|
+
const selectPage = useCallback3((state) => state.pickerPages[pageKey], [pageKey]);
|
|
784
|
+
const pageState = useAgentCatalogState(selectPage);
|
|
785
|
+
const pageUnsupported = pageState?.error === "unsupported";
|
|
786
|
+
const paged = catalog.pickerReads === "page" && !needsFullCatalog && !fullCatalogReady && !pageUnsupported;
|
|
787
|
+
const selectById = useCallback3((state) => state.byId, []);
|
|
788
|
+
const byId = useAgentCatalogState(selectById);
|
|
789
|
+
const pageRows = useMemo3(() => {
|
|
790
|
+
if (!pageState) return [];
|
|
791
|
+
const out = [];
|
|
792
|
+
for (const id of pageState.ids) {
|
|
793
|
+
const row = byId[id];
|
|
794
|
+
if (row) out.push(row);
|
|
795
|
+
}
|
|
796
|
+
return out;
|
|
797
|
+
}, [pageState, byId]);
|
|
798
|
+
const tabCountsForFilter = paged ? {
|
|
799
|
+
mine: pageState?.counts?.mine ?? 0,
|
|
800
|
+
shared: pageState?.counts?.shared ?? 0,
|
|
801
|
+
all: (pageState?.counts?.all ?? 0) + (includeSystemInAll ? pageState?.counts?.system ?? 0 : 0),
|
|
802
|
+
system: pageState?.counts?.system ?? 0
|
|
803
|
+
} : selectAgentTabCounts(rows, consumer.state.archFilter);
|
|
762
804
|
const ownedCount = tabCountsForFilter.mine;
|
|
763
805
|
const selectFiltered = useMemo3(
|
|
764
806
|
() => makeSelectFilteredAgents(includeSystemInAll),
|
|
765
807
|
[includeSystemInAll]
|
|
766
808
|
);
|
|
767
|
-
const
|
|
809
|
+
const searchPool = useMemo3(() => {
|
|
810
|
+
if (!paged || !consumer.state.searchTerm) return null;
|
|
811
|
+
const pool = /* @__PURE__ */ new Map();
|
|
812
|
+
for (const row of pageRows) pool.set(row.id, row);
|
|
813
|
+
for (const id of consumer.state.serverMatchedIds) {
|
|
814
|
+
const row = byId[id];
|
|
815
|
+
if (row) pool.set(id, row);
|
|
816
|
+
}
|
|
817
|
+
const list = [...pool.values()];
|
|
818
|
+
return includeQuickAgents ? list : withoutQuickAgents(list);
|
|
819
|
+
}, [paged, consumer.state.searchTerm, consumer.state.serverMatchedIds, pageRows, byId, includeQuickAgents]);
|
|
820
|
+
const filteredAgents = paged ? searchPool ? selectFiltered(searchPool, consumer.state) : pageRows : selectFiltered(rows, consumer.state);
|
|
768
821
|
const excludedAgentIds = useMemo3(
|
|
769
822
|
() => new Set(excludeAgentIds),
|
|
770
823
|
[excludeAgentIds]
|
|
771
824
|
);
|
|
772
|
-
const [setAsideLabel, setSetAsideLabel] = useState2(null);
|
|
773
|
-
const activeAgentFilter = agentFilter && setAsideLabel !== agentFilter.label ? agentFilter : null;
|
|
774
825
|
const filterTest = activeAgentFilter?.test;
|
|
775
826
|
const applyExcludes = useCallback3(
|
|
776
827
|
(list) => excludedAgentIds.size === 0 && !filterTest ? list : list.filter(
|
|
@@ -793,23 +844,44 @@ function useAgentListCore({
|
|
|
793
844
|
() => ({ ...consumer.state, archFilter: "archived" }),
|
|
794
845
|
[consumer.state]
|
|
795
846
|
);
|
|
796
|
-
const archivedHiddenCount = consumer.archFilter
|
|
797
|
-
const status = useAgentCatalogState(selectStatus);
|
|
847
|
+
const archivedHiddenCount = consumer.archFilter !== "active" || paged && consumer.state.searchTerm ? 0 : paged ? pageState?.counts?.archivedHidden ?? 0 : applyExcludes(selectArchivedProbe(rows, archiveProbeState)).length;
|
|
798
848
|
const activeAgentId = activeAgentIdOverride ?? null;
|
|
799
|
-
const pinnedAgent = activeAgentId ?
|
|
849
|
+
const pinnedAgent = activeAgentId ? byId[activeAgentId] : void 0;
|
|
800
850
|
const onSystemTab = consumer.tab === "system";
|
|
801
851
|
const allCategories = onSystemTab ? selectAllSystemAgentCategories(rows) : selectAllAgentCategories(rows);
|
|
802
852
|
const allTags = onSystemTab ? selectAllSystemAgentTags(rows) : selectAllAgentTags(rows);
|
|
803
853
|
const tabCounts = tabCountsForFilter;
|
|
804
|
-
const
|
|
805
|
-
const
|
|
854
|
+
const [engaged, setEngaged] = useState2(false);
|
|
855
|
+
const pageStatus = pageState?.status ?? "idle";
|
|
856
|
+
const isLoading = paged ? (pageStatus === "loading" || pageStatus === "idle") && pageRows.length === 0 && engaged : status === "loading";
|
|
857
|
+
const agentsLoaded = paged ? pageStatus === "succeeded" || pageStatus === "failed" : status === "succeeded" || status === "failed";
|
|
806
858
|
const ensureLoaded = useCallback3(() => {
|
|
807
859
|
if (!hasFetchedRef.current) {
|
|
808
860
|
hasFetchedRef.current = true;
|
|
809
|
-
|
|
810
|
-
});
|
|
861
|
+
setEngaged(true);
|
|
811
862
|
}
|
|
812
|
-
}, [
|
|
863
|
+
}, []);
|
|
864
|
+
const pageQueryRef = useRef2(pageQuery);
|
|
865
|
+
pageQueryRef.current = pageQuery;
|
|
866
|
+
useEffect3(() => {
|
|
867
|
+
if (!engaged) return;
|
|
868
|
+
if (paged) void catalog.ensurePickerPage(pageQueryRef.current).catch(() => void 0);
|
|
869
|
+
else void catalog.ensureLoaded().catch(() => void 0);
|
|
870
|
+
}, [engaged, paged, pageKey, catalog]);
|
|
871
|
+
const loadMore = useCallback3(() => {
|
|
872
|
+
if (!paged) return;
|
|
873
|
+
void catalog.loadMorePickerPage(pageQueryRef.current).catch(() => void 0);
|
|
874
|
+
}, [paged, catalog]);
|
|
875
|
+
const ensureFullCatalog = useCallback3(() => {
|
|
876
|
+
setFacetsWanted(true);
|
|
877
|
+
}, []);
|
|
878
|
+
const hasMore = paged && pageStatus === "succeeded" && pageState?.done === false;
|
|
879
|
+
const loadingMore = paged && pageState?.loadingMore === true;
|
|
880
|
+
const pinnedMissing = Boolean(activeAgentId) && pinnedAgent === void 0;
|
|
881
|
+
useEffect3(() => {
|
|
882
|
+
if (!engaged || !pinnedMissing || !activeAgentId) return;
|
|
883
|
+
void catalog.ensureAgentRows([activeAgentId]).catch(() => void 0);
|
|
884
|
+
}, [engaged, pinnedMissing, activeAgentId, catalog]);
|
|
813
885
|
const selectDefaultRow = useCallback3(
|
|
814
886
|
(state) => defaultMandateKey ? state.defaultRows[defaultMandateKey] : void 0,
|
|
815
887
|
[defaultMandateKey]
|
|
@@ -886,13 +958,14 @@ function useAgentListCore({
|
|
|
886
958
|
const handleAgentHover = useCallback3(
|
|
887
959
|
(agent, panelOpen) => {
|
|
888
960
|
if (panelOpen) return;
|
|
961
|
+
if (agent.isThin) void catalog.ensureAgentRows([agent.id]).catch(() => void 0);
|
|
889
962
|
if (hoverLeaveTimerRef.current) {
|
|
890
963
|
clearTimeout(hoverLeaveTimerRef.current);
|
|
891
964
|
hoverLeaveTimerRef.current = null;
|
|
892
965
|
}
|
|
893
966
|
setHoveredAgent(agent);
|
|
894
967
|
},
|
|
895
|
-
[]
|
|
968
|
+
[catalog]
|
|
896
969
|
);
|
|
897
970
|
const handleAgentHoverEnd = useCallback3(
|
|
898
971
|
(agent, onClear) => {
|
|
@@ -923,8 +996,10 @@ function useAgentListCore({
|
|
|
923
996
|
},
|
|
924
997
|
[]
|
|
925
998
|
);
|
|
999
|
+
const tabTotal = consumer.state.tab === "mine" ? tabCountsForFilter.mine : consumer.state.tab === "shared" ? tabCountsForFilter.shared : consumer.state.tab === "system" ? tabCountsForFilter.system : tabCountsForFilter.all;
|
|
1000
|
+
const totalCount = paged && !consumer.state.searchTerm && excludedAgentIds.size === 0 ? Math.max(tabTotal, agents.length) : agents.length;
|
|
926
1001
|
const footerLabel = agentListFooterLabel({
|
|
927
|
-
count:
|
|
1002
|
+
count: totalCount,
|
|
928
1003
|
archFilter: consumer.archFilter,
|
|
929
1004
|
archivedHiddenCount
|
|
930
1005
|
});
|
|
@@ -948,6 +1023,14 @@ function useAgentListCore({
|
|
|
948
1023
|
hoveredAgent,
|
|
949
1024
|
setHoveredAgent,
|
|
950
1025
|
ensureLoaded,
|
|
1026
|
+
/** FW-014: the list reads a page at a time; true while the slice has more. */
|
|
1027
|
+
paged,
|
|
1028
|
+
hasMore,
|
|
1029
|
+
loadingMore,
|
|
1030
|
+
loadMore,
|
|
1031
|
+
/** The tab's total (server count when paged) — the footer's number. */
|
|
1032
|
+
totalCount,
|
|
1033
|
+
ensureFullCatalog,
|
|
951
1034
|
handleSelectAgent,
|
|
952
1035
|
handleAgentHover,
|
|
953
1036
|
handleAgentHoverEnd,
|
|
@@ -1442,7 +1525,8 @@ function AgentDetailCard({ agent, onSelect, duplicate, onHoldChange }) {
|
|
|
1442
1525
|
const select = useCallback4((state) => state.byId[agent.id], [agent.id]);
|
|
1443
1526
|
const row = useAgentCatalogState(select);
|
|
1444
1527
|
const on = row?.isFavorite ?? agent.isFavorite ?? false;
|
|
1445
|
-
const
|
|
1528
|
+
const live = row && !row.isThin ? row : agent;
|
|
1529
|
+
const record = agentPeekRecord(live, {
|
|
1446
1530
|
favorite: {
|
|
1447
1531
|
on,
|
|
1448
1532
|
onToggle: () => {
|
|
@@ -1739,6 +1823,7 @@ var AGENT_ROW_HEIGHT = 36;
|
|
|
1739
1823
|
var UNMEASURED_VIEWPORT = 480;
|
|
1740
1824
|
var ROW_OVERSCAN = 8;
|
|
1741
1825
|
var WINDOW_ABOVE = 50;
|
|
1826
|
+
var REACH_END_ROWS = 10;
|
|
1742
1827
|
function AgentListContent({
|
|
1743
1828
|
agents,
|
|
1744
1829
|
isLoading,
|
|
@@ -1766,7 +1851,11 @@ function AgentListContent({
|
|
|
1766
1851
|
listOpen = false,
|
|
1767
1852
|
archivedHiddenCount = 0,
|
|
1768
1853
|
agentFilterLabel = null,
|
|
1769
|
-
onClearAgentFilter
|
|
1854
|
+
onClearAgentFilter,
|
|
1855
|
+
hasMore = false,
|
|
1856
|
+
onReachEnd,
|
|
1857
|
+
loadingMore = false,
|
|
1858
|
+
totalCount
|
|
1770
1859
|
}) {
|
|
1771
1860
|
const catalog = useAgentCatalog();
|
|
1772
1861
|
const listScrollRef = useRef3(null);
|
|
@@ -1815,6 +1904,12 @@ function AgentListContent({
|
|
|
1815
1904
|
scroller.scrollTo({ top });
|
|
1816
1905
|
setScrollTop(top);
|
|
1817
1906
|
}, [listOpen, pinnedAgent?.id, activeAgentId, isLoading]);
|
|
1907
|
+
useEffect4(() => {
|
|
1908
|
+
if (!hasMore || !onReachEnd || isLoading) return;
|
|
1909
|
+
const view = viewport > 0 ? viewport : UNMEASURED_VIEWPORT;
|
|
1910
|
+
const listBottom = listStart + agents.length * AGENT_ROW_HEIGHT;
|
|
1911
|
+
if (scrollTop + view >= listBottom - REACH_END_ROWS * AGENT_ROW_HEIGHT) onReachEnd();
|
|
1912
|
+
}, [hasMore, onReachEnd, isLoading, scrollTop, viewport, listStart, agents.length]);
|
|
1818
1913
|
const win = agents.length <= WINDOW_ABOVE ? { start: 0, end: agents.length, padStart: 0, padEnd: 0 } : computeWindow({
|
|
1819
1914
|
count: agents.length,
|
|
1820
1915
|
itemSize: AGENT_ROW_HEIGHT,
|
|
@@ -2071,6 +2166,14 @@ function AgentListContent({
|
|
|
2071
2166
|
]
|
|
2072
2167
|
}
|
|
2073
2168
|
),
|
|
2169
|
+
loadingMore && /* @__PURE__ */ jsx10(
|
|
2170
|
+
"div",
|
|
2171
|
+
{
|
|
2172
|
+
"data-testid": "agent-list-loading-more",
|
|
2173
|
+
className: "flex items-center justify-center py-2 text-muted-foreground",
|
|
2174
|
+
children: /* @__PURE__ */ jsx10(LoaderIcon, { className: "w-3.5 h-3.5 matrx-agent-spin" })
|
|
2175
|
+
}
|
|
2176
|
+
),
|
|
2074
2177
|
agents.length === 0 && (pinnedAgent || defaultAgent) && /* @__PURE__ */ jsx10("div", { className: "flex flex-col items-center py-4 text-muted-foreground", children: /* @__PURE__ */ jsx10("span", { className: "text-xs", children: emptyLabel }) })
|
|
2075
2178
|
] })
|
|
2076
2179
|
}
|
|
@@ -2083,7 +2186,7 @@ function AgentListContent({
|
|
|
2083
2186
|
"data-testid": "agent-list-footer",
|
|
2084
2187
|
className: "text-[10px] text-muted-foreground tabular-nums",
|
|
2085
2188
|
children: agentListFooterLabel({
|
|
2086
|
-
count: agents.length,
|
|
2189
|
+
count: totalCount ?? agents.length,
|
|
2087
2190
|
archFilter: consumer.archFilter,
|
|
2088
2191
|
archivedHiddenCount
|
|
2089
2192
|
})
|
|
@@ -2317,6 +2420,11 @@ function AgentListPanelBody({
|
|
|
2317
2420
|
hoveredAgent,
|
|
2318
2421
|
setHoveredAgent,
|
|
2319
2422
|
ensureLoaded,
|
|
2423
|
+
hasMore,
|
|
2424
|
+
loadMore,
|
|
2425
|
+
loadingMore,
|
|
2426
|
+
totalCount,
|
|
2427
|
+
ensureFullCatalog,
|
|
2320
2428
|
handleSelectAgent: coreSelectAgent,
|
|
2321
2429
|
handleAgentHover: coreAgentHover,
|
|
2322
2430
|
handleAgentHoverEnd: coreAgentHoverEnd,
|
|
@@ -2344,6 +2452,7 @@ function AgentListPanelBody({
|
|
|
2344
2452
|
onAfterSelect?.(agent);
|
|
2345
2453
|
};
|
|
2346
2454
|
const handleFilterChipClick = (panel) => {
|
|
2455
|
+
if (panel !== "sort") ensureFullCatalog();
|
|
2347
2456
|
if (isMobile) {
|
|
2348
2457
|
setMobileSubView(panel);
|
|
2349
2458
|
} else {
|
|
@@ -2416,6 +2525,10 @@ function AgentListPanelBody({
|
|
|
2416
2525
|
defaultRow,
|
|
2417
2526
|
agentFilterLabel,
|
|
2418
2527
|
onClearAgentFilter: clearAgentFilter,
|
|
2528
|
+
hasMore,
|
|
2529
|
+
onReachEnd: loadMore,
|
|
2530
|
+
loadingMore,
|
|
2531
|
+
totalCount,
|
|
2419
2532
|
listOpen: true
|
|
2420
2533
|
}
|
|
2421
2534
|
);
|
|
@@ -2755,6 +2868,11 @@ function AgentListInlinePicker({
|
|
|
2755
2868
|
agentFilterLabel,
|
|
2756
2869
|
clearAgentFilter,
|
|
2757
2870
|
ensureLoaded,
|
|
2871
|
+
hasMore,
|
|
2872
|
+
loadMore,
|
|
2873
|
+
loadingMore,
|
|
2874
|
+
totalCount,
|
|
2875
|
+
ensureFullCatalog,
|
|
2758
2876
|
handleSelectAgent: coreSelectAgent,
|
|
2759
2877
|
handleAgentHover,
|
|
2760
2878
|
handleAgentHoverEnd
|
|
@@ -2784,6 +2902,7 @@ function AgentListInlinePicker({
|
|
|
2784
2902
|
setMobileSubView(null);
|
|
2785
2903
|
};
|
|
2786
2904
|
const handleFilterChipClick = (panel) => {
|
|
2905
|
+
if (panel !== "sort") ensureFullCatalog();
|
|
2787
2906
|
setMobileSubView(panel);
|
|
2788
2907
|
};
|
|
2789
2908
|
const isMobile = true;
|
|
@@ -2862,6 +2981,10 @@ function AgentListInlinePicker({
|
|
|
2862
2981
|
defaultRow,
|
|
2863
2982
|
agentFilterLabel,
|
|
2864
2983
|
onClearAgentFilter: clearAgentFilter,
|
|
2984
|
+
hasMore,
|
|
2985
|
+
onReachEnd: loadMore,
|
|
2986
|
+
loadingMore,
|
|
2987
|
+
totalCount,
|
|
2865
2988
|
listOpen: true
|
|
2866
2989
|
}
|
|
2867
2990
|
) });
|
|
@@ -406,7 +406,6 @@ var PROVISION_OFFER_KINDS = {
|
|
|
406
406
|
"social.x_search": "social.x_search.offer",
|
|
407
407
|
"spaces.ask_page": "spaces.ask_page.offer",
|
|
408
408
|
"spaces.ask_workspace": "spaces.ask_workspace.offer",
|
|
409
|
-
"spaces.autofill": "spaces.autofill.offer",
|
|
410
409
|
"spaces.build": "spaces.build.offer",
|
|
411
410
|
"spaces.design_database": "spaces.design_database.offer",
|
|
412
411
|
"spaces.move_in": "spaces.move_in.offer",
|