import * as react from 'react'; import { ComponentType, ReactNode } from 'react'; import { A as AgentSummary, a as AgentConsumerState, b as AgentConsumerPatch, c as AgentTab, d as AgentSortOption, e as AgentFavFilter, f as AgentArchFilter, g as AgentAccessFilter, O as OrchestraBadge } from '../../types-5pX8AZBM.js'; /** * `@ai-matrx/agents/catalog` — THE PORTS. * * Two are required (`client`, `identity`); every other one ships a working * default with a DOCUMENTED degradation, per THE ALL-INCLUSIVE LAW. A host * injects identity and a Supabase client and nothing else; the picker's only * contract back to the host is `onSelect(agentId)`. * * React appears nowhere in this file — it is import-inert and legal in any * graph, server components included. */ /** * Every degraded path, every failed read, every drift detection reports here. * DEFAULT: a tagged `console.error` that announces ONCE that no sink is bound * and names the remedy. */ type AgentCatalogErrorSink = (event: { code: string; message: string; context?: object; }) => void; /** * The drift scream's second channel. DEFAULT: none — and that is safe * ONLY because the in-picker persistent banner is the primary scream and * always renders. A drift is never console-only. */ type AgentCatalogNotifier = (event: { level: "warning" | "error"; title: string; message: string; }) => void; /** Every user-visible string the picker renders, overridable per host. */ interface AgentCatalogLabels { /** User-facing label for the `system` ownership tab. */ publicTab: string; /** * Badge label on a builtin row. Default `"system"` — the string * `AgentRow` actually rendered in matrx-frontend. (The frontend also * declared `AGENT_PUBLIC_BADGE_LABEL = "Public"` for this badge and NOTHING * ever read it — a constant that named the badge without being the badge. * Recorded in the CHANGELOG and dropped rather than ported.) */ systemBadge: string; /** * Badge on an archived row. Archived agents stay offered (THE ARCHIVED-ITEMS * LAW: one or two clicks away) and say so on the row itself. Default * `"Archived"`. */ archivedBadge: string; searchPlaceholder: string; currentAgentHeading: string; emptyDefault: string; emptyShared: string; emptySystem: string; /** Empty state for a signed-out visitor — there is no catalog to read. */ emptySignedOut: string; loading: string; triggerFallback: string; triggerResolving: string; triggerUnnameable: string; hoverHint: string; } /** * Where a row's href goes when someone cmd-clicks it, and where `navigateTo` * lands. DEFAULT: `window.location.assign` (so cmd-click and ordinary * navigation both work with no host wiring at all). */ type AgentCatalogNavigate = (href: string) => void; /** * The sneak-peek affordance. Absent → the affordance is HIDDEN, never a dead * button (nothing fails silently, and nothing looks alive when it is not). */ type AgentCatalogOpenPeek = (agent: AgentSummary) => void; /** * catalog/data.ts — @ai-matrx/agents/catalog * * Every database call the catalog makes, and nothing else. Five of them: * * 1. `agx_get_list_full()` — the complete catalog (owned + directly-shared * + org-shared user agents, unioned with active builtins). * 2. `agx_search(p_query, p_deep, p_limit, p_offset)` — tier-2 server search. * 3. `agx_resolve_agent_address(p_ids)` — the single-agent name read, used * when a mandate's Holder is not in the caller's own catalog. * 4. The caller's stars arrive ON the rows: every read above computes * `is_favorite` server-side from `platform.user_entity_state` * (`platform.my_favorite`). There is no second star read (FW-014; the * `ues_get_bulk` overlay over every id was retired 2026-10-23). * 5. ONE write: the caller's star, `ues_set` through the same port. The * retired `agent.definition.is_favorite` column is never read or written. * 6. THE PINNED READ (2026-10-09; by-ids 2026-10-23): the caller's starred * ids (`ues_list` through the favorites port), then * `agent.agx_get_by_ids(p_ids)` for exactly those ids. A sidebar's Pinned * section shows a handful of rows; it never pays for the whole catalog, * in the browser or on the server, to find them. * 7. THE PICKER PAGE (FW-014, 2026-10-23): `agent.agx_picker_page` — the * menu opens with ~50 thin rows (favorites, recently used, name) and the * tab counts; scrolling reads the next keyset page. `agent.agx_get_by_ids` * fills in a full row on hover/open. The whole-catalog read above remains * for surfaces that genuinely list everything, and for a picker whose * person narrows by category or tag (the facets need every row). * * 🚨 THE LIST READ IS COMPLETE OR IT THROWS. PostgREST caps every response at * `db-max-rows` (1000 on Matrx Main) and reports success — matrx-frontend's * `fetchAgentsListFull` issued a bare `.rpc()` and would silently have * returned 1000 rows of a larger catalog (the ~400-builtin truncation * incident is the near-miss). Here the read goes through `readAllRows` with * `{ count: "exact" }` and a stable `.order("id")`, so a truncation is a * loud `IncompleteReadError` instead of a confidently short picker. * The DB's own favourites-first/`updated_at` order is irrelevant: every row * is re-sorted client-side by `sortFilteredAgents`. */ /** Which slice of the caller's catalog a picker page reads. */ interface AgentPickerPageQuery { /** `all_system` = the admin "All" tab that blends system agents in. */ tab: "mine" | "shared" | "all" | "all_system" | "system"; archive: "active" | "archived" | "both"; includeQuick: boolean; } /** The tab badges, counted server-side over the same reach as the rows. */ interface AgentPickerCounts { mine: number; shared: number; all: number; system: number; /** Archived rows the active filter hides behind THIS tab (0 unless archive = active). */ archivedHidden: number; } /** * catalog/default-row-types.ts — @ai-matrx/agents/catalog * * Split out of `default-row.ts` so `global-state.ts` can name the cached * shape without importing the resolver (and its transport work) — the same * split-out reflex C8 asks for, at module scale. */ /** A resolved default row, cached for first paint and compared for drift. */ interface ResolvedDefaultRow { mandateKey: string; /** The `agent.definition` id the mandate resolved to. */ holderId: string; /** The holder's REAL name, read from the catalog — never hardcoded. */ holderName: string; holderDescription: string | null; provenance: string; /** Epoch ms of this resolution. */ at: number; } /** What the picker renders and what it screams, together. */ interface DefaultRowState { mandateKey: string; /** The row to render at the top of the list. `null` until resolved. */ row: AgentSummary | null; /** The last live resolution, or the cached one before the live answer lands. */ resolved: ResolvedDefaultRow | null; /** True while the resolution is in flight. */ loading: boolean; /** * 🚨 THE DRIFT SCREAM. Set when the cached first-paint row and the LIVE * resolution disagree on holder id or holder name. Rendered as a persistent * in-picker banner AND sent to the `notifier` port — never console-only. */ drift: string | null; /** Set when the mandate could not be resolved at all. Loud, with a remedy. */ error: string | null; } /** * catalog/schema.ts — @ai-matrx/agents/catalog * * THE DEMANDED SCHEMA PROBE (the `@ai-matrx/associations` `assertDemandedSchema` * precedent). It proves that the database a host pointed this catalog at can * actually answer the functions the picker demands — the three `agx_*` reads * and `ues_get_bulk`, the star read (`platform.user_entity_state`). * * 🚨 IT MUST BE ABLE TO FAIL. A probe whose green result is unconditional * proves nothing, so `selfTest: true` also probes a fabricated function name * and REQUIRES it to come back missing. `catalog/__tests__/schema.test.ts` * runs the whole probe against a client whose `rpc` answers PostgreSQL 42883 * (`undefined_function`, which PostgREST surfaces as PGRST202) and asserts the * loud error. */ interface AgentCatalogSchemaReport { ok: boolean; /** Demanded functions the database cannot answer. */ missing: string[]; /** Functions the probe could not reach (transport/unknown failure). */ unreachable: { fn: string; error: unknown; }[]; /** Functions that answered (success or any non-missing refusal). */ answered: string[]; /** * Whether `agx_get_list_full` carries the `orchestra` badge column (added to * the platform 2026-09-08). * * NOT a violation when absent — the picker degrades to no conductor marker, * which is honest — but it is never SILENT either: a `false` here is the one * signal that tells a host "this database predates the badge" instead of * leaving someone to wonder why their Orchestra looks like a plain agent. * `null` means the probe could not tell (the caller can see no agents at * all), never "fine". */ orchestraColumn: boolean | null; } interface AssertAgentCatalogSchemaOptions { /** * Also probe a fabricated function name and REQUIRE it to come back * missing — proves this probe can fail. */ selfTest?: boolean; /** Return the report instead of throwing. The self-test failure ALWAYS throws. */ throwOnViolation?: boolean; } /** * catalog/store.ts — @ai-matrx/agents/catalog * * `createAgentCatalog(config)` — the headless kernel. Everything the picker * knows lives here: the row registry, the complete-list read with its * session-shared in-flight dedupe and freshness window, the tier-2 server * search, the per-consumer view state, the ONE write, and the mandate default * row with its drift screamer. * * The host injects a Supabase client and who the user is. Nothing else. * * The consumer reducers below are a VERBATIM port of matrx-frontend * `features/agents/redux/agent-consumers/slice.ts`; only the seam inverts * (a Redux reducer becomes a store method, and Immer's draft mutation becomes * an explicit copy so the store can publish a new reference). */ type AgentCatalogStatus = "idle" | "loading" | "succeeded" | "failed"; /** One picker slice (tab × archive × quick) read a page at a time (FW-014). */ interface AgentPickerPageState { /** Row ids in server order across every page read so far. */ ids: string[]; /** Keyset position of the next page; `null` once the last page is in. */ nextCursor: unknown; done: boolean; status: AgentCatalogStatus; /** True while a next page (not the first) is in flight. */ loadingMore: boolean; error: string | null; counts: AgentPickerCounts | null; fetchedAt: number; } interface AgentCatalogState { /** Every row, one stable array reference per change (memoization depends on it). */ rows: AgentSummary[]; byId: Record; status: AgentCatalogStatus; error: string | null; consumers: Record; /** Keyed by mandate key. */ defaultRows: Record; /** * The caller's starred rows (`ensureFavoriteRows`): `succeeded` once every * starred agent the caller can see is in `byId` — by the narrow pinned read * or by a complete catalog load, whichever came first. */ favoriteRowsStatus: AgentCatalogStatus; /** The picker's paged slices, keyed by `agentPickerPageKey` (FW-014). */ pickerPages: Record; } interface AgentCatalog { readonly catalogId: string; readonly labels: AgentCatalogLabels; readonly errorSink: AgentCatalogErrorSink; readonly notifier: AgentCatalogNotifier | undefined; /** True when this catalog was given a transport and can resolve mandates. */ readonly canResolveMandates: boolean; /** `AgentCatalogConfig.pickerReads`, resolved (default `"page"`). */ readonly pickerReads: "page" | "catalog"; /** * The consumer state a brand-new consumer starts from, AFTER the host's * `defaults` knob is applied. Read it instead of * `DEFAULT_AGENT_CONSUMER_STATE` whenever the question is "is this filter at * its default?" — a host that legitimately defaults the archive filter to * "show all" must not have every control render as though it were modified. */ readonly consumerDefaults: AgentConsumerState; getState(): AgentCatalogState; subscribe(listener: () => void): () => void; getAgent(agentId: string): AgentSummary | undefined; /** * The signed-in user id, or `null` when there is no session. The ONE place * `identity.requireUserId()`'s throw is caught — a signed-out visitor * legitimately lands on the public catalog — and it is reported to the * errorSink the first time so it is never silent. */ getUserIdOrNull(): string | null; /** Load the catalog if it is not fresh. Safe to call on every mount. */ ensureLoaded(options?: { force?: boolean; }): Promise; /** * Load ONLY the caller's starred agents into the registry (the pinned read — * `ues_list` then `agx_get_list_full?id=in.(…)`), never the whole catalog. * Answered at once when a complete load is fresh. Deduped; safe on every * mount. For surfaces that show stars without being a picker (a sidebar's * Pinned section): nothing loads the full list until a picker opens. */ ensureFavoriteRows(): Promise; /** * Answer the pinned read from rows a batched route read already returned * (`agx_get_list_full` rows for the caller's starred agents — e.g. the * `chat.route_seed` first-paint read). Merges them like the pinned read * does and marks `favoriteRowsStatus` succeeded, so `ensureFavoriteRows` * makes no request. Rows that do not parse are skipped. */ seedFavoriteRows(rawRows: unknown[]): void; /** * THE PICKER'S OPEN (FW-014): the first page (~50 thin rows) of one slice, * plus its tab counts — never the whole catalog. Fresh for the list's TTL; * deduped in flight; safe on every open. */ ensurePickerPage(query: AgentPickerPageQuery): Promise; /** The next page of a slice (scroll). No-op when the slice is done or already loading. */ loadMorePickerPage(query: AgentPickerPageQuery): Promise; /** * FULL rows for these ids (`agent.agx_get_by_ids`), merged into the registry: * a hover card's details, the pinned current agent, a name for an id that is * not on page 1. Ids already held as full rows are not asked again; deduped * in flight. */ ensureAgentRows(agentIds: readonly string[]): Promise; /** * The caller's live agents carrying any of these tags, merged into the * registry (`agent.agx_picker_tagged`) — the composer's quick agents and * presets, never the catalog. Fresh for the list's TTL; deduped in flight; * answered at once when the whole catalog is already fresh. */ ensureTaggedRows(tags: readonly string[]): Promise; /** True when the last load is inside the 15-minute TTL. */ isFresh(): boolean; /** True when the last load is older than 4 hours (tab-restore threshold). */ isStale(): boolean; getConsumer(consumerId: string): AgentConsumerState; registerConsumer(consumerId: string, initial?: AgentConsumerPatch): void; unregisterConsumer(consumerId: string): void; setConsumerFilter(consumerId: string, patch: AgentConsumerPatch): void; setConsumerPage(consumerId: string, which: "list" | "shared", page: number): void; setConsumerServerSearch(consumerId: string, args: { matchedIds?: string[]; isSearching?: boolean; }): void; resetConsumerFilters(consumerId: string): void; /** Tier-2 search. Purely additive; merges rows into the registry. */ searchServer(query: string, deep?: boolean): Promise; /** THE ONE WRITE. Optimistic, rolled back on failure, loud either way. */ setFavorite(agentId: string, isFavorite: boolean): Promise; /** * Reflect a write the HOST made elsewhere (a rename, a description, an * archive) into the held row, so every list reading the catalog shows it at * once. Returns the row as it was before — pass it back to `restoreAgent` to * roll the change back when the host's write fails. `undefined` = the row is * not held, and nothing changed. */ patchAgent(agentId: string, patch: Partial>): AgentSummary | undefined; /** Put a row back exactly as `patchAgent` returned it (a failed write's rollback). */ restoreAgent(row: AgentSummary): void; /** Resolve (once) the default row for a mandate key. Idempotent. */ ensureDefaultRow(mandateKey: string): void; getDefaultRow(mandateKey: string): DefaultRowState | undefined; /** A person acknowledged the drift banner. The notifier already fired. */ dismissDefaultRowDrift(mandateKey: string): void; /** Re-ask the mandate door for a row that refused (see ensureDefaultRow). */ retryDefaultRow(mandateKey: string): void; assertSchema(options?: AssertAgentCatalogSchemaOptions): Promise; } /** * The anchor a row renders for cmd/ctrl-click. DEFAULT (web): a plain `` — * which is a REAL, working link in every framework. A Next.js host passes * `next/link`; a TanStack host passes its own `Link`. On React Native the * default is a pressable `Text` that routes through `navigate`. */ /** * What a row's link handler receives. A DOM `React.MouseEvent` satisfies this * structurally, and the native default link supplies the two methods itself — * so a shared component can call `preventDefault()` on either platform * without a platform check. */ interface AgentCatalogLinkClickEvent { metaKey?: boolean; ctrlKey?: boolean; preventDefault(): void; stopPropagation(): void; } type AgentCatalogLinkComponent = ComponentType<{ href: string; className?: string; target?: string; rel?: string; title?: string; "aria-label"?: string; onClick?: (e: AgentCatalogLinkClickEvent) => void; children: ReactNode; }>; /** * Render a model id as whatever door the host has. DEFAULT: plain text. * `offeringId` is the agent's class pin when the row carries it (`null` = no * pin, the preferred class runs); ABSENT = unknown — name the model alone. */ type AgentCatalogRenderModelRef = (args: { modelId: string; offeringId?: string | null; className?: string; }) => ReactNode; interface AgentCatalogUiPorts { /** DEFAULT (web): `window.location.assign`. DEFAULT (native): a reporting no-op. */ navigate?: AgentCatalogNavigate; /** DEFAULT: a plain `` (web) / a pressable `Text` (native). */ LinkComponent?: AgentCatalogLinkComponent; /** DEFAULT: the model id as plain text. */ renderModelRef?: AgentCatalogRenderModelRef; /** ABSENT: the sneak-peek affordance is HIDDEN — never a dead button. */ openPeek?: AgentCatalogOpenPeek; /** * Portal container for the desktop popover, when the picker is mounted * inside a dialog. DEFAULT: the document body (Radix's own default). * Web only — the native entry ignores it. */ popoverContainer?: HTMLElement | null; } /** * The three defaults a PLATFORM supplies. Never a host concern: a host * overrides an individual port through `AgentCatalogUiPorts`, and whatever it * leaves unset falls back to the platform's own working answer. */ interface AgentCatalogPlatformDefaults { navigate: AgentCatalogNavigate; LinkComponent: AgentCatalogLinkComponent; renderModelRef: AgentCatalogRenderModelRef; } /** The headless catalog this tree is bound to. Throws with no provider. */ declare function useAgentCatalog(): AgentCatalog; /** The UI ports this tree is bound to, with every default already applied. */ declare function useAgentCatalogUiPorts(): Required> & { openPeek: AgentCatalogOpenPeek | undefined; popoverContainer: HTMLElement | null | undefined; }; /** * Subscribe to a slice of catalog state. `useSyncExternalStore` because the * catalog is an external store and React 18/19 concurrency must not tear it. * `select` MUST return a stable reference for unchanged state — every * selector in `../selectors` does. */ declare function useAgentCatalogState(select: (state: AgentCatalogState) => T): T; /** Every row currently in the catalog, as one stable array. */ declare function useAgentCatalogRows(): AgentSummary[]; /** * catalog/native/theme.ts — @ai-matrx/agents/catalog/native * * 🚨 C26 ON A PLATFORM WITH NO CSS. The web picker consumes color ONLY * through named semantic tokens (`--matrx-agent-*` plus the design system's * `--primary`, `--muted-foreground`, …), and `catalog/styles.css` ships their * DEFAULT VALUES so the picker renders correctly unthemed in a fresh app. * React Native has no custom properties and no cascade, so the identical * contract is expressed as a plain object: the package ships every default * value, and a host re-brands by passing `theme` to `AgentCatalogProvider`. * * Every name below is the native twin of a token the web sheet defines, so a * color changed in one place is findable in the other. NOTHING in the native * components hardcodes a color — they read this object and nothing else. */ /** The color half of the contract. Every value is host-overridable. */ interface AgentCatalogNativeColors { /** Sheet / list background. Web twin: `--background`. */ background: string; /** Cards, inputs and chips sitting on the background. Web twin: `--muted`. */ surface: string; /** A pressed/hovered row. Web twin: `bg-muted/50`. */ surfacePressed: string; /** Primary text. Web twin: `--foreground`. */ text: string; /** Secondary text. Web twin: `--muted-foreground`. */ textMuted: string; /** Tertiary text (timestamps, hints). Web twin: `--muted-foreground/70`. */ textFaint: string; /** Hairlines. Web twin: `--border`. */ border: string; /** The accent. Web twin: `--primary`. */ primary: string; /** Text ON the accent. Web twin: `--primary-foreground`. */ primaryForeground: string; /** A tinted accent surface (active tab, badges). Web twin: `bg-primary/10`. */ primarySoft: string; /** The favorite star. Web twin: `--matrx-agent-favorite`. */ favorite: string; /** The drift banner. Web twins: `--matrx-agent-drift-{bg,fg,border}`. */ driftBackground: string; driftForeground: string; driftBorder: string; } interface AgentCatalogNativeTheme { colors: AgentCatalogNativeColors; /** Corner radii, in points. */ radius: { sm: number; md: number; lg: number; }; /** The one spacing scale the picker uses, in points. */ spacing: { xs: number; sm: number; md: number; lg: number; }; /** Font sizes, in points. The web picker's 10/11/12/13px ladder. */ fontSize: { xs: number; sm: number; md: number; lg: number; }; } /** * The DEFAULT theme, mirroring `catalog/styles.css` plus the dark palette the * web picker inherits from the design system. Dark by default is deliberate: * it is what every Matrx mobile surface renders, and a light host overrides * `theme` rather than fighting a half-applied palette. */ declare const DEFAULT_AGENT_CATALOG_NATIVE_THEME: AgentCatalogNativeTheme; /** * The light default, offered so a light host is one prop away rather than a * fifteen-value object away. Values mirror `catalog/styles.css`'s `:root`. */ declare const LIGHT_AGENT_CATALOG_NATIVE_THEME: AgentCatalogNativeTheme; /** A host may override any subset; everything it omits keeps its default. */ type AgentCatalogNativeThemeOverride = { colors?: Partial; radius?: Partial; spacing?: Partial; fontSize?: Partial; }; /** Merge a host override onto a base theme. Pure; never mutates either. */ declare function resolveAgentCatalogNativeTheme(override?: AgentCatalogNativeThemeOverride, base?: AgentCatalogNativeTheme): AgentCatalogNativeTheme; /** The resolved theme for this tree. Never throws — there is always a default. */ declare function useAgentCatalogNativeTheme(): AgentCatalogNativeTheme; interface AgentCatalogNativeProviderProps extends AgentCatalogUiPorts { catalog: AgentCatalog; /** * Brand overrides for the picker's token values (C26). Anything omitted * keeps the shipped default, so an unthemed app still renders correctly. */ theme?: AgentCatalogNativeThemeOverride; children: ReactNode; } declare function AgentCatalogProvider({ catalog, theme, children, ...ports }: AgentCatalogNativeProviderProps): react.JSX.Element; interface AgentPickerProps { /** Unique consumer id for filter/tab state (see `useAgentConsumer`). */ consumerId: string; /** THE ONLY CONTRACT BACK TO THE HOST (ruling D1). */ onSelect: (agentId: string) => void; activeAgentId?: string | null; /** Focus the search field on mount. Default false on native — a keyboard * that opens with the sheet covers the list it was meant to filter. */ autoFocusSearch?: boolean; /** Tab to open on. Admin surfaces that manage system agents pass `"system"`. */ initialTab?: AgentTab; /** ADMIN variant: blend system agents into the "All" tab. */ includeSystemInAll?: boolean; /** Restrict which ownership tabs this call site may reach. Enforced, not decorated. */ visibleTabs?: readonly AgentTab[]; /** Label override for the builtin-agent tab. Admin surfaces use "System". */ systemTabLabel?: string; /** Show the host's active agent above the filtered list. Default true. */ showPinnedAgent?: boolean; /** Hide records that are invalid for this call site. */ excludeAgentIds?: readonly string[]; /** Offer the mandate's live-resolved default agent at the top of the list. */ defaultMandateKey?: string; /** Compact spacing for a short sheet. */ compact?: boolean; /** Called after a selection, so a sheet shell can close itself. */ onAfterSelect?: () => void; } declare function AgentPickerBody({ consumerId, onSelect, activeAgentId, autoFocusSearch, initialTab, includeSystemInAll, visibleTabs, systemTabLabel, showPinnedAgent, excludeAgentIds, defaultMandateKey, compact, onAfterSelect, }: AgentPickerProps): react.JSX.Element; interface AgentListSheetProps extends AgentPickerProps { /** * Heading above the picker. Omit it when the host's sheet chrome already * prints one — two titles is worse than none. */ title?: string; } declare function AgentListSheet({ title, ...props }: AgentListSheetProps): react.JSX.Element; type AgentListInlinePickerProps = AgentPickerProps; declare function AgentListInlinePicker(props: AgentListInlinePickerProps): react.JSX.Element; interface UseAgentConsumerReturn { tab: AgentTab; sortBy: AgentSortOption; searchTerm: string; includedCats: string[]; includedTags: string[]; favFilter: AgentFavFilter; archFilter: AgentArchFilter; /** * The archive-filter state this catalog starts consumers in — the platform * default `"active"` unless the host set the knob. What "not the default * state" means on this surface, so the control and the footer can both say * it honestly. */ archFilterDefault: AgentArchFilter; accessFilter: AgentAccessFilter; favoritesFirst: boolean; listPage: number; sharedPage: number; /** The whole slot, for the selectors that take a consumer state. */ state: AgentConsumerState; /** True if any filter differs from its default value. */ hasActiveFilters: boolean; /** Tier 2 search — also match against agent prompt content. */ deepSearch: boolean; /** True while a server-side search is in flight. */ isServerSearching: boolean; setSearchTerm: (value: string) => void; setDeepSearch: (value: boolean) => void; setSortBy: (value: AgentSortOption) => void; setTab: (value: AgentTab) => void; setFavFilter: (value: AgentFavFilter) => void; setArchFilter: (value: AgentArchFilter) => void; setAccessFilter: (value: AgentAccessFilter) => void; /** Add category to inclusion set; if already present, remove (toggle). */ toggleCategory: (cat: string) => void; /** Add tag to inclusion set; if already present, remove (toggle). */ toggleTag: (tag: string) => void; /** * Replace the WHOLE category inclusion set in one write. * * Prefer this over looping `toggleCategory` whenever more than one value * changes. Each `toggleCategory` call computes its next array from the * `includedCats` captured in the CURRENT render, so two toggles fired in the * same tick both start from the same snapshot and the second one's patch * overwrites the first — a "Clear filter" click with three categories active * removes only one. One write of the final set cannot drift that way. */ setIncludedCats: (next: string[]) => void; /** Replace the WHOLE tag inclusion set in one write. See setIncludedCats. */ setIncludedTags: (next: string[]) => void; toggleFavoritesFirst: () => void; loadMoreList: () => void; loadMoreShared: () => void; resetFilters: () => void; } declare function useAgentConsumer(consumerId: string, options?: { /** Delete the slot on unmount. Default false — persistent consumers keep state. */ unregisterOnUnmount?: boolean; /** Applied only when the consumer is first registered (idempotent). */ initialTab?: AgentTab; /** * THE ARCHIVED-ITEMS LAW's per-consumer override of the catalog-wide * `defaults.archiveFilter` knob. Applied only at first registration — the * state belongs to the person after that. */ initialArchFilter?: AgentArchFilter; }): UseAgentConsumerReturn; declare function useAgentListCore({ consumerId, onSelect, navigateTo, activeAgentIdOverride, initialTab, includeSystemInAll, visibleTabs, initialArchFilter, excludeAgentIds, includeQuickAgents, defaultMandateKey, agentFilter, loadOnMount, }: AgentListCoreOptions): { agents: AgentSummary[]; archivedHiddenCount: number; footerLabel: string; isLoading: boolean; activeAgentId: string | null; pinnedAgent: AgentSummary | null; defaultRow: DefaultRowState | null; /** The host filter's chip label while it narrows the list; else null. */ agentFilterLabel: string | null; clearAgentFilter: () => void; resumeAgentFilter: () => void; allCategories: string[]; allTags: string[]; consumer: UseAgentConsumerReturn; tabCounts: AgentListTabCounts; activeFilterCount: number; hoveredAgent: AgentSummary | null; setHoveredAgent: react.Dispatch>; ensureLoaded: () => void; /** FW-014: the list reads a page at a time; true while the slice has more. */ paged: boolean; hasMore: boolean; loadingMore: boolean; loadMore: () => void; /** The tab's total (server count when paged) — the footer's number. */ totalCount: number; ensureFullCatalog: () => void; handleSelectAgent: (agent: AgentSummary) => void; handleAgentHover: (agent: AgentSummary, panelOpen: boolean) => void; handleAgentHoverEnd: (agent: AgentSummary, onClear: () => void) => void; handleDetailPanelMouseEnter: () => void; handleDetailPanelMouseLeave: (onClear: () => void) => void; }; /** * The four ownership counts a tab strip prints. Declared HERE, beside the code * that computes them, so both the web tab strip and the native one name the * same shape (0.9.0 — it used to live in the web-only `AgentListTabs`). * * 🚨 These are the numbers on the BADGES, and since 0.9.1 they count exactly * the rows the tab renders under the consumer's own archive filter. A badge * that counted rows the list hid was the screen telling the user something * untrue about her own agents. */ interface AgentListTabCounts { mine: number; shared: number; all: number; system: number; } interface AgentListCoreOptions { consumerId: string; onSelect?: (agentId: string) => void; navigateTo?: string; /** Route-scoped agent id (chat/builder headers). `undefined` = nothing pinned. */ activeAgentIdOverride?: string | null; /** * Tab this surface opens on, applied once on first mount. Admin surfaces * that manage system agents pass `"system"`. When omitted the normal * user heuristic applies (a user with no agents of their own lands on the * public/system catalog). */ initialTab?: AgentTab; /** * ADMIN reading of the "All" tab: blend system agents into it instead of * keeping them to their own tab. See `makeSelectFilteredAgents`. */ includeSystemInAll?: boolean; /** * 🚨 THE OWNERSHIP TABS THIS CALL SITE MAY REACH — a HARD restriction, not a * default. * * `AgentListTabs` already hides the buttons this list omits, but hiding a * button never emptied the list behind it: the active tab lives in the * consumer's REMEMBERED state, and `initialTab` is applied exactly once per * mount (`defaultTabAppliedRef`). So a person who had been on "Mine" and * then moved a control that narrows the picker got the personal catalog * with the tab bar removed — the forbidden list, and no way to see that it * was forbidden. That is the defect Arman named on the mandate binding * screen's global rung (VISION-RECONCILIATION D2, 2026-08-31: the picker * opened on "Mine · 40"). * * Given here, the tab is COERCED into the allowed set on every change of it, * not just at mount — so a restriction that arrives later is enforced later. */ visibleTabs?: readonly AgentTab[]; /** * THE ARCHIVED-ITEMS LAW's per-consumer override of the catalog-wide * `defaults.archiveFilter` knob, applied once at first registration. Almost * nothing should pass it: the platform answer is "hide archived", and the * person reveals the archive from the control on the filter bar. */ initialArchFilter?: AgentArchFilter; /** Hide records that are invalid for this call site. */ excludeAgentIds?: readonly string[]; /** * Quick agents (`kind: quick`) are ordinary agents and DEFAULT ON: a list with no "My quick * agents" section must never silently drop a person's own agents. Only a picker that sits under * such a section (the composer pill's "Choose any agent") passes false, so nothing shows twice. */ includeQuickAgents?: boolean; /** * Show the platform DEFAULT agent for this mandate key at the top of the * list, resolved live through the server (never a hardcoded name). Requires * the catalog to have been built with a `transport`. */ defaultMandateKey?: string; /** * A HOST-SUPPLIED narrowing of the list (e.g. "agents whose model makes * images"). Never silent: every web presentation renders `label` as a chip * above the rows with a "Show all" control. The pinned current agent stays * visible whether or not it passes. "Show all" calls `onClear` when given * (the host clears its own source of truth); otherwise the filter is set * aside until `resumeAgentFilter()` (the dropdown calls it on every open). */ agentFilter?: AgentListFilter; /** * Load the roster when the hook mounts (DEFAULT true). A dropdown whose * closed trigger names nothing from the roster passes false: the list then * loads when it OPENS (its body calls `ensureLoaded`), never on page paint. */ loadOnMount?: boolean; } /** See `AgentListCoreOptions.agentFilter`. */ interface AgentListFilter { /** The chip's words, e.g. "Makes: Image". Keep it short — one line. */ label: string; /** `true` keeps the agent in the list. */ test: (agent: AgentSummary) => boolean; /** Called by "Show all". Omit to have the picker set the filter aside itself. */ onClear?: () => void; } /** * How long the hover-preview detail panel stays open after the cursor leaves * an agent row (or the panel itself) before it closes. Generous on purpose: * the panel renders to the side of the list, so the user needs ample time to * travel the cursor across the gap to reach it without it vanishing. Paired * with a reserved, non-resizing popover width so the target never moves. */ declare const HOVER_GRACE_MS = 900; /** * THE RULE, ALONE AND TESTABLE: which tab a restricted picker must land on. * * Returns `null` when the current tab is already allowed (nothing to do), and * otherwise the tab to switch to — `initialTab` when that is itself allowed, * else the first allowed tab. */ declare function coerceVisibleTab(current: AgentTab, allowed: readonly AgentTab[], initialTab?: AgentTab): AgentTab | null; interface AgentListContentProps { agents: AgentSummary[]; isLoading: boolean; consumer: UseAgentConsumerReturn; activeAgentId: string | null; allCategories: string[]; allTags: string[]; onSelectAgent: (agent: AgentSummary) => void; onDetailPress: (agent: AgentSummary) => void; onFilterChipPress: (panel: "sort" | "categories" | "tags") => void; onReset: () => void; activeFilterCount: number; tabCounts: AgentListTabCounts; visibleTabs?: readonly AgentTab[] | undefined; systemTabLabel?: string | undefined; pinnedAgent?: AgentSummary | null | undefined; defaultRow?: DefaultRowState | null | undefined; autoFocusSearch?: boolean; /** * Rows the ARCHIVE FILTER is keeping off this screen — from * `useAgentListCore`. The footer names them, exactly as on the web. */ archivedHiddenCount?: number | undefined; /** FW-014 paging: asked for the next page when the list nears its end. */ onReachEnd?: (() => void) | undefined; /** The tab's total for the footer (server count when paged). */ totalCount?: number | undefined; } declare function AgentListContent({ agents, isLoading, consumer, activeAgentId, allCategories, allTags, onSelectAgent, onDetailPress, onFilterChipPress, onReset, activeFilterCount, tabCounts, visibleTabs, systemTabLabel, pinnedAgent, defaultRow, autoFocusSearch, archivedHiddenCount, onReachEnd, totalCount, }: AgentListContentProps): react.JSX.Element; interface AgentListTabsProps { consumer: UseAgentConsumerReturn; tabCounts: AgentListTabCounts; visibleTabs?: readonly AgentTab[] | undefined; /** Label for the builtin tab. Users see "Public"; admin surfaces "System". */ systemTabLabel?: string | undefined; } declare function AgentListTabs({ consumer, tabCounts, visibleTabs, systemTabLabel, }: AgentListTabsProps): react.JSX.Element; interface AgentFilterBarProps { consumer: UseAgentConsumerReturn; allCategories: string[]; allTags: string[]; activeFilterCount: number; onFilterChipPress: (panel: "sort" | "categories" | "tags") => void; onReset: () => void; } declare function AgentFilterBar({ consumer, allCategories, allTags, activeFilterCount, onFilterChipPress, onReset, }: AgentFilterBarProps): react.JSX.Element; interface AgentRowProps { agent: AgentSummary; isActive: boolean; onPress: () => void; onDetailPress: () => void; } declare function AgentRow({ agent, isActive, onPress, onDetailPress, }: AgentRowProps): react.JSX.Element; interface AgentDetailCardProps { agent: AgentSummary; onSelect: () => void; } declare function AgentDetailCard({ agent: given, onSelect }: AgentDetailCardProps): react.JSX.Element; type AgentSubViewName = "sort" | "categories" | "tags"; interface AgentSubViewProps { view: AgentSubViewName; consumer: UseAgentConsumerReturn; allCategories: string[]; allTags: string[]; search: string; setSearch: (value: string) => void; onBack: () => void; } declare function AgentSubView({ view, consumer, allCategories, allTags, search, setSearch, onBack, }: AgentSubViewProps): react.JSX.Element; /** * catalog/native/FavoriteAgentButton.tsx — the native twin of * `catalog/react/FavoriteAgentButton.tsx`. Same one write * (`catalog.setFavorite` → `platform.user_entity_state`, optimistic and * rolled back by the store). A star is per-person, so every row is starrable. */ interface FavoriteAgentButtonProps { id: string; size?: number; disabled?: boolean; } declare function FavoriteAgentButton({ id, size, disabled, }: FavoriteAgentButtonProps): react.JSX.Element; declare const SHAPES: { star: { paths: string[]; fillable: true; }; chevronRight: { paths: string[]; }; chevronDown: { paths: string[]; }; search: { paths: string[]; circles: { cx: number; cy: number; r: number; }[]; }; x: { paths: string[]; }; check: { paths: string[]; }; arrowUpDown: { paths: string[]; }; folder: { paths: string[]; }; tag: { paths: string[]; circles: { cx: number; cy: number; r: number; filled: true; }[]; }; rotateCcw: { paths: string[]; }; loader: { paths: string[]; }; alertTriangle: { paths: string[]; }; /** * The Orchestra marker (Lucide `music-4`): the glyph the Workflow Studio's * picker used for a conductor, kept identical so the same agent wears the * same mark in every client. */ music4: { paths: string[]; circles: { cx: number; cy: number; r: number; }[]; }; clock: { paths: string[]; circles: { cx: number; cy: number; r: number; }[]; }; globe: { paths: string[]; circles: { cx: number; cy: number; r: number; }[]; }; users: { paths: string[]; circles: { cx: number; cy: number; r: number; }[]; }; externalLink: { paths: string[]; }; messageCircle: { paths: string[]; }; rocket: { paths: string[]; }; /** * THE ARCHIVE GLYPH (Lucide `archive`) — the ONE mark of the platform * archive filter, drawn identically in every Matrx client so the control a * person learns on one surface is recognizable on all of them. */ archive: { paths: string[]; }; circleCheck: { paths: string[]; circles: { cx: number; cy: number; r: number; }[]; }; }; type AgentCatalogIconName = keyof typeof SHAPES; /** The `react-native-svg` surface this package uses, structurally. */ interface SvgModuleLike { default: unknown; Svg?: unknown; Path: unknown; Circle: unknown; } /** Test seam: forget the probe so a test can exercise both paths. */ declare function _resetNativeIconState(svg?: SvgModuleLike | null): void; /** * Resolve `react-native-svg` once. Kept inside a function (not at module * scope) so importing this file never throws in an environment that has no * such module — an import-time throw would take the whole picker down instead * of degrading it. */ declare function resolveSvgModule(): SvgModuleLike | null; /** * The degradation announcement. Fires ONCE per process, with the remedy. * `report` is the catalog's own `errorSink` — the picker passes it down so * this file never reaches for a console of its own. */ declare function announceMissingSvgOnce(report: (event: { code: string; message: string; context?: object; }) => void): void; interface AgentIconProps { name: AgentCatalogIconName; /** Points. Default 14 — the web picker's `w-3.5`. */ size?: number; color: string; /** Paint the shape (the favorite star's "on" state). */ filled?: boolean; /** * Where the degradation is reported. REQUIRED so nothing here can fail * silently; every call site already holds the catalog's `errorSink`. */ report: (event: { code: string; message: string; context?: object; }) => void; } /** * ONE icon component. `name` picks the shape from the shared path table, so * the native picker can never wear a glyph the web picker does not have. */ declare function AgentIcon({ name, size, color, filled, report, }: AgentIconProps): react.JSX.Element; declare function CatalogSearchField({ value, onChange, placeholder, autoFocus, }: { value: string; onChange: (v: string) => void; placeholder: string; autoFocus?: boolean; }): react.JSX.Element; declare function FilterChip({ icon, label, active, onPress, accessibilityLabel, testID, }: { icon: AgentCatalogIconName; label: string; active: boolean; onPress: () => void; /** Accessible name when the visible label alone would not say the state. */ accessibilityLabel?: string; /** Stable hook so a shell test can measure the control, not a style. */ testID?: string; }): react.JSX.Element; declare function OptionRow({ label, selected, onPress, }: { label: string; selected: boolean; onPress: () => void; }): react.JSX.Element; declare function CheckRow({ label, checked, onPress, }: { label: string; checked: boolean; onPress: () => void; }): react.JSX.Element; declare function SidePanelHeader({ title, onClose, }: { title: string; onClose: () => void; }): react.JSX.Element; /** * catalog/orchestra.ts — @ai-matrx/agents/catalog * * Creator words for an Orchestra badge. A VERBATIM port of the helpers the * Workflow Studio's hand-rolled picker carried (`apps/workflow-studio/src/ * hooks/use-agents.ts` before `c0f958b43`), lifted here so every client says * the same sentence about the same ensemble. * * Pure — no React, no DOM. The row shape is `OrchestraBadge` in `./types`. */ declare function orchestraModeLabel(mode: string): string; /** Human label for a badge: "Orchestra · supervisor · 3 members" (singular-safe). */ declare function orchestraLabel(badge: OrchestraBadge): string; /** * The member-call ceiling in Creator language ("Team members can call helpers * up to 2 levels deep"), or `null` when the composition declared none. A badge * never invents the platform default: the server that runs the ensemble owns * that number, and printing a promise nobody made is worse than printing * nothing. */ declare function orchestraDepthLine(badge: OrchestraBadge): string | null; /** * One visible line naming who the Orchestra delegates to, collapsing past * `limit` into "+N more". `null` when no member carries a real role title — * "Member, Member, Member" is noise that reads like data. */ declare function orchestraDelegatesLine(badge: OrchestraBadge, limit?: number): string | null; export { AgentCatalogProvider, AgentDetailCard, AgentFilterBar, AgentIcon, AgentListContent, AgentListInlinePicker, AgentListSheet, AgentListTabs, AgentPickerBody, AgentRow, AgentSubView, CatalogSearchField, CheckRow, DEFAULT_AGENT_CATALOG_NATIVE_THEME, FavoriteAgentButton, FilterChip, HOVER_GRACE_MS, LIGHT_AGENT_CATALOG_NATIVE_THEME, OptionRow, OrchestraBadge, SidePanelHeader, _resetNativeIconState, announceMissingSvgOnce, coerceVisibleTab, orchestraDelegatesLine, orchestraDepthLine, orchestraLabel, orchestraModeLabel, resolveAgentCatalogNativeTheme, resolveSvgModule, useAgentCatalog, useAgentCatalogNativeTheme, useAgentCatalogRows, useAgentCatalogState, useAgentCatalogUiPorts, useAgentConsumer, useAgentListCore }; export type { AgentCatalogIconName, AgentCatalogLinkComponent, AgentCatalogNativeColors, AgentCatalogNativeProviderProps, AgentCatalogNativeTheme, AgentCatalogNativeThemeOverride, AgentCatalogPlatformDefaults, AgentCatalogRenderModelRef, AgentCatalogUiPorts, AgentDetailCardProps, AgentFilterBarProps, AgentIconProps, AgentListContentProps, AgentListCoreOptions, AgentListInlinePickerProps, AgentListSheetProps, AgentListTabCounts, AgentListTabsProps, AgentPickerProps, AgentRowProps, AgentSubViewName, AgentSubViewProps, FavoriteAgentButtonProps, UseAgentConsumerReturn };