@ai-matrx/agents 0.59.256 → 0.59.258

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.
@@ -42,6 +42,7 @@ __export(models_exports, {
42
42
  TOP_TIER_COST_RATING: () => TOP_TIER_COST_RATING,
43
43
  TOP_TIER_LOCKED_TITLE: () => TOP_TIER_LOCKED_TITLE,
44
44
  acceptsTextInstructions: () => acceptsTextInstructions,
45
+ announceModelCatalogChanged: () => announceModelCatalogChanged,
45
46
  blendedPrice: () => blendedPrice,
46
47
  buildModelClassIndex: () => buildModelClassIndex,
47
48
  buildSuccessionIndex: () => buildSuccessionIndex,
@@ -65,6 +66,7 @@ __export(models_exports, {
65
66
  fitsMemory: () => fitsMemory,
66
67
  fitsNeed: () => fitsNeed,
67
68
  fitsThisComputer: () => fitsThisComputer,
69
+ followModelCatalogChanges: () => followModelCatalogChanges,
68
70
  formatMemoryBytes: () => formatMemoryBytes,
69
71
  fromDiscovery: () => fromDiscovery,
70
72
  ggufEngine: () => ggufEngine,
@@ -82,21 +84,25 @@ __export(models_exports, {
82
84
  isListedVariant: () => isListedVariant,
83
85
  isLocalModelCatalog: () => isLocalModelCatalog,
84
86
  isManagedAgentModelCapability: () => isManagedAgentModelCapability,
87
+ isModelCatalogChange: () => isModelCatalogChange,
85
88
  isProjectorName: () => isProjectorName,
86
89
  isSupersedable: () => isSupersedable,
87
90
  isTopTierRating: () => isTopTierRating,
88
91
  judgeHeaderFacts: () => judgeHeaderFacts,
89
92
  mergeAuditRecordIntoCapabilities: () => mergeAuditRecordIntoCapabilities,
90
93
  missingShards: () => missingShards,
94
+ modelCatalogRevision: () => modelCatalogRevision,
91
95
  modelClassKnobFor: () => modelClassKnobFor,
92
96
  modelClassName: () => modelClassName,
93
97
  modelClassPinOf: () => modelClassPinOf,
98
+ modelDisplayLabel: () => modelDisplayLabel,
94
99
  modelsForSelectionPurpose: () => modelsForSelectionPurpose,
95
100
  normalizeArch: () => normalizeArch,
96
101
  normalizeModel: () => normalizeModel,
97
102
  normalizeModels: () => normalizeModels,
98
103
  normalizeOs: () => normalizeOs,
99
104
  normalizePromptSettings: () => normalizePromptSettings,
105
+ onModelCatalogChanged: () => onModelCatalogChanged,
100
106
  orderCatalogGroups: () => orderCatalogGroups,
101
107
  orderCatalogModels: () => orderCatalogModels,
102
108
  outputFormatControlKey: () => outputFormatControlKey,
@@ -108,6 +114,8 @@ __export(models_exports, {
108
114
  readCuratedRows: () => readCuratedRows,
109
115
  readGgufHeader: () => readGgufHeader,
110
116
  readModelCatalog: () => readModelCatalog,
117
+ readModelIdentities: () => readModelIdentities,
118
+ readPublicModel: () => readPublicModel,
111
119
  reconcileModelFavoriteIds: () => reconcileModelFavoriteIds,
112
120
  reportModelDiagnostic: () => reportModelDiagnostic,
113
121
  requireCanonicalCapabilities: () => requireCanonicalCapabilities,
@@ -918,6 +926,48 @@ var OUTPUT_MODALITIES = [
918
926
  "decision"
919
927
  ];
920
928
 
929
+ // models/catalog/changes.ts
930
+ var KEY2 = /* @__PURE__ */ Symbol.for("ai-matrx.agents.model-catalog-changes");
931
+ function hub() {
932
+ const g = globalThis;
933
+ return g[KEY2] ??= { listeners: /* @__PURE__ */ new Set(), revision: 0 };
934
+ }
935
+ function isModelCatalogChange(payload) {
936
+ const catalog = payload?.["catalog"];
937
+ return catalog === void 0 || catalog === null || catalog === "models";
938
+ }
939
+ function onModelCatalogChanged(listener) {
940
+ const h = hub();
941
+ h.listeners.add(listener);
942
+ return () => {
943
+ h.listeners.delete(listener);
944
+ };
945
+ }
946
+ function modelCatalogRevision() {
947
+ return hub().revision;
948
+ }
949
+ function announceModelCatalogChanged(payload = {}) {
950
+ if (!isModelCatalogChange(payload)) return;
951
+ const h = hub();
952
+ h.revision += 1;
953
+ for (const l of [...h.listeners]) {
954
+ try {
955
+ l(payload);
956
+ } catch (err) {
957
+ console.error("[@ai-matrx/agents/models] a catalog_changed listener threw", err);
958
+ }
959
+ }
960
+ }
961
+ function followModelCatalogChanges(subscribe) {
962
+ return subscribe("catalog_changed", (payload) => announceModelCatalogChanged(payload ?? {}));
963
+ }
964
+
965
+ // models/label.ts
966
+ function modelDisplayLabel(row) {
967
+ if (!row) return void 0;
968
+ return row.common_name || row.name || void 0;
969
+ }
970
+
921
971
  // models/catalog/records.ts
922
972
  var classConfigKey = (modelId, offeringId) => `${modelId}:${offeringId}`;
923
973
  var EMPTY_MODEL_RECORDS = Object.freeze({
@@ -986,7 +1036,7 @@ function withIdLists(state2) {
986
1036
  const active = [];
987
1037
  const deprecated = [];
988
1038
  for (const [id, record] of Object.entries(state2.entities)) (record.is_deprecated === true ? deprecated : active).push(id);
989
- const label = (id) => state2.entities[id]?.common_name ?? state2.entities[id]?.name ?? "";
1039
+ const label = (id) => modelDisplayLabel(state2.entities[id]) ?? "";
990
1040
  const sort = (ids) => ids.sort((a, b) => label(a).localeCompare(label(b)));
991
1041
  return { ...state2, activeIds: sort(active), deprecatedIds: sort(deprecated) };
992
1042
  }
@@ -1407,13 +1457,13 @@ function selectDeprecatedModels(s3) {
1407
1457
  var allOf = memo((entities) => Object.values(entities));
1408
1458
  var selectAllModels = (s3) => allOf(s3.entities);
1409
1459
  var optionsOf = memo(
1410
- (models) => models.map((m) => ({ value: m.id, label: m.common_name || m.name || m.id, maker: m.maker, costRating: m.cost_rating, speedRating: m.speed_rating }))
1460
+ (models) => models.map((m) => ({ value: m.id, label: modelDisplayLabel(m) || m.id, maker: m.maker, costRating: m.cost_rating, speedRating: m.speed_rating }))
1411
1461
  );
1412
1462
  var selectModelOptions = (s3) => optionsOf(selectActiveModels(s3));
1413
1463
  var allOptionsOf = memo(
1414
1464
  (entities) => Object.values(entities).map((m) => ({
1415
1465
  value: m.id,
1416
- label: m.common_name || m.name || m.id,
1466
+ label: modelDisplayLabel(m) || m.id,
1417
1467
  maker: m.maker,
1418
1468
  isDeprecated: m.is_deprecated ?? false,
1419
1469
  isRetired: m.retired_at != null,
@@ -1426,7 +1476,7 @@ function selectModelLabelById(s3, modelId) {
1426
1476
  if (!modelId) return void 0;
1427
1477
  const m = s3.entities[modelId];
1428
1478
  const identity = s3.identityById[modelId];
1429
- return m?.common_name || m?.name || identity?.common_name || identity?.name || void 0;
1479
+ return modelDisplayLabel(m) || modelDisplayLabel(identity);
1430
1480
  }
1431
1481
  var selectModelNameById = (s3, modelId) => modelId ? s3.entities[modelId]?.name : void 0;
1432
1482
  var selectModelClassName = (s3, modelId, offeringId) => modelClassName(s3.modelClassIndex, modelId, offeringId);
@@ -1444,6 +1494,18 @@ var selectDeprecatedModelsReady = (s3) => (s3.fetchScope === "deprecated" || s3.
1444
1494
  var selectAllModelsReady = (s3) => s3.fetchScope === "all";
1445
1495
  var selectModelIdentityById = (s3, modelId) => s3.identityById[modelId];
1446
1496
  var selectModelIdentityLookupStatus = (s3, modelId) => s3.identityStatusById[modelId] ?? "idle";
1497
+ async function readModelIdentities(client, modelIds) {
1498
+ const ids = [...new Set(modelIds.filter(Boolean))];
1499
+ if (ids.length === 0) return [];
1500
+ const { data, error } = await client.schema("ai").from("model_definition").select("id, name, common_name, is_deprecated").in("id", ids);
1501
+ if (error) throw new Error(error.message);
1502
+ return Array.isArray(data) ? data : [];
1503
+ }
1504
+ async function readPublicModel(client, key) {
1505
+ const { data, error } = await client.schema("ai").from("model_public").select("id, name").eq(catalogKeyColumn(key), key.trim()).maybeSingle();
1506
+ if (error) return { row: null, error: error.message };
1507
+ return { row: data ?? null, error: null };
1508
+ }
1447
1509
 
1448
1510
  // models/catalog/store.ts
1449
1511
  var IDLE = { status: "idle", models: [], error: null, loadedAt: null };
@@ -1713,8 +1775,16 @@ function createModelCatalog(config) {
1713
1775
  errorSink,
1714
1776
  ...config.isSignedIn ? { isSignedIn: config.isSignedIn } : {}
1715
1777
  });
1778
+ const refresh = async () => {
1779
+ const variants = Object.keys(state2).filter((v) => state2[v].status !== "idle");
1780
+ await Promise.all([records.refresh(), ...variants.map((v) => run(v))]);
1781
+ };
1782
+ const stopFollowing = config.followChanges === false ? () => {
1783
+ } : onModelCatalogChanged(() => void refresh());
1716
1784
  return {
1717
1785
  records,
1786
+ refresh,
1787
+ dispose: stopFollowing,
1718
1788
  getState: () => state2,
1719
1789
  subscribe(listener) {
1720
1790
  listeners.add(listener);
@@ -750,6 +750,41 @@ declare const selectDeprecatedModelsReady: (s: ModelRecordsState) => boolean;
750
750
  declare const selectAllModelsReady: (s: ModelRecordsState) => boolean;
751
751
  declare const selectModelIdentityById: (s: ModelRecordsState, modelId: string) => ModelIdentityRow | undefined;
752
752
  declare const selectModelIdentityLookupStatus: (s: ModelRecordsState, modelId: string) => "loading" | "succeeded" | "failed" | "idle";
753
+ /**
754
+ * The durable identities (id, name, common_name, is_deprecated) of these models, read from
755
+ * `ai.model_definition` — the same read `records.loadIdentity` makes. For a process with no records
756
+ * store that stamps a name ONCE (a sync snapshot); a screen names models through the records hooks.
757
+ * Rows the database does not return are absent. Throws the database's error.
758
+ */
759
+ declare function readModelIdentities(client: ModelRecordsClient, modelIds: readonly string[]): Promise<ModelIdentityRow[]>;
760
+ /** The structural read `readPublicModel` needs (a supabase-js client fits it as is). */
761
+ interface PublicModelClient {
762
+ schema(name: "ai"): {
763
+ from(table: "model_public"): {
764
+ select(columns: string): {
765
+ eq(column: string, value: string): {
766
+ maybeSingle(): PromiseLike<{
767
+ data: unknown;
768
+ error: {
769
+ message: string;
770
+ } | null;
771
+ }>;
772
+ };
773
+ };
774
+ };
775
+ };
776
+ }
777
+ /**
778
+ * One `ai.model_public` row (id + canonical name) by id or name, read fresh — for a value sent on the wire at
779
+ * the moment it is used (a voice session's transcription model), never for a label on screen.
780
+ */
781
+ declare function readPublicModel(client: PublicModelClient, key: string): Promise<{
782
+ row: {
783
+ id?: unknown;
784
+ name?: unknown;
785
+ } | null;
786
+ error: string | null;
787
+ }>;
753
788
 
754
789
  /**
755
790
  * `@ai-matrx/agents/models` — `createModelCatalog`: the headless model catalog store.
@@ -802,6 +837,8 @@ type ModelCatalogErrorSink = (event: {
802
837
  context?: object;
803
838
  }) => void;
804
839
  interface ModelCatalogConfig {
840
+ /** Re-read on every model catalog change notice (catalog/changes.ts). Default true. */
841
+ followChanges?: boolean;
805
842
  client: ModelCatalogClient;
806
843
  /** OPTIONAL — DEFAULT: a tagged `console.error` (the in-picker error banner always renders too). */
807
844
  errorSink?: ModelCatalogErrorSink;
@@ -828,6 +865,14 @@ interface ModelCatalog {
828
865
  load(variant: ModelCatalogVariant): Promise<void>;
829
866
  /** Re-read the variant now (rows stay visible while it runs). Never throws. */
830
867
  reload(variant: ModelCatalogVariant): Promise<void>;
868
+ /**
869
+ * The catalog changed upstream (`catalog_changed`): re-read every picker variant already read and every
870
+ * record already read (`records.refresh()`). Called by itself on every `announceModelCatalogChanged`
871
+ * (catalog/changes.ts) unless `followChanges: false`. Never throws.
872
+ */
873
+ refresh(): Promise<void>;
874
+ /** Stop following `catalog_changed` (a test, or a catalog a host discards). */
875
+ dispose(): void;
831
876
  /**
832
877
  * The RECORDS part (agent core B3): options list, full records with resolved controls, class
833
878
  * configs, the class index and identities — what chat's retired `modelRegistry` slice held.
@@ -1818,4 +1863,49 @@ catalog?: SuccessionClient): Promise<NewAgentModel>;
1818
1863
  */
1819
1864
  declare function resolveModelMapKnob(value: unknown, catalog: SuccessionClient): Promise<Record<string, string>>;
1820
1865
 
1821
- export { type AdminOffering, type AdminPricingBand, type ArtifactUse, type AuditCapabilitiesRecord, CHAT_DEFAULT_MODEL_KNOB, CONTENT_TYPES, type CapabilitiesParseContext, type CatalogKeyColumn, type CatalogKindInput, type CatalogModel, type CatalogTier, type CheapestAdequateOptions, type CompatStatus, type ContentType, type CuratedCatalogClient, type CuratedModel, type CuratedPresentation, type CuratedRows, type CuratedVariant, DEFAULT_CAPABILITIES, type DecidedBy, type DeviceGraphics, type DeviceHardware, type DiscoveryAmbiguity, type DiscoveryArtifact, type DiscoveryMedia, type DiscoveryResult, type DiscoveryRuntime, type DiscoverySource, type DiscoveryVariant, EMPTY_MODEL_RECORDS, EXCLUDED_REVIEW_STATES, FEATURE_BUCKETS, FEATURE_BUCKET_ORDER, FEATURE_KEYS, FILE_TYPE_QUANT, type FeatureBucket, type FeatureBucketMeta, type FeatureKey, type FitCapabilities, GgufError, type GgufHeader, type GgufRead, type GgufTensor, type GroupedFeatures, type HardwareClass, type HardwareClasses, type HeaderFlag, type HeaderJudgement, type HeaderRefusal, INPUT_MODALITIES, INTERACTION_MODES, type Interaction, type InteractionMode, type LocalArtifact, type LocalCatalogClient, type LocalCatalogQuery, type LocalCompatRow, type LocalModel, type LocalModelCatalog, type LocalPresentation, type LocalRuntime, type LocalRuntimeBuild, type LocalVariant, MAX_SUCCESSION_HOPS, MODEL_CATALOG_KINDS, MODEL_FAVORITE_ENTITY_TYPE, type Modality, type ModelCapabilities, type ModelCatalog, type ModelCatalogClient, type ModelCatalogConfig, type ModelCatalogErrorSink, type ModelCatalogPgError, type ModelCatalogPgResult, type ModelCatalogQuery, type ModelCatalogState, type ModelCatalogVariant, type ModelCatalogVariantState, type ModelClassConfig, type ModelClassEntry, type ModelClassIndex, type ModelDiagnosticEntry, type ModelDiagnosticsSink, type ModelFavorites, type ModelFavoritesCache, type ModelFavoritesClient, type ModelFavoritesConfig, type ModelFavoritesErrorSink, type ModelFavoritesState, type ModelFetchScope, type ModelFileHandle, type ModelFit, type ModelIdentityRow, type ModelJson, type ModelNeed, type ModelOfferingRow, type ModelOption, type ModelOptionWithLifecycle, type ModelRecord, type ModelRecordFetchType, type ModelRecords, type ModelRecordsClient, type ModelRecordsConfig, type ModelRecordsState, type ModelSelectionPurpose, type ModelSetEnv, type ModelSetFacts, type ModelSetValidation, NEW_AGENT_DEFAULT_MODEL_KNOB, type NewAgentModel, type NoSelection, OUTPUT_MODALITIES, type OrderedLike, type PreferredModelChoice, type PreferredModelChoicePorts, type PriceTier, type PricedCatalogModel, type RankOptions, type RankedVariant, type ReadModelCatalogOptions, SERVICE_LABEL, SERVICE_LABEL_PLURAL, type SelectDevice, type Selection, type ShardName, type SuccessionClient, type SuccessionIndex, type SuccessionNode, TOP_TIER_COST_RATING, TOP_TIER_LOCKED_TITLE, type VariantDiscovery, type VariantVerdict, type Want, acceptsTextInstructions, blendedPrice, buildModelClassIndex, buildSuccessionIndex, catalogFingerprint, catalogForApp, catalogKeyColumn, catalogKindOf, cheapestAdequateModel, classConfigKey, collapseTiersByClass, costRatingTier, costRatingWord, createModelCatalog, createModelFavorites, createModelRecords, curatedList, displayOrderOf, favoriteIdsEqual, fetchCuratedModels, findNonCanonicalCapabilityValues, fitsMemory, fitsNeed, fitsThisComputer, formatMemoryBytes, fromDiscovery, ggufEngine, ggufFacts, groupFeatures, hardwareClassesOf, hasCompatibleDecisionInteraction, isChatModelCapability, isContentType, isConversationalModelCapability, isDecisionModelCapability, isFeatureKey, isHelperGguf, isInteractionMode, isListedVariant, isLocalModelCatalog, isManagedAgentModelCapability, isProjectorName, isSupersedable, isTopTierRating, judgeHeaderFacts, mergeAuditRecordIntoCapabilities, missingShards, modelClassKnobFor, modelClassName, modelClassPinOf, modelsForSelectionPurpose, normalizeArch, normalizeModel, normalizeModels, normalizeOs, normalizePromptSettings, orderCatalogGroups, orderCatalogModels, outputFormatControlKey, parseCapabilities, pickProjector, priceTierRating, quantFromName, rankVariants, readCuratedRows, readGgufHeader, readModelCatalog, reconcileModelFavoriteIds, reportModelDiagnostic, requireCanonicalCapabilities, requiredArtifacts, resetSuccessionLogForTests, resolveLiveModelId, resolveModelMapKnob, resolveNewAgentModelId, resolvePreferredModelChoice, resolveSuccessor, runtimesOnEngine, seesImages, selectActiveModels, selectActiveModelsReady, selectAllModelOptions, selectAllModels, selectAllModelsReady, selectDeprecatedModels, selectDeprecatedModelsReady, selectModelById, selectModelClassConfig, selectModelClassName, selectModelControls, selectModelDetailError, selectModelFetchType, selectModelFullyLoaded, selectModelIdentityById, selectModelIdentityLookupStatus, selectModelLabelById, selectModelLabelWithClass, selectModelNameById, selectModelOptions, selectVariant, serviceCountLabel, setModelDiagnosticsSink, shapeCuratedModels, shardFileName, shardName, speedRatingLabel, speedRatingWord, splitSetProblems, takesInput, toAuditRecord, validateModelSet, variantVerdict, withModelClass };
1866
+ /**
1867
+ * `@ai-matrx/agents/models` — THE model label formula. Every surface that names a model computes the
1868
+ * name here: the person-facing `common_name`, else the canonical `name`. An empty `common_name` counts
1869
+ * as absent. Pure; the row it is handed must come from the subscribed catalog (records / readModelCatalog),
1870
+ * so a `catalog_changed` notice re-renders it. Guard: `scripts/check-model-catalog-reads.mjs`.
1871
+ */
1872
+ /** The two columns a model label is made from (cloud records, identities, local catalog rows). */
1873
+ interface ModelLabelSource {
1874
+ common_name?: string | null;
1875
+ name?: string | null;
1876
+ }
1877
+ /** A row that always has a canonical name always has a label. */
1878
+ declare function modelDisplayLabel(row: {
1879
+ common_name?: string | null;
1880
+ name: string;
1881
+ }): string;
1882
+ declare function modelDisplayLabel(row: ModelLabelSource | null | undefined): string | undefined;
1883
+
1884
+ /**
1885
+ * `@ai-matrx/agents/models` — the ONE subscription to `catalog_changed`.
1886
+ *
1887
+ * The server publishes `catalog_changed` on the platform bus for every admin edit and direct SQL write to
1888
+ * the model catalog. A host binds its bus client ONCE with `followModelCatalogChanges(subscribe)` (the web's
1889
+ * directive registry, Matrx 2's IPC forward of the main-process bus); every model catalog
1890
+ * (`createModelCatalog`: picker rows + records) re-reads itself on the notice, and any other reader
1891
+ * subscribes here (`onModelCatalogChanged`, or `useModelCatalogRevision` / `useOnModelCatalogChanged` in
1892
+ * `@ai-matrx/agents/models/react`). No surface keeps a model cache that misses the event.
1893
+ *
1894
+ * The hub lives on `globalThis` so two bundled copies of this module share one set of listeners.
1895
+ */
1896
+ /** A `catalog_changed` payload: `{ catalog?: "models" | ..., change?, model_id? }`. */
1897
+ type ModelCatalogChangePayload = Record<string, unknown>;
1898
+ /** The bus client's subscribe call: `(action, handler) => unsubscribe` (the web's `registerDirectiveHandler`). */
1899
+ type ModelCatalogDirectiveSubscribe = (action: "catalog_changed", handler: (payload: ModelCatalogChangePayload) => void) => () => void;
1900
+ /** Is this notice about the MODEL catalog? (`catalog` absent or `"models"`; the tool catalog shares the action.) */
1901
+ declare function isModelCatalogChange(payload: ModelCatalogChangePayload | null | undefined): boolean;
1902
+ /** Listen for model catalog changes. Returns the unsubscribe. */
1903
+ declare function onModelCatalogChanged(listener: (payload: ModelCatalogChangePayload) => void): () => void;
1904
+ /** How many model catalog changes this page has seen (a React key / effect dependency). */
1905
+ declare function modelCatalogRevision(): number;
1906
+ /** Announce a model catalog change to every listener (a notice that is not about models is ignored). */
1907
+ declare function announceModelCatalogChanged(payload?: ModelCatalogChangePayload): void;
1908
+ /** Bind the host's bus client: every `catalog_changed` it delivers reaches every model reader. Returns the unbind. */
1909
+ declare function followModelCatalogChanges(subscribe: ModelCatalogDirectiveSubscribe): () => void;
1910
+
1911
+ export { type AdminOffering, type AdminPricingBand, type ArtifactUse, type AuditCapabilitiesRecord, CHAT_DEFAULT_MODEL_KNOB, CONTENT_TYPES, type CapabilitiesParseContext, type CatalogKeyColumn, type CatalogKindInput, type CatalogModel, type CatalogTier, type CheapestAdequateOptions, type CompatStatus, type ContentType, type CuratedCatalogClient, type CuratedModel, type CuratedPresentation, type CuratedRows, type CuratedVariant, DEFAULT_CAPABILITIES, type DecidedBy, type DeviceGraphics, type DeviceHardware, type DiscoveryAmbiguity, type DiscoveryArtifact, type DiscoveryMedia, type DiscoveryResult, type DiscoveryRuntime, type DiscoverySource, type DiscoveryVariant, EMPTY_MODEL_RECORDS, EXCLUDED_REVIEW_STATES, FEATURE_BUCKETS, FEATURE_BUCKET_ORDER, FEATURE_KEYS, FILE_TYPE_QUANT, type FeatureBucket, type FeatureBucketMeta, type FeatureKey, type FitCapabilities, GgufError, type GgufHeader, type GgufRead, type GgufTensor, type GroupedFeatures, type HardwareClass, type HardwareClasses, type HeaderFlag, type HeaderJudgement, type HeaderRefusal, INPUT_MODALITIES, INTERACTION_MODES, type Interaction, type InteractionMode, type LocalArtifact, type LocalCatalogClient, type LocalCatalogQuery, type LocalCompatRow, type LocalModel, type LocalModelCatalog, type LocalPresentation, type LocalRuntime, type LocalRuntimeBuild, type LocalVariant, MAX_SUCCESSION_HOPS, MODEL_CATALOG_KINDS, MODEL_FAVORITE_ENTITY_TYPE, type Modality, type ModelCapabilities, type ModelCatalog, type ModelCatalogChangePayload, type ModelCatalogClient, type ModelCatalogConfig, type ModelCatalogDirectiveSubscribe, type ModelCatalogErrorSink, type ModelCatalogPgError, type ModelCatalogPgResult, type ModelCatalogQuery, type ModelCatalogState, type ModelCatalogVariant, type ModelCatalogVariantState, type ModelClassConfig, type ModelClassEntry, type ModelClassIndex, type ModelDiagnosticEntry, type ModelDiagnosticsSink, type ModelFavorites, type ModelFavoritesCache, type ModelFavoritesClient, type ModelFavoritesConfig, type ModelFavoritesErrorSink, type ModelFavoritesState, type ModelFetchScope, type ModelFileHandle, type ModelFit, type ModelIdentityRow, type ModelJson, type ModelLabelSource, type ModelNeed, type ModelOfferingRow, type ModelOption, type ModelOptionWithLifecycle, type ModelRecord, type ModelRecordFetchType, type ModelRecords, type ModelRecordsClient, type ModelRecordsConfig, type ModelRecordsState, type ModelSelectionPurpose, type ModelSetEnv, type ModelSetFacts, type ModelSetValidation, NEW_AGENT_DEFAULT_MODEL_KNOB, type NewAgentModel, type NoSelection, OUTPUT_MODALITIES, type OrderedLike, type PreferredModelChoice, type PreferredModelChoicePorts, type PriceTier, type PricedCatalogModel, type PublicModelClient, type RankOptions, type RankedVariant, type ReadModelCatalogOptions, SERVICE_LABEL, SERVICE_LABEL_PLURAL, type SelectDevice, type Selection, type ShardName, type SuccessionClient, type SuccessionIndex, type SuccessionNode, TOP_TIER_COST_RATING, TOP_TIER_LOCKED_TITLE, type VariantDiscovery, type VariantVerdict, type Want, acceptsTextInstructions, announceModelCatalogChanged, blendedPrice, buildModelClassIndex, buildSuccessionIndex, catalogFingerprint, catalogForApp, catalogKeyColumn, catalogKindOf, cheapestAdequateModel, classConfigKey, collapseTiersByClass, costRatingTier, costRatingWord, createModelCatalog, createModelFavorites, createModelRecords, curatedList, displayOrderOf, favoriteIdsEqual, fetchCuratedModels, findNonCanonicalCapabilityValues, fitsMemory, fitsNeed, fitsThisComputer, followModelCatalogChanges, formatMemoryBytes, fromDiscovery, ggufEngine, ggufFacts, groupFeatures, hardwareClassesOf, hasCompatibleDecisionInteraction, isChatModelCapability, isContentType, isConversationalModelCapability, isDecisionModelCapability, isFeatureKey, isHelperGguf, isInteractionMode, isListedVariant, isLocalModelCatalog, isManagedAgentModelCapability, isModelCatalogChange, isProjectorName, isSupersedable, isTopTierRating, judgeHeaderFacts, mergeAuditRecordIntoCapabilities, missingShards, modelCatalogRevision, modelClassKnobFor, modelClassName, modelClassPinOf, modelDisplayLabel, modelsForSelectionPurpose, normalizeArch, normalizeModel, normalizeModels, normalizeOs, normalizePromptSettings, onModelCatalogChanged, orderCatalogGroups, orderCatalogModels, outputFormatControlKey, parseCapabilities, pickProjector, priceTierRating, quantFromName, rankVariants, readCuratedRows, readGgufHeader, readModelCatalog, readModelIdentities, readPublicModel, reconcileModelFavoriteIds, reportModelDiagnostic, requireCanonicalCapabilities, requiredArtifacts, resetSuccessionLogForTests, resolveLiveModelId, resolveModelMapKnob, resolveNewAgentModelId, resolvePreferredModelChoice, resolveSuccessor, runtimesOnEngine, seesImages, selectActiveModels, selectActiveModelsReady, selectAllModelOptions, selectAllModels, selectAllModelsReady, selectDeprecatedModels, selectDeprecatedModelsReady, selectModelById, selectModelClassConfig, selectModelClassName, selectModelControls, selectModelDetailError, selectModelFetchType, selectModelFullyLoaded, selectModelIdentityById, selectModelIdentityLookupStatus, selectModelLabelById, selectModelLabelWithClass, selectModelNameById, selectModelOptions, selectVariant, serviceCountLabel, setModelDiagnosticsSink, shapeCuratedModels, shardFileName, shardName, speedRatingLabel, speedRatingWord, splitSetProblems, takesInput, toAuditRecord, validateModelSet, variantVerdict, withModelClass };
@@ -750,6 +750,41 @@ declare const selectDeprecatedModelsReady: (s: ModelRecordsState) => boolean;
750
750
  declare const selectAllModelsReady: (s: ModelRecordsState) => boolean;
751
751
  declare const selectModelIdentityById: (s: ModelRecordsState, modelId: string) => ModelIdentityRow | undefined;
752
752
  declare const selectModelIdentityLookupStatus: (s: ModelRecordsState, modelId: string) => "loading" | "succeeded" | "failed" | "idle";
753
+ /**
754
+ * The durable identities (id, name, common_name, is_deprecated) of these models, read from
755
+ * `ai.model_definition` — the same read `records.loadIdentity` makes. For a process with no records
756
+ * store that stamps a name ONCE (a sync snapshot); a screen names models through the records hooks.
757
+ * Rows the database does not return are absent. Throws the database's error.
758
+ */
759
+ declare function readModelIdentities(client: ModelRecordsClient, modelIds: readonly string[]): Promise<ModelIdentityRow[]>;
760
+ /** The structural read `readPublicModel` needs (a supabase-js client fits it as is). */
761
+ interface PublicModelClient {
762
+ schema(name: "ai"): {
763
+ from(table: "model_public"): {
764
+ select(columns: string): {
765
+ eq(column: string, value: string): {
766
+ maybeSingle(): PromiseLike<{
767
+ data: unknown;
768
+ error: {
769
+ message: string;
770
+ } | null;
771
+ }>;
772
+ };
773
+ };
774
+ };
775
+ };
776
+ }
777
+ /**
778
+ * One `ai.model_public` row (id + canonical name) by id or name, read fresh — for a value sent on the wire at
779
+ * the moment it is used (a voice session's transcription model), never for a label on screen.
780
+ */
781
+ declare function readPublicModel(client: PublicModelClient, key: string): Promise<{
782
+ row: {
783
+ id?: unknown;
784
+ name?: unknown;
785
+ } | null;
786
+ error: string | null;
787
+ }>;
753
788
 
754
789
  /**
755
790
  * `@ai-matrx/agents/models` — `createModelCatalog`: the headless model catalog store.
@@ -802,6 +837,8 @@ type ModelCatalogErrorSink = (event: {
802
837
  context?: object;
803
838
  }) => void;
804
839
  interface ModelCatalogConfig {
840
+ /** Re-read on every model catalog change notice (catalog/changes.ts). Default true. */
841
+ followChanges?: boolean;
805
842
  client: ModelCatalogClient;
806
843
  /** OPTIONAL — DEFAULT: a tagged `console.error` (the in-picker error banner always renders too). */
807
844
  errorSink?: ModelCatalogErrorSink;
@@ -828,6 +865,14 @@ interface ModelCatalog {
828
865
  load(variant: ModelCatalogVariant): Promise<void>;
829
866
  /** Re-read the variant now (rows stay visible while it runs). Never throws. */
830
867
  reload(variant: ModelCatalogVariant): Promise<void>;
868
+ /**
869
+ * The catalog changed upstream (`catalog_changed`): re-read every picker variant already read and every
870
+ * record already read (`records.refresh()`). Called by itself on every `announceModelCatalogChanged`
871
+ * (catalog/changes.ts) unless `followChanges: false`. Never throws.
872
+ */
873
+ refresh(): Promise<void>;
874
+ /** Stop following `catalog_changed` (a test, or a catalog a host discards). */
875
+ dispose(): void;
831
876
  /**
832
877
  * The RECORDS part (agent core B3): options list, full records with resolved controls, class
833
878
  * configs, the class index and identities — what chat's retired `modelRegistry` slice held.
@@ -1818,4 +1863,49 @@ catalog?: SuccessionClient): Promise<NewAgentModel>;
1818
1863
  */
1819
1864
  declare function resolveModelMapKnob(value: unknown, catalog: SuccessionClient): Promise<Record<string, string>>;
1820
1865
 
1821
- export { type AdminOffering, type AdminPricingBand, type ArtifactUse, type AuditCapabilitiesRecord, CHAT_DEFAULT_MODEL_KNOB, CONTENT_TYPES, type CapabilitiesParseContext, type CatalogKeyColumn, type CatalogKindInput, type CatalogModel, type CatalogTier, type CheapestAdequateOptions, type CompatStatus, type ContentType, type CuratedCatalogClient, type CuratedModel, type CuratedPresentation, type CuratedRows, type CuratedVariant, DEFAULT_CAPABILITIES, type DecidedBy, type DeviceGraphics, type DeviceHardware, type DiscoveryAmbiguity, type DiscoveryArtifact, type DiscoveryMedia, type DiscoveryResult, type DiscoveryRuntime, type DiscoverySource, type DiscoveryVariant, EMPTY_MODEL_RECORDS, EXCLUDED_REVIEW_STATES, FEATURE_BUCKETS, FEATURE_BUCKET_ORDER, FEATURE_KEYS, FILE_TYPE_QUANT, type FeatureBucket, type FeatureBucketMeta, type FeatureKey, type FitCapabilities, GgufError, type GgufHeader, type GgufRead, type GgufTensor, type GroupedFeatures, type HardwareClass, type HardwareClasses, type HeaderFlag, type HeaderJudgement, type HeaderRefusal, INPUT_MODALITIES, INTERACTION_MODES, type Interaction, type InteractionMode, type LocalArtifact, type LocalCatalogClient, type LocalCatalogQuery, type LocalCompatRow, type LocalModel, type LocalModelCatalog, type LocalPresentation, type LocalRuntime, type LocalRuntimeBuild, type LocalVariant, MAX_SUCCESSION_HOPS, MODEL_CATALOG_KINDS, MODEL_FAVORITE_ENTITY_TYPE, type Modality, type ModelCapabilities, type ModelCatalog, type ModelCatalogClient, type ModelCatalogConfig, type ModelCatalogErrorSink, type ModelCatalogPgError, type ModelCatalogPgResult, type ModelCatalogQuery, type ModelCatalogState, type ModelCatalogVariant, type ModelCatalogVariantState, type ModelClassConfig, type ModelClassEntry, type ModelClassIndex, type ModelDiagnosticEntry, type ModelDiagnosticsSink, type ModelFavorites, type ModelFavoritesCache, type ModelFavoritesClient, type ModelFavoritesConfig, type ModelFavoritesErrorSink, type ModelFavoritesState, type ModelFetchScope, type ModelFileHandle, type ModelFit, type ModelIdentityRow, type ModelJson, type ModelNeed, type ModelOfferingRow, type ModelOption, type ModelOptionWithLifecycle, type ModelRecord, type ModelRecordFetchType, type ModelRecords, type ModelRecordsClient, type ModelRecordsConfig, type ModelRecordsState, type ModelSelectionPurpose, type ModelSetEnv, type ModelSetFacts, type ModelSetValidation, NEW_AGENT_DEFAULT_MODEL_KNOB, type NewAgentModel, type NoSelection, OUTPUT_MODALITIES, type OrderedLike, type PreferredModelChoice, type PreferredModelChoicePorts, type PriceTier, type PricedCatalogModel, type RankOptions, type RankedVariant, type ReadModelCatalogOptions, SERVICE_LABEL, SERVICE_LABEL_PLURAL, type SelectDevice, type Selection, type ShardName, type SuccessionClient, type SuccessionIndex, type SuccessionNode, TOP_TIER_COST_RATING, TOP_TIER_LOCKED_TITLE, type VariantDiscovery, type VariantVerdict, type Want, acceptsTextInstructions, blendedPrice, buildModelClassIndex, buildSuccessionIndex, catalogFingerprint, catalogForApp, catalogKeyColumn, catalogKindOf, cheapestAdequateModel, classConfigKey, collapseTiersByClass, costRatingTier, costRatingWord, createModelCatalog, createModelFavorites, createModelRecords, curatedList, displayOrderOf, favoriteIdsEqual, fetchCuratedModels, findNonCanonicalCapabilityValues, fitsMemory, fitsNeed, fitsThisComputer, formatMemoryBytes, fromDiscovery, ggufEngine, ggufFacts, groupFeatures, hardwareClassesOf, hasCompatibleDecisionInteraction, isChatModelCapability, isContentType, isConversationalModelCapability, isDecisionModelCapability, isFeatureKey, isHelperGguf, isInteractionMode, isListedVariant, isLocalModelCatalog, isManagedAgentModelCapability, isProjectorName, isSupersedable, isTopTierRating, judgeHeaderFacts, mergeAuditRecordIntoCapabilities, missingShards, modelClassKnobFor, modelClassName, modelClassPinOf, modelsForSelectionPurpose, normalizeArch, normalizeModel, normalizeModels, normalizeOs, normalizePromptSettings, orderCatalogGroups, orderCatalogModels, outputFormatControlKey, parseCapabilities, pickProjector, priceTierRating, quantFromName, rankVariants, readCuratedRows, readGgufHeader, readModelCatalog, reconcileModelFavoriteIds, reportModelDiagnostic, requireCanonicalCapabilities, requiredArtifacts, resetSuccessionLogForTests, resolveLiveModelId, resolveModelMapKnob, resolveNewAgentModelId, resolvePreferredModelChoice, resolveSuccessor, runtimesOnEngine, seesImages, selectActiveModels, selectActiveModelsReady, selectAllModelOptions, selectAllModels, selectAllModelsReady, selectDeprecatedModels, selectDeprecatedModelsReady, selectModelById, selectModelClassConfig, selectModelClassName, selectModelControls, selectModelDetailError, selectModelFetchType, selectModelFullyLoaded, selectModelIdentityById, selectModelIdentityLookupStatus, selectModelLabelById, selectModelLabelWithClass, selectModelNameById, selectModelOptions, selectVariant, serviceCountLabel, setModelDiagnosticsSink, shapeCuratedModels, shardFileName, shardName, speedRatingLabel, speedRatingWord, splitSetProblems, takesInput, toAuditRecord, validateModelSet, variantVerdict, withModelClass };
1866
+ /**
1867
+ * `@ai-matrx/agents/models` — THE model label formula. Every surface that names a model computes the
1868
+ * name here: the person-facing `common_name`, else the canonical `name`. An empty `common_name` counts
1869
+ * as absent. Pure; the row it is handed must come from the subscribed catalog (records / readModelCatalog),
1870
+ * so a `catalog_changed` notice re-renders it. Guard: `scripts/check-model-catalog-reads.mjs`.
1871
+ */
1872
+ /** The two columns a model label is made from (cloud records, identities, local catalog rows). */
1873
+ interface ModelLabelSource {
1874
+ common_name?: string | null;
1875
+ name?: string | null;
1876
+ }
1877
+ /** A row that always has a canonical name always has a label. */
1878
+ declare function modelDisplayLabel(row: {
1879
+ common_name?: string | null;
1880
+ name: string;
1881
+ }): string;
1882
+ declare function modelDisplayLabel(row: ModelLabelSource | null | undefined): string | undefined;
1883
+
1884
+ /**
1885
+ * `@ai-matrx/agents/models` — the ONE subscription to `catalog_changed`.
1886
+ *
1887
+ * The server publishes `catalog_changed` on the platform bus for every admin edit and direct SQL write to
1888
+ * the model catalog. A host binds its bus client ONCE with `followModelCatalogChanges(subscribe)` (the web's
1889
+ * directive registry, Matrx 2's IPC forward of the main-process bus); every model catalog
1890
+ * (`createModelCatalog`: picker rows + records) re-reads itself on the notice, and any other reader
1891
+ * subscribes here (`onModelCatalogChanged`, or `useModelCatalogRevision` / `useOnModelCatalogChanged` in
1892
+ * `@ai-matrx/agents/models/react`). No surface keeps a model cache that misses the event.
1893
+ *
1894
+ * The hub lives on `globalThis` so two bundled copies of this module share one set of listeners.
1895
+ */
1896
+ /** A `catalog_changed` payload: `{ catalog?: "models" | ..., change?, model_id? }`. */
1897
+ type ModelCatalogChangePayload = Record<string, unknown>;
1898
+ /** The bus client's subscribe call: `(action, handler) => unsubscribe` (the web's `registerDirectiveHandler`). */
1899
+ type ModelCatalogDirectiveSubscribe = (action: "catalog_changed", handler: (payload: ModelCatalogChangePayload) => void) => () => void;
1900
+ /** Is this notice about the MODEL catalog? (`catalog` absent or `"models"`; the tool catalog shares the action.) */
1901
+ declare function isModelCatalogChange(payload: ModelCatalogChangePayload | null | undefined): boolean;
1902
+ /** Listen for model catalog changes. Returns the unsubscribe. */
1903
+ declare function onModelCatalogChanged(listener: (payload: ModelCatalogChangePayload) => void): () => void;
1904
+ /** How many model catalog changes this page has seen (a React key / effect dependency). */
1905
+ declare function modelCatalogRevision(): number;
1906
+ /** Announce a model catalog change to every listener (a notice that is not about models is ignored). */
1907
+ declare function announceModelCatalogChanged(payload?: ModelCatalogChangePayload): void;
1908
+ /** Bind the host's bus client: every `catalog_changed` it delivers reaches every model reader. Returns the unbind. */
1909
+ declare function followModelCatalogChanges(subscribe: ModelCatalogDirectiveSubscribe): () => void;
1910
+
1911
+ export { type AdminOffering, type AdminPricingBand, type ArtifactUse, type AuditCapabilitiesRecord, CHAT_DEFAULT_MODEL_KNOB, CONTENT_TYPES, type CapabilitiesParseContext, type CatalogKeyColumn, type CatalogKindInput, type CatalogModel, type CatalogTier, type CheapestAdequateOptions, type CompatStatus, type ContentType, type CuratedCatalogClient, type CuratedModel, type CuratedPresentation, type CuratedRows, type CuratedVariant, DEFAULT_CAPABILITIES, type DecidedBy, type DeviceGraphics, type DeviceHardware, type DiscoveryAmbiguity, type DiscoveryArtifact, type DiscoveryMedia, type DiscoveryResult, type DiscoveryRuntime, type DiscoverySource, type DiscoveryVariant, EMPTY_MODEL_RECORDS, EXCLUDED_REVIEW_STATES, FEATURE_BUCKETS, FEATURE_BUCKET_ORDER, FEATURE_KEYS, FILE_TYPE_QUANT, type FeatureBucket, type FeatureBucketMeta, type FeatureKey, type FitCapabilities, GgufError, type GgufHeader, type GgufRead, type GgufTensor, type GroupedFeatures, type HardwareClass, type HardwareClasses, type HeaderFlag, type HeaderJudgement, type HeaderRefusal, INPUT_MODALITIES, INTERACTION_MODES, type Interaction, type InteractionMode, type LocalArtifact, type LocalCatalogClient, type LocalCatalogQuery, type LocalCompatRow, type LocalModel, type LocalModelCatalog, type LocalPresentation, type LocalRuntime, type LocalRuntimeBuild, type LocalVariant, MAX_SUCCESSION_HOPS, MODEL_CATALOG_KINDS, MODEL_FAVORITE_ENTITY_TYPE, type Modality, type ModelCapabilities, type ModelCatalog, type ModelCatalogChangePayload, type ModelCatalogClient, type ModelCatalogConfig, type ModelCatalogDirectiveSubscribe, type ModelCatalogErrorSink, type ModelCatalogPgError, type ModelCatalogPgResult, type ModelCatalogQuery, type ModelCatalogState, type ModelCatalogVariant, type ModelCatalogVariantState, type ModelClassConfig, type ModelClassEntry, type ModelClassIndex, type ModelDiagnosticEntry, type ModelDiagnosticsSink, type ModelFavorites, type ModelFavoritesCache, type ModelFavoritesClient, type ModelFavoritesConfig, type ModelFavoritesErrorSink, type ModelFavoritesState, type ModelFetchScope, type ModelFileHandle, type ModelFit, type ModelIdentityRow, type ModelJson, type ModelLabelSource, type ModelNeed, type ModelOfferingRow, type ModelOption, type ModelOptionWithLifecycle, type ModelRecord, type ModelRecordFetchType, type ModelRecords, type ModelRecordsClient, type ModelRecordsConfig, type ModelRecordsState, type ModelSelectionPurpose, type ModelSetEnv, type ModelSetFacts, type ModelSetValidation, NEW_AGENT_DEFAULT_MODEL_KNOB, type NewAgentModel, type NoSelection, OUTPUT_MODALITIES, type OrderedLike, type PreferredModelChoice, type PreferredModelChoicePorts, type PriceTier, type PricedCatalogModel, type PublicModelClient, type RankOptions, type RankedVariant, type ReadModelCatalogOptions, SERVICE_LABEL, SERVICE_LABEL_PLURAL, type SelectDevice, type Selection, type ShardName, type SuccessionClient, type SuccessionIndex, type SuccessionNode, TOP_TIER_COST_RATING, TOP_TIER_LOCKED_TITLE, type VariantDiscovery, type VariantVerdict, type Want, acceptsTextInstructions, announceModelCatalogChanged, blendedPrice, buildModelClassIndex, buildSuccessionIndex, catalogFingerprint, catalogForApp, catalogKeyColumn, catalogKindOf, cheapestAdequateModel, classConfigKey, collapseTiersByClass, costRatingTier, costRatingWord, createModelCatalog, createModelFavorites, createModelRecords, curatedList, displayOrderOf, favoriteIdsEqual, fetchCuratedModels, findNonCanonicalCapabilityValues, fitsMemory, fitsNeed, fitsThisComputer, followModelCatalogChanges, formatMemoryBytes, fromDiscovery, ggufEngine, ggufFacts, groupFeatures, hardwareClassesOf, hasCompatibleDecisionInteraction, isChatModelCapability, isContentType, isConversationalModelCapability, isDecisionModelCapability, isFeatureKey, isHelperGguf, isInteractionMode, isListedVariant, isLocalModelCatalog, isManagedAgentModelCapability, isModelCatalogChange, isProjectorName, isSupersedable, isTopTierRating, judgeHeaderFacts, mergeAuditRecordIntoCapabilities, missingShards, modelCatalogRevision, modelClassKnobFor, modelClassName, modelClassPinOf, modelDisplayLabel, modelsForSelectionPurpose, normalizeArch, normalizeModel, normalizeModels, normalizeOs, normalizePromptSettings, onModelCatalogChanged, orderCatalogGroups, orderCatalogModels, outputFormatControlKey, parseCapabilities, pickProjector, priceTierRating, quantFromName, rankVariants, readCuratedRows, readGgufHeader, readModelCatalog, readModelIdentities, readPublicModel, reconcileModelFavoriteIds, reportModelDiagnostic, requireCanonicalCapabilities, requiredArtifacts, resetSuccessionLogForTests, resolveLiveModelId, resolveModelMapKnob, resolveNewAgentModelId, resolvePreferredModelChoice, resolveSuccessor, runtimesOnEngine, seesImages, selectActiveModels, selectActiveModelsReady, selectAllModelOptions, selectAllModels, selectAllModelsReady, selectDeprecatedModels, selectDeprecatedModelsReady, selectModelById, selectModelClassConfig, selectModelClassName, selectModelControls, selectModelDetailError, selectModelFetchType, selectModelFullyLoaded, selectModelIdentityById, selectModelIdentityLookupStatus, selectModelLabelById, selectModelLabelWithClass, selectModelNameById, selectModelOptions, selectVariant, serviceCountLabel, setModelDiagnosticsSink, shapeCuratedModels, shardFileName, shardName, speedRatingLabel, speedRatingWord, splitSetProblems, takesInput, toAuditRecord, validateModelSet, variantVerdict, withModelClass };
@@ -759,6 +759,48 @@ var OUTPUT_MODALITIES = [
759
759
  "decision"
760
760
  ];
761
761
 
762
+ // models/catalog/changes.ts
763
+ var KEY2 = /* @__PURE__ */ Symbol.for("ai-matrx.agents.model-catalog-changes");
764
+ function hub() {
765
+ const g = globalThis;
766
+ return g[KEY2] ??= { listeners: /* @__PURE__ */ new Set(), revision: 0 };
767
+ }
768
+ function isModelCatalogChange(payload) {
769
+ const catalog = payload?.["catalog"];
770
+ return catalog === void 0 || catalog === null || catalog === "models";
771
+ }
772
+ function onModelCatalogChanged(listener) {
773
+ const h = hub();
774
+ h.listeners.add(listener);
775
+ return () => {
776
+ h.listeners.delete(listener);
777
+ };
778
+ }
779
+ function modelCatalogRevision() {
780
+ return hub().revision;
781
+ }
782
+ function announceModelCatalogChanged(payload = {}) {
783
+ if (!isModelCatalogChange(payload)) return;
784
+ const h = hub();
785
+ h.revision += 1;
786
+ for (const l of [...h.listeners]) {
787
+ try {
788
+ l(payload);
789
+ } catch (err) {
790
+ console.error("[@ai-matrx/agents/models] a catalog_changed listener threw", err);
791
+ }
792
+ }
793
+ }
794
+ function followModelCatalogChanges(subscribe) {
795
+ return subscribe("catalog_changed", (payload) => announceModelCatalogChanged(payload ?? {}));
796
+ }
797
+
798
+ // models/label.ts
799
+ function modelDisplayLabel(row) {
800
+ if (!row) return void 0;
801
+ return row.common_name || row.name || void 0;
802
+ }
803
+
762
804
  // models/catalog/records.ts
763
805
  var classConfigKey = (modelId, offeringId) => `${modelId}:${offeringId}`;
764
806
  var EMPTY_MODEL_RECORDS = Object.freeze({
@@ -827,7 +869,7 @@ function withIdLists(state2) {
827
869
  const active = [];
828
870
  const deprecated = [];
829
871
  for (const [id, record] of Object.entries(state2.entities)) (record.is_deprecated === true ? deprecated : active).push(id);
830
- const label = (id) => state2.entities[id]?.common_name ?? state2.entities[id]?.name ?? "";
872
+ const label = (id) => modelDisplayLabel(state2.entities[id]) ?? "";
831
873
  const sort = (ids) => ids.sort((a, b) => label(a).localeCompare(label(b)));
832
874
  return { ...state2, activeIds: sort(active), deprecatedIds: sort(deprecated) };
833
875
  }
@@ -1248,13 +1290,13 @@ function selectDeprecatedModels(s3) {
1248
1290
  var allOf = memo((entities) => Object.values(entities));
1249
1291
  var selectAllModels = (s3) => allOf(s3.entities);
1250
1292
  var optionsOf = memo(
1251
- (models) => models.map((m) => ({ value: m.id, label: m.common_name || m.name || m.id, maker: m.maker, costRating: m.cost_rating, speedRating: m.speed_rating }))
1293
+ (models) => models.map((m) => ({ value: m.id, label: modelDisplayLabel(m) || m.id, maker: m.maker, costRating: m.cost_rating, speedRating: m.speed_rating }))
1252
1294
  );
1253
1295
  var selectModelOptions = (s3) => optionsOf(selectActiveModels(s3));
1254
1296
  var allOptionsOf = memo(
1255
1297
  (entities) => Object.values(entities).map((m) => ({
1256
1298
  value: m.id,
1257
- label: m.common_name || m.name || m.id,
1299
+ label: modelDisplayLabel(m) || m.id,
1258
1300
  maker: m.maker,
1259
1301
  isDeprecated: m.is_deprecated ?? false,
1260
1302
  isRetired: m.retired_at != null,
@@ -1267,7 +1309,7 @@ function selectModelLabelById(s3, modelId) {
1267
1309
  if (!modelId) return void 0;
1268
1310
  const m = s3.entities[modelId];
1269
1311
  const identity = s3.identityById[modelId];
1270
- return m?.common_name || m?.name || identity?.common_name || identity?.name || void 0;
1312
+ return modelDisplayLabel(m) || modelDisplayLabel(identity);
1271
1313
  }
1272
1314
  var selectModelNameById = (s3, modelId) => modelId ? s3.entities[modelId]?.name : void 0;
1273
1315
  var selectModelClassName = (s3, modelId, offeringId) => modelClassName(s3.modelClassIndex, modelId, offeringId);
@@ -1285,6 +1327,18 @@ var selectDeprecatedModelsReady = (s3) => (s3.fetchScope === "deprecated" || s3.
1285
1327
  var selectAllModelsReady = (s3) => s3.fetchScope === "all";
1286
1328
  var selectModelIdentityById = (s3, modelId) => s3.identityById[modelId];
1287
1329
  var selectModelIdentityLookupStatus = (s3, modelId) => s3.identityStatusById[modelId] ?? "idle";
1330
+ async function readModelIdentities(client, modelIds) {
1331
+ const ids = [...new Set(modelIds.filter(Boolean))];
1332
+ if (ids.length === 0) return [];
1333
+ const { data, error } = await client.schema("ai").from("model_definition").select("id, name, common_name, is_deprecated").in("id", ids);
1334
+ if (error) throw new Error(error.message);
1335
+ return Array.isArray(data) ? data : [];
1336
+ }
1337
+ async function readPublicModel(client, key) {
1338
+ const { data, error } = await client.schema("ai").from("model_public").select("id, name").eq(catalogKeyColumn(key), key.trim()).maybeSingle();
1339
+ if (error) return { row: null, error: error.message };
1340
+ return { row: data ?? null, error: null };
1341
+ }
1288
1342
 
1289
1343
  // models/catalog/store.ts
1290
1344
  var IDLE = { status: "idle", models: [], error: null, loadedAt: null };
@@ -1554,8 +1608,16 @@ function createModelCatalog(config) {
1554
1608
  errorSink,
1555
1609
  ...config.isSignedIn ? { isSignedIn: config.isSignedIn } : {}
1556
1610
  });
1611
+ const refresh = async () => {
1612
+ const variants = Object.keys(state2).filter((v) => state2[v].status !== "idle");
1613
+ await Promise.all([records.refresh(), ...variants.map((v) => run(v))]);
1614
+ };
1615
+ const stopFollowing = config.followChanges === false ? () => {
1616
+ } : onModelCatalogChanged(() => void refresh());
1557
1617
  return {
1558
1618
  records,
1619
+ refresh,
1620
+ dispose: stopFollowing,
1559
1621
  getState: () => state2,
1560
1622
  subscribe(listener) {
1561
1623
  listeners.add(listener);
@@ -2771,6 +2833,7 @@ export {
2771
2833
  TOP_TIER_COST_RATING,
2772
2834
  TOP_TIER_LOCKED_TITLE,
2773
2835
  acceptsTextInstructions,
2836
+ announceModelCatalogChanged,
2774
2837
  blendedPrice,
2775
2838
  buildModelClassIndex,
2776
2839
  buildSuccessionIndex,
@@ -2794,6 +2857,7 @@ export {
2794
2857
  fitsMemory,
2795
2858
  fitsNeed,
2796
2859
  fitsThisComputer,
2860
+ followModelCatalogChanges,
2797
2861
  formatMemoryBytes,
2798
2862
  fromDiscovery,
2799
2863
  ggufEngine,
@@ -2811,21 +2875,25 @@ export {
2811
2875
  isListedVariant,
2812
2876
  isLocalModelCatalog,
2813
2877
  isManagedAgentModelCapability,
2878
+ isModelCatalogChange,
2814
2879
  isProjectorName,
2815
2880
  isSupersedable,
2816
2881
  isTopTierRating,
2817
2882
  judgeHeaderFacts,
2818
2883
  mergeAuditRecordIntoCapabilities,
2819
2884
  missingShards,
2885
+ modelCatalogRevision,
2820
2886
  modelClassKnobFor,
2821
2887
  modelClassName,
2822
2888
  modelClassPinOf,
2889
+ modelDisplayLabel,
2823
2890
  modelsForSelectionPurpose,
2824
2891
  normalizeArch,
2825
2892
  normalizeModel,
2826
2893
  normalizeModels,
2827
2894
  normalizeOs,
2828
2895
  normalizePromptSettings,
2896
+ onModelCatalogChanged,
2829
2897
  orderCatalogGroups,
2830
2898
  orderCatalogModels,
2831
2899
  outputFormatControlKey,
@@ -2837,6 +2905,8 @@ export {
2837
2905
  readCuratedRows,
2838
2906
  readGgufHeader,
2839
2907
  readModelCatalog,
2908
+ readModelIdentities,
2909
+ readPublicModel,
2840
2910
  reconcileModelFavoriteIds,
2841
2911
  reportModelDiagnostic,
2842
2912
  requireCanonicalCapabilities,
@@ -29,10 +29,12 @@ __export(react_exports, {
29
29
  useLocalModels: () => useLocalModels,
30
30
  useModelAccess: () => useModelAccess,
31
31
  useModelCatalog: () => useModelCatalog,
32
+ useModelCatalogRevision: () => useModelCatalogRevision,
32
33
  useModelCatalogStore: () => useModelCatalogStore,
33
34
  useModelCatalogUiPorts: () => useModelCatalogUiPorts,
34
35
  useModelFavorites: () => useModelFavorites,
35
- useModelRecordsSelector: () => useModelRecordsSelector
36
+ useModelRecordsSelector: () => useModelRecordsSelector,
37
+ useOnModelCatalogChanged: () => useOnModelCatalogChanged
36
38
  });
37
39
  module.exports = __toCommonJS(react_exports);
38
40
 
@@ -2289,6 +2291,25 @@ function ModelListDropdown({
2289
2291
 
2290
2292
  // models/react/records.ts
2291
2293
  var import_react3 = require("react");
2294
+
2295
+ // models/catalog/changes.ts
2296
+ var KEY = /* @__PURE__ */ Symbol.for("ai-matrx.agents.model-catalog-changes");
2297
+ function hub() {
2298
+ const g = globalThis;
2299
+ return g[KEY] ??= { listeners: /* @__PURE__ */ new Set(), revision: 0 };
2300
+ }
2301
+ function onModelCatalogChanged(listener) {
2302
+ const h = hub();
2303
+ h.listeners.add(listener);
2304
+ return () => {
2305
+ h.listeners.delete(listener);
2306
+ };
2307
+ }
2308
+ function modelCatalogRevision() {
2309
+ return hub().revision;
2310
+ }
2311
+
2312
+ // models/react/records.ts
2292
2313
  function useModelRecordsSelector(records, select) {
2293
2314
  return (0, import_react3.useSyncExternalStore)(
2294
2315
  records.subscribe,
@@ -2296,6 +2317,15 @@ function useModelRecordsSelector(records, select) {
2296
2317
  () => select(records.getState())
2297
2318
  );
2298
2319
  }
2320
+ var subscribeRevision = (listener) => onModelCatalogChanged(() => listener());
2321
+ function useModelCatalogRevision() {
2322
+ return (0, import_react3.useSyncExternalStore)(subscribeRevision, modelCatalogRevision, modelCatalogRevision);
2323
+ }
2324
+ function useOnModelCatalogChanged(onChange) {
2325
+ const latest = (0, import_react3.useRef)(onChange);
2326
+ latest.current = onChange;
2327
+ (0, import_react3.useEffect)(() => onModelCatalogChanged((payload) => latest.current(payload)), []);
2328
+ }
2299
2329
 
2300
2330
  // models/react/ModelDiscovery.tsx
2301
2331
  var import_format3 = require("@ai-matrx/kit/format");