@ai-matrx/agents 0.59.263 → 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/context/react/index.cjs +19 -10
- package/dist/context/react/index.d.cts +0 -6
- package/dist/context/react/index.d.ts +0 -6
- package/dist/context/react/index.js +19 -10
- 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
|
@@ -198,12 +198,14 @@ function useAgentSummary(catalog, agentId) {
|
|
|
198
198
|
() => catalog && agentId ? catalog.getState().byId[agentId] : void 0,
|
|
199
199
|
() => catalog && agentId ? catalog.getState().byId[agentId] : void 0
|
|
200
200
|
);
|
|
201
|
-
const present = row !== void 0;
|
|
201
|
+
const present = row !== void 0 && !row.isThin;
|
|
202
202
|
(0, import_react3.useEffect)(() => {
|
|
203
203
|
if (!catalog || !agentId || present) return;
|
|
204
204
|
let cancelled = false;
|
|
205
|
-
void catalog.ensureLoaded().then(() => {
|
|
206
|
-
|
|
205
|
+
void catalog.ensureAgentRows([agentId]).catch(() => catalog.ensureLoaded()).then(() => {
|
|
206
|
+
const held = catalog.getState().byId[agentId];
|
|
207
|
+
if (cancelled || held && !held.isThin) return;
|
|
208
|
+
if (held) return;
|
|
207
209
|
return lookUpAgentOnce(catalog, agentId);
|
|
208
210
|
}).catch(() => void 0);
|
|
209
211
|
return () => {
|
|
@@ -788,6 +790,14 @@ function agentListFooterLabel(args) {
|
|
|
788
790
|
return archivedHiddenCount > 0 ? `${base} \xB7 ${archivedHiddenCount} archived hidden` : base;
|
|
789
791
|
}
|
|
790
792
|
|
|
793
|
+
// catalog/store.ts
|
|
794
|
+
var import_core = require("@ai-matrx/associations/core");
|
|
795
|
+
var LIST_TTL_MS = 15 * 60 * 1e3;
|
|
796
|
+
var LIST_STALE_MS = 4 * 60 * 60 * 1e3;
|
|
797
|
+
function agentPickerPageKey(query) {
|
|
798
|
+
return `${query.tab}|${query.archive}|${query.includeQuick ? "q" : "-"}`;
|
|
799
|
+
}
|
|
800
|
+
|
|
791
801
|
// catalog/shared/useAgentListCore-react.ts
|
|
792
802
|
function useAgentListCore({
|
|
793
803
|
consumerId,
|
|
@@ -817,19 +827,60 @@ function useAgentListCore({
|
|
|
817
827
|
const allRows = useAgentCatalogRows();
|
|
818
828
|
const rows = (0, import_react5.useMemo)(() => includeQuickAgents ? allRows : withoutQuickAgents(allRows), [allRows, includeQuickAgents]);
|
|
819
829
|
const userId = catalog.getUserIdOrNull();
|
|
820
|
-
const
|
|
830
|
+
const status = useAgentCatalogState(selectStatus);
|
|
831
|
+
const [setAsideLabel, setSetAsideLabel] = (0, import_react5.useState)(null);
|
|
832
|
+
const activeAgentFilter = agentFilter && setAsideLabel !== agentFilter.label ? agentFilter : null;
|
|
833
|
+
const [facetsWanted, setFacetsWanted] = (0, import_react5.useState)(false);
|
|
834
|
+
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";
|
|
835
|
+
const fullCatalogReady = status === "succeeded" && catalog.isFresh();
|
|
836
|
+
const pageQuery = {
|
|
837
|
+
tab: includeSystemInAll && consumer.state.tab === "all" ? "all_system" : consumer.state.tab,
|
|
838
|
+
archive: consumer.state.archFilter,
|
|
839
|
+
includeQuick: includeQuickAgents
|
|
840
|
+
};
|
|
841
|
+
const pageKey = agentPickerPageKey(pageQuery);
|
|
842
|
+
const selectPage = (0, import_react5.useCallback)((state) => state.pickerPages[pageKey], [pageKey]);
|
|
843
|
+
const pageState = useAgentCatalogState(selectPage);
|
|
844
|
+
const pageUnsupported = pageState?.error === "unsupported";
|
|
845
|
+
const paged = catalog.pickerReads === "page" && !needsFullCatalog && !fullCatalogReady && !pageUnsupported;
|
|
846
|
+
const selectById = (0, import_react5.useCallback)((state) => state.byId, []);
|
|
847
|
+
const byId = useAgentCatalogState(selectById);
|
|
848
|
+
const pageRows = (0, import_react5.useMemo)(() => {
|
|
849
|
+
if (!pageState) return [];
|
|
850
|
+
const out = [];
|
|
851
|
+
for (const id of pageState.ids) {
|
|
852
|
+
const row = byId[id];
|
|
853
|
+
if (row) out.push(row);
|
|
854
|
+
}
|
|
855
|
+
return out;
|
|
856
|
+
}, [pageState, byId]);
|
|
857
|
+
const tabCountsForFilter = paged ? {
|
|
858
|
+
mine: pageState?.counts?.mine ?? 0,
|
|
859
|
+
shared: pageState?.counts?.shared ?? 0,
|
|
860
|
+
all: (pageState?.counts?.all ?? 0) + (includeSystemInAll ? pageState?.counts?.system ?? 0 : 0),
|
|
861
|
+
system: pageState?.counts?.system ?? 0
|
|
862
|
+
} : selectAgentTabCounts(rows, consumer.state.archFilter);
|
|
821
863
|
const ownedCount = tabCountsForFilter.mine;
|
|
822
864
|
const selectFiltered = (0, import_react5.useMemo)(
|
|
823
865
|
() => makeSelectFilteredAgents(includeSystemInAll),
|
|
824
866
|
[includeSystemInAll]
|
|
825
867
|
);
|
|
826
|
-
const
|
|
868
|
+
const searchPool = (0, import_react5.useMemo)(() => {
|
|
869
|
+
if (!paged || !consumer.state.searchTerm) return null;
|
|
870
|
+
const pool = /* @__PURE__ */ new Map();
|
|
871
|
+
for (const row of pageRows) pool.set(row.id, row);
|
|
872
|
+
for (const id of consumer.state.serverMatchedIds) {
|
|
873
|
+
const row = byId[id];
|
|
874
|
+
if (row) pool.set(id, row);
|
|
875
|
+
}
|
|
876
|
+
const list = [...pool.values()];
|
|
877
|
+
return includeQuickAgents ? list : withoutQuickAgents(list);
|
|
878
|
+
}, [paged, consumer.state.searchTerm, consumer.state.serverMatchedIds, pageRows, byId, includeQuickAgents]);
|
|
879
|
+
const filteredAgents = paged ? searchPool ? selectFiltered(searchPool, consumer.state) : pageRows : selectFiltered(rows, consumer.state);
|
|
827
880
|
const excludedAgentIds = (0, import_react5.useMemo)(
|
|
828
881
|
() => new Set(excludeAgentIds),
|
|
829
882
|
[excludeAgentIds]
|
|
830
883
|
);
|
|
831
|
-
const [setAsideLabel, setSetAsideLabel] = (0, import_react5.useState)(null);
|
|
832
|
-
const activeAgentFilter = agentFilter && setAsideLabel !== agentFilter.label ? agentFilter : null;
|
|
833
884
|
const filterTest = activeAgentFilter?.test;
|
|
834
885
|
const applyExcludes = (0, import_react5.useCallback)(
|
|
835
886
|
(list) => excludedAgentIds.size === 0 && !filterTest ? list : list.filter(
|
|
@@ -852,23 +903,44 @@ function useAgentListCore({
|
|
|
852
903
|
() => ({ ...consumer.state, archFilter: "archived" }),
|
|
853
904
|
[consumer.state]
|
|
854
905
|
);
|
|
855
|
-
const archivedHiddenCount = consumer.archFilter
|
|
856
|
-
const status = useAgentCatalogState(selectStatus);
|
|
906
|
+
const archivedHiddenCount = consumer.archFilter !== "active" || paged && consumer.state.searchTerm ? 0 : paged ? pageState?.counts?.archivedHidden ?? 0 : applyExcludes(selectArchivedProbe(rows, archiveProbeState)).length;
|
|
857
907
|
const activeAgentId = activeAgentIdOverride ?? null;
|
|
858
|
-
const pinnedAgent = activeAgentId ?
|
|
908
|
+
const pinnedAgent = activeAgentId ? byId[activeAgentId] : void 0;
|
|
859
909
|
const onSystemTab = consumer.tab === "system";
|
|
860
910
|
const allCategories = onSystemTab ? selectAllSystemAgentCategories(rows) : selectAllAgentCategories(rows);
|
|
861
911
|
const allTags = onSystemTab ? selectAllSystemAgentTags(rows) : selectAllAgentTags(rows);
|
|
862
912
|
const tabCounts = tabCountsForFilter;
|
|
863
|
-
const
|
|
864
|
-
const
|
|
913
|
+
const [engaged, setEngaged] = (0, import_react5.useState)(false);
|
|
914
|
+
const pageStatus = pageState?.status ?? "idle";
|
|
915
|
+
const isLoading = paged ? (pageStatus === "loading" || pageStatus === "idle") && pageRows.length === 0 && engaged : status === "loading";
|
|
916
|
+
const agentsLoaded = paged ? pageStatus === "succeeded" || pageStatus === "failed" : status === "succeeded" || status === "failed";
|
|
865
917
|
const ensureLoaded = (0, import_react5.useCallback)(() => {
|
|
866
918
|
if (!hasFetchedRef.current) {
|
|
867
919
|
hasFetchedRef.current = true;
|
|
868
|
-
|
|
869
|
-
});
|
|
920
|
+
setEngaged(true);
|
|
870
921
|
}
|
|
871
|
-
}, [
|
|
922
|
+
}, []);
|
|
923
|
+
const pageQueryRef = (0, import_react5.useRef)(pageQuery);
|
|
924
|
+
pageQueryRef.current = pageQuery;
|
|
925
|
+
(0, import_react5.useEffect)(() => {
|
|
926
|
+
if (!engaged) return;
|
|
927
|
+
if (paged) void catalog.ensurePickerPage(pageQueryRef.current).catch(() => void 0);
|
|
928
|
+
else void catalog.ensureLoaded().catch(() => void 0);
|
|
929
|
+
}, [engaged, paged, pageKey, catalog]);
|
|
930
|
+
const loadMore = (0, import_react5.useCallback)(() => {
|
|
931
|
+
if (!paged) return;
|
|
932
|
+
void catalog.loadMorePickerPage(pageQueryRef.current).catch(() => void 0);
|
|
933
|
+
}, [paged, catalog]);
|
|
934
|
+
const ensureFullCatalog = (0, import_react5.useCallback)(() => {
|
|
935
|
+
setFacetsWanted(true);
|
|
936
|
+
}, []);
|
|
937
|
+
const hasMore = paged && pageStatus === "succeeded" && pageState?.done === false;
|
|
938
|
+
const loadingMore = paged && pageState?.loadingMore === true;
|
|
939
|
+
const pinnedMissing = Boolean(activeAgentId) && pinnedAgent === void 0;
|
|
940
|
+
(0, import_react5.useEffect)(() => {
|
|
941
|
+
if (!engaged || !pinnedMissing || !activeAgentId) return;
|
|
942
|
+
void catalog.ensureAgentRows([activeAgentId]).catch(() => void 0);
|
|
943
|
+
}, [engaged, pinnedMissing, activeAgentId, catalog]);
|
|
872
944
|
const selectDefaultRow = (0, import_react5.useCallback)(
|
|
873
945
|
(state) => defaultMandateKey ? state.defaultRows[defaultMandateKey] : void 0,
|
|
874
946
|
[defaultMandateKey]
|
|
@@ -945,13 +1017,14 @@ function useAgentListCore({
|
|
|
945
1017
|
const handleAgentHover = (0, import_react5.useCallback)(
|
|
946
1018
|
(agent, panelOpen) => {
|
|
947
1019
|
if (panelOpen) return;
|
|
1020
|
+
if (agent.isThin) void catalog.ensureAgentRows([agent.id]).catch(() => void 0);
|
|
948
1021
|
if (hoverLeaveTimerRef.current) {
|
|
949
1022
|
clearTimeout(hoverLeaveTimerRef.current);
|
|
950
1023
|
hoverLeaveTimerRef.current = null;
|
|
951
1024
|
}
|
|
952
1025
|
setHoveredAgent(agent);
|
|
953
1026
|
},
|
|
954
|
-
[]
|
|
1027
|
+
[catalog]
|
|
955
1028
|
);
|
|
956
1029
|
const handleAgentHoverEnd = (0, import_react5.useCallback)(
|
|
957
1030
|
(agent, onClear) => {
|
|
@@ -982,8 +1055,10 @@ function useAgentListCore({
|
|
|
982
1055
|
},
|
|
983
1056
|
[]
|
|
984
1057
|
);
|
|
1058
|
+
const tabTotal = consumer.state.tab === "mine" ? tabCountsForFilter.mine : consumer.state.tab === "shared" ? tabCountsForFilter.shared : consumer.state.tab === "system" ? tabCountsForFilter.system : tabCountsForFilter.all;
|
|
1059
|
+
const totalCount = paged && !consumer.state.searchTerm && excludedAgentIds.size === 0 ? Math.max(tabTotal, agents.length) : agents.length;
|
|
985
1060
|
const footerLabel = agentListFooterLabel({
|
|
986
|
-
count:
|
|
1061
|
+
count: totalCount,
|
|
987
1062
|
archFilter: consumer.archFilter,
|
|
988
1063
|
archivedHiddenCount
|
|
989
1064
|
});
|
|
@@ -1007,6 +1082,14 @@ function useAgentListCore({
|
|
|
1007
1082
|
hoveredAgent,
|
|
1008
1083
|
setHoveredAgent,
|
|
1009
1084
|
ensureLoaded,
|
|
1085
|
+
/** FW-014: the list reads a page at a time; true while the slice has more. */
|
|
1086
|
+
paged,
|
|
1087
|
+
hasMore,
|
|
1088
|
+
loadingMore,
|
|
1089
|
+
loadMore,
|
|
1090
|
+
/** The tab's total (server count when paged) — the footer's number. */
|
|
1091
|
+
totalCount,
|
|
1092
|
+
ensureFullCatalog,
|
|
1010
1093
|
handleSelectAgent,
|
|
1011
1094
|
handleAgentHover,
|
|
1012
1095
|
handleAgentHoverEnd,
|
|
@@ -1488,7 +1571,8 @@ function AgentDetailCard({ agent, onSelect, duplicate, onHoldChange }) {
|
|
|
1488
1571
|
const select = (0, import_react6.useCallback)((state) => state.byId[agent.id], [agent.id]);
|
|
1489
1572
|
const row = useAgentCatalogState(select);
|
|
1490
1573
|
const on = row?.isFavorite ?? agent.isFavorite ?? false;
|
|
1491
|
-
const
|
|
1574
|
+
const live = row && !row.isThin ? row : agent;
|
|
1575
|
+
const record = agentPeekRecord(live, {
|
|
1492
1576
|
favorite: {
|
|
1493
1577
|
on,
|
|
1494
1578
|
onToggle: () => {
|
|
@@ -1785,6 +1869,7 @@ var AGENT_ROW_HEIGHT = 36;
|
|
|
1785
1869
|
var UNMEASURED_VIEWPORT = 480;
|
|
1786
1870
|
var ROW_OVERSCAN = 8;
|
|
1787
1871
|
var WINDOW_ABOVE = 50;
|
|
1872
|
+
var REACH_END_ROWS = 10;
|
|
1788
1873
|
function AgentListContent({
|
|
1789
1874
|
agents,
|
|
1790
1875
|
isLoading,
|
|
@@ -1812,7 +1897,11 @@ function AgentListContent({
|
|
|
1812
1897
|
listOpen = false,
|
|
1813
1898
|
archivedHiddenCount = 0,
|
|
1814
1899
|
agentFilterLabel = null,
|
|
1815
|
-
onClearAgentFilter
|
|
1900
|
+
onClearAgentFilter,
|
|
1901
|
+
hasMore = false,
|
|
1902
|
+
onReachEnd,
|
|
1903
|
+
loadingMore = false,
|
|
1904
|
+
totalCount
|
|
1816
1905
|
}) {
|
|
1817
1906
|
const catalog = useAgentCatalog();
|
|
1818
1907
|
const listScrollRef = (0, import_react7.useRef)(null);
|
|
@@ -1861,6 +1950,12 @@ function AgentListContent({
|
|
|
1861
1950
|
scroller.scrollTo({ top });
|
|
1862
1951
|
setScrollTop(top);
|
|
1863
1952
|
}, [listOpen, pinnedAgent?.id, activeAgentId, isLoading]);
|
|
1953
|
+
(0, import_react7.useEffect)(() => {
|
|
1954
|
+
if (!hasMore || !onReachEnd || isLoading) return;
|
|
1955
|
+
const view = viewport > 0 ? viewport : UNMEASURED_VIEWPORT;
|
|
1956
|
+
const listBottom = listStart + agents.length * AGENT_ROW_HEIGHT;
|
|
1957
|
+
if (scrollTop + view >= listBottom - REACH_END_ROWS * AGENT_ROW_HEIGHT) onReachEnd();
|
|
1958
|
+
}, [hasMore, onReachEnd, isLoading, scrollTop, viewport, listStart, agents.length]);
|
|
1864
1959
|
const win = agents.length <= WINDOW_ABOVE ? { start: 0, end: agents.length, padStart: 0, padEnd: 0 } : (0, import_virtual_window.computeWindow)({
|
|
1865
1960
|
count: agents.length,
|
|
1866
1961
|
itemSize: AGENT_ROW_HEIGHT,
|
|
@@ -2117,6 +2212,14 @@ function AgentListContent({
|
|
|
2117
2212
|
]
|
|
2118
2213
|
}
|
|
2119
2214
|
),
|
|
2215
|
+
loadingMore && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2216
|
+
"div",
|
|
2217
|
+
{
|
|
2218
|
+
"data-testid": "agent-list-loading-more",
|
|
2219
|
+
className: "flex items-center justify-center py-2 text-muted-foreground",
|
|
2220
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(LoaderIcon, { className: "w-3.5 h-3.5 matrx-agent-spin" })
|
|
2221
|
+
}
|
|
2222
|
+
),
|
|
2120
2223
|
agents.length === 0 && (pinnedAgent || defaultAgent) && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex flex-col items-center py-4 text-muted-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-xs", children: emptyLabel }) })
|
|
2121
2224
|
] })
|
|
2122
2225
|
}
|
|
@@ -2129,7 +2232,7 @@ function AgentListContent({
|
|
|
2129
2232
|
"data-testid": "agent-list-footer",
|
|
2130
2233
|
className: "text-[10px] text-muted-foreground tabular-nums",
|
|
2131
2234
|
children: agentListFooterLabel({
|
|
2132
|
-
count: agents.length,
|
|
2235
|
+
count: totalCount ?? agents.length,
|
|
2133
2236
|
archFilter: consumer.archFilter,
|
|
2134
2237
|
archivedHiddenCount
|
|
2135
2238
|
})
|
|
@@ -2363,6 +2466,11 @@ function AgentListPanelBody({
|
|
|
2363
2466
|
hoveredAgent,
|
|
2364
2467
|
setHoveredAgent,
|
|
2365
2468
|
ensureLoaded,
|
|
2469
|
+
hasMore,
|
|
2470
|
+
loadMore,
|
|
2471
|
+
loadingMore,
|
|
2472
|
+
totalCount,
|
|
2473
|
+
ensureFullCatalog,
|
|
2366
2474
|
handleSelectAgent: coreSelectAgent,
|
|
2367
2475
|
handleAgentHover: coreAgentHover,
|
|
2368
2476
|
handleAgentHoverEnd: coreAgentHoverEnd,
|
|
@@ -2390,6 +2498,7 @@ function AgentListPanelBody({
|
|
|
2390
2498
|
onAfterSelect?.(agent);
|
|
2391
2499
|
};
|
|
2392
2500
|
const handleFilterChipClick = (panel) => {
|
|
2501
|
+
if (panel !== "sort") ensureFullCatalog();
|
|
2393
2502
|
if (isMobile) {
|
|
2394
2503
|
setMobileSubView(panel);
|
|
2395
2504
|
} else {
|
|
@@ -2462,6 +2571,10 @@ function AgentListPanelBody({
|
|
|
2462
2571
|
defaultRow,
|
|
2463
2572
|
agentFilterLabel,
|
|
2464
2573
|
onClearAgentFilter: clearAgentFilter,
|
|
2574
|
+
hasMore,
|
|
2575
|
+
onReachEnd: loadMore,
|
|
2576
|
+
loadingMore,
|
|
2577
|
+
totalCount,
|
|
2465
2578
|
listOpen: true
|
|
2466
2579
|
}
|
|
2467
2580
|
);
|
|
@@ -2801,6 +2914,11 @@ function AgentListInlinePicker({
|
|
|
2801
2914
|
agentFilterLabel,
|
|
2802
2915
|
clearAgentFilter,
|
|
2803
2916
|
ensureLoaded,
|
|
2917
|
+
hasMore,
|
|
2918
|
+
loadMore,
|
|
2919
|
+
loadingMore,
|
|
2920
|
+
totalCount,
|
|
2921
|
+
ensureFullCatalog,
|
|
2804
2922
|
handleSelectAgent: coreSelectAgent,
|
|
2805
2923
|
handleAgentHover,
|
|
2806
2924
|
handleAgentHoverEnd
|
|
@@ -2830,6 +2948,7 @@ function AgentListInlinePicker({
|
|
|
2830
2948
|
setMobileSubView(null);
|
|
2831
2949
|
};
|
|
2832
2950
|
const handleFilterChipClick = (panel) => {
|
|
2951
|
+
if (panel !== "sort") ensureFullCatalog();
|
|
2833
2952
|
setMobileSubView(panel);
|
|
2834
2953
|
};
|
|
2835
2954
|
const isMobile = true;
|
|
@@ -2908,6 +3027,10 @@ function AgentListInlinePicker({
|
|
|
2908
3027
|
defaultRow,
|
|
2909
3028
|
agentFilterLabel,
|
|
2910
3029
|
onClearAgentFilter: clearAgentFilter,
|
|
3030
|
+
hasMore,
|
|
3031
|
+
onReachEnd: loadMore,
|
|
3032
|
+
loadingMore,
|
|
3033
|
+
totalCount,
|
|
2911
3034
|
listOpen: true
|
|
2912
3035
|
}
|
|
2913
3036
|
) });
|
|
@@ -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;
|