@ai-matrx/agents 0.59.270 → 0.59.271

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,10 +1,15 @@
1
1
  # Changelog
2
2
 
3
- ## 0.59.270
3
+ ## 0.59.271
4
4
 
5
5
  UNVERIFIED: this release has no Unreleased entry and the source comparison could not be made (the caller supplied no source comparison). Whether source changed is unknown; consumer action is unknown.
6
6
  Check the package's gitHead on npm against this version's gitHead: `git diff <previous gitHead> <this gitHead> -- apps/shared/matrx-agents/src`.
7
7
 
8
+ ## 0.59.270
9
+
10
+ Automatic changed-only republish (docs/metadata drift since the last published version; verified: `git diff 4c08cba6c4 8a4ed656ea -- apps/shared/matrx-agents/src`
11
+ has no shipped-source changes outside tests, fixtures and docs). No consumer action required.
12
+
8
13
  ## 0.59.269
9
14
 
10
15
  Automatic changed-only republish (docs/metadata drift since the last published version; verified: `git diff 5b7c48a99c 4c08cba6c4 -- apps/shared/matrx-agents/src`
@@ -74,6 +74,7 @@ __export(models_exports, {
74
74
  fitsNeed: () => fitsNeed,
75
75
  fitsThisComputer: () => fitsThisComputer,
76
76
  followModelCatalogChanges: () => followModelCatalogChanges,
77
+ followModelCatalogVisibility: () => followModelCatalogVisibility,
77
78
  formatMemoryBytes: () => formatMemoryBytes,
78
79
  fromDiscovery: () => fromDiscovery,
79
80
  ggufEngine: () => ggufEngine,
@@ -122,6 +123,7 @@ __export(models_exports, {
122
123
  readDeviceModelFiles: () => readDeviceModelFiles,
123
124
  readGgufHeader: () => readGgufHeader,
124
125
  readModelCatalog: () => readModelCatalog,
126
+ readModelCatalogStamp: () => readModelCatalogStamp,
125
127
  readModelIdentities: () => readModelIdentities,
126
128
  readPublicModel: () => readPublicModel,
127
129
  reconcileModelFavoriteIds: () => reconcileModelFavoriteIds,
@@ -937,42 +939,6 @@ var OUTPUT_MODALITIES = [
937
939
  "decision"
938
940
  ];
939
941
 
940
- // models/catalog/changes.ts
941
- var KEY2 = /* @__PURE__ */ Symbol.for("ai-matrx.agents.model-catalog-changes");
942
- function hub() {
943
- const g = globalThis;
944
- return g[KEY2] ??= { listeners: /* @__PURE__ */ new Set(), revision: 0 };
945
- }
946
- function isModelCatalogChange(payload) {
947
- const catalog = payload?.["catalog"];
948
- return catalog === void 0 || catalog === null || catalog === "models";
949
- }
950
- function onModelCatalogChanged(listener) {
951
- const h = hub();
952
- h.listeners.add(listener);
953
- return () => {
954
- h.listeners.delete(listener);
955
- };
956
- }
957
- function modelCatalogRevision() {
958
- return hub().revision;
959
- }
960
- function announceModelCatalogChanged(payload = {}) {
961
- if (!isModelCatalogChange(payload)) return;
962
- const h = hub();
963
- h.revision += 1;
964
- for (const l of [...h.listeners]) {
965
- try {
966
- l(payload);
967
- } catch (err) {
968
- console.error("[@ai-matrx/agents/models] a catalog_changed listener threw", err);
969
- }
970
- }
971
- }
972
- function followModelCatalogChanges(subscribe) {
973
- return subscribe("catalog_changed", (payload) => announceModelCatalogChanged(payload ?? {}));
974
- }
975
-
976
942
  // models/label.ts
977
943
  function modelDisplayLabel(row) {
978
944
  if (!row) return void 0;
@@ -1564,6 +1530,102 @@ async function readPublicModel(client, key) {
1564
1530
  if (error) return { row: null, error: error.message };
1565
1531
  return { row: data ?? null, error: null };
1566
1532
  }
1533
+ async function readModelCatalogStamp(client) {
1534
+ const q = client.schema("ai").from("model_definition").select("updated_at").order("updated_at", { ascending: false, nullsFirst: false });
1535
+ if (!q.limit) throw new Error("readModelCatalogStamp: the client has no .limit() (a supabase-js client does)");
1536
+ const { data, error } = await q.limit(1).maybeSingle();
1537
+ if (error) throw new Error(error.message);
1538
+ const stamp = data?.updated_at;
1539
+ return typeof stamp === "string" ? stamp : null;
1540
+ }
1541
+
1542
+ // models/catalog/changes.ts
1543
+ var KEY2 = /* @__PURE__ */ Symbol.for("ai-matrx.agents.model-catalog-changes");
1544
+ function hub() {
1545
+ const g = globalThis;
1546
+ return g[KEY2] ??= { listeners: /* @__PURE__ */ new Set(), revision: 0 };
1547
+ }
1548
+ function isModelCatalogChange(payload) {
1549
+ const catalog = payload?.["catalog"];
1550
+ return catalog === void 0 || catalog === null || catalog === "models";
1551
+ }
1552
+ function onModelCatalogChanged(listener) {
1553
+ const h = hub();
1554
+ h.listeners.add(listener);
1555
+ return () => {
1556
+ h.listeners.delete(listener);
1557
+ };
1558
+ }
1559
+ function modelCatalogRevision() {
1560
+ return hub().revision;
1561
+ }
1562
+ function announceModelCatalogChanged(payload = {}) {
1563
+ if (!isModelCatalogChange(payload)) return;
1564
+ const h = hub();
1565
+ h.revision += 1;
1566
+ for (const l of [...h.listeners]) {
1567
+ try {
1568
+ l(payload);
1569
+ } catch (err) {
1570
+ console.error("[@ai-matrx/agents/models] a catalog_changed listener threw", err);
1571
+ }
1572
+ }
1573
+ }
1574
+ function followModelCatalogChanges(subscribe) {
1575
+ return subscribe("catalog_changed", (payload) => announceModelCatalogChanged(payload ?? {}));
1576
+ }
1577
+ function followModelCatalogVisibility(options) {
1578
+ const page = options.page ?? (typeof document !== "undefined" && typeof window !== "undefined" ? { document, window } : null);
1579
+ if (!page) return () => {
1580
+ };
1581
+ const minIntervalMs = options.minIntervalMs ?? 2e4;
1582
+ const now = options.now ?? (() => Date.now());
1583
+ const sink = options.errorSink ?? ((e) => {
1584
+ console.error(`[@ai-matrx/agents/models] ${e.code}: ${e.message}`);
1585
+ });
1586
+ let baseline;
1587
+ let inFlight = null;
1588
+ let lastShownRead = -Infinity;
1589
+ const stamp = () => inFlight ??= readModelCatalogStamp(options.client).finally(() => {
1590
+ inFlight = null;
1591
+ });
1592
+ const fail = (err) => sink({ code: "model-catalog-stamp-failed", message: err instanceof Error ? err.message : String(err) });
1593
+ const learnBaseline = () => {
1594
+ if (baseline !== void 0) return;
1595
+ void stamp().then((s4) => {
1596
+ if (baseline === void 0) baseline = s4;
1597
+ }, fail);
1598
+ };
1599
+ const onHidden = () => {
1600
+ if (page.document.visibilityState === "hidden") learnBaseline();
1601
+ };
1602
+ const onShown = () => {
1603
+ if (page.document.visibilityState === "hidden") return;
1604
+ const t = now();
1605
+ if (t - lastShownRead < minIntervalMs) return;
1606
+ lastShownRead = t;
1607
+ const known = baseline;
1608
+ if (known === void 0) return learnBaseline();
1609
+ void stamp().then((s4) => {
1610
+ if (s4 === known) return;
1611
+ baseline = s4;
1612
+ announceModelCatalogChanged({ catalog: "models", change: "catch_up_visible" });
1613
+ }, fail);
1614
+ };
1615
+ const offChanges = onModelCatalogChanged(() => {
1616
+ baseline = void 0;
1617
+ if (page.document.visibilityState === "hidden") learnBaseline();
1618
+ });
1619
+ const onVisibility = () => page.document.visibilityState === "hidden" ? onHidden() : onShown();
1620
+ page.document.addEventListener("visibilitychange", onVisibility);
1621
+ page.window.addEventListener("focus", onShown);
1622
+ if (page.document.visibilityState === "hidden") learnBaseline();
1623
+ return () => {
1624
+ offChanges();
1625
+ page.document.removeEventListener("visibilitychange", onVisibility);
1626
+ page.window.removeEventListener("focus", onShown);
1627
+ };
1628
+ }
1567
1629
 
1568
1630
  // models/catalog/store.ts
1569
1631
  var IDLE = { status: "idle", models: [], error: null, loadedAt: null };
@@ -641,6 +641,8 @@ interface RecordsQuery extends PromiseLike<PgResult> {
641
641
  nullsFirst?: boolean;
642
642
  }): RecordsQuery;
643
643
  maybeSingle(): PromiseLike<PgResult>;
644
+ /** Only the catalog stamp (`readModelCatalogStamp`) uses it. */
645
+ limit?(count: number): RecordsQuery;
644
646
  }
645
647
  interface ModelRecordsClient {
646
648
  schema(name: string): {
@@ -791,6 +793,12 @@ declare function readPublicModel(client: PublicModelClient, key: string): Promis
791
793
  } | null;
792
794
  error: string | null;
793
795
  }>;
796
+ /**
797
+ * THE catalog stamp: the newest `ai.model_definition.updated_at` (one row, one column) — the cheap read a
798
+ * tab makes to learn whether the catalog changed while it was not listening (catalog/changes.ts
799
+ * `followModelCatalogVisibility`). Null when the catalog is empty. Throws the database's error.
800
+ */
801
+ declare function readModelCatalogStamp(client: ModelRecordsClient): Promise<string | null>;
794
802
 
795
803
  /**
796
804
  * `@ai-matrx/agents/models` — `createModelCatalog`: the headless model catalog store.
@@ -2032,7 +2040,12 @@ declare function modelDisplayLabel(row: ModelLabelSource | null | undefined): st
2032
2040
  * `@ai-matrx/agents/models/react`). No surface keeps a model cache that misses the event.
2033
2041
  *
2034
2042
  * The hub lives on `globalThis` so two bundled copies of this module share one set of listeners.
2043
+ *
2044
+ * A tab that is not listening (the bus room not joined yet, or asleep) catches up when it comes to the
2045
+ * front: `followModelCatalogVisibility({ client })` reads the catalog stamp when the tab hides and again
2046
+ * when it shows or takes focus (throttled), and announces a change when the stamp moved.
2035
2047
  */
2048
+
2036
2049
  /** A `catalog_changed` payload: `{ catalog?: "models" | ..., change?, model_id? }`. */
2037
2050
  type ModelCatalogChangePayload = Record<string, unknown>;
2038
2051
  /** The bus client's subscribe call: `(action, handler) => unsubscribe` (the web's `registerDirectiveHandler`). */
@@ -2047,6 +2060,32 @@ declare function modelCatalogRevision(): number;
2047
2060
  declare function announceModelCatalogChanged(payload?: ModelCatalogChangePayload): void;
2048
2061
  /** Bind the host's bus client: every `catalog_changed` it delivers reaches every model reader. Returns the unbind. */
2049
2062
  declare function followModelCatalogChanges(subscribe: ModelCatalogDirectiveSubscribe): () => void;
2063
+ interface ModelCatalogVisibilityOptions {
2064
+ /** The client the stamp is read with (supabase-js fits as is). */
2065
+ client: ModelRecordsClient;
2066
+ /** At most one stamp read per this many ms when the tab shows / takes focus. Default 20 s. */
2067
+ minIntervalMs?: number;
2068
+ /** The page (tests pass a double); defaults to the global `document` / `window`. */
2069
+ page?: {
2070
+ document: Pick<Document, "visibilityState" | "addEventListener" | "removeEventListener">;
2071
+ window: Pick<Window, "addEventListener" | "removeEventListener">;
2072
+ };
2073
+ /** Where a failed stamp read is said. Default: console.error, tagged. */
2074
+ errorSink?: (event: {
2075
+ code: string;
2076
+ message: string;
2077
+ }) => void;
2078
+ now?: () => number;
2079
+ }
2080
+ /**
2081
+ * When the tab shows or takes focus, read the catalog stamp (one row) and, when it moved since the tab
2082
+ * last knew it, announce a model catalog change through the SAME door as the bus (every catalog re-reads
2083
+ * what it read; every reader following `onModelCatalogChanged` re-reads). The baseline is taken when the
2084
+ * tab hides (or at bind when it starts hidden), and refreshed by every announced change. Reads already in
2085
+ * flight are joined; shows/focuses within `minIntervalMs` share the last read. Returns the unbind. No-op
2086
+ * where there is no document (a server render, a worker).
2087
+ */
2088
+ declare function followModelCatalogVisibility(options: ModelCatalogVisibilityOptions): () => void;
2050
2089
 
2051
- export { CHAT_DEFAULT_MODEL_KNOB, CONTENT_TYPES, DEFAULT_CAPABILITIES, DEVICE_FILE_USABLE_STATES, DEVICE_OWN_FILE_USABLE_STATES, DEVICE_RULE_TWIN_FIXTURE, DEVICE_SERVED_SETTINGS_KEY, DEVICE_SERVED_VIA, DeviceModelUnavailableError, EMPTY_MODEL_RECORDS, EXCLUDED_REVIEW_STATES, FEATURE_BUCKETS, FEATURE_BUCKET_ORDER, FEATURE_KEYS, FILE_TYPE_QUANT, GgufError, INPUT_MODALITIES, INTERACTION_MODES, MAX_SUCCESSION_HOPS, MODEL_CATALOG_KINDS, MODEL_FAVORITE_ENTITY_TYPE, NEW_AGENT_DEFAULT_MODEL_KNOB, OUTPUT_MODALITIES, SERVICE_LABEL, SERVICE_LABEL_PLURAL, TOP_TIER_COST_RATING, TOP_TIER_LOCKED_TITLE, acceptsTextInstructions, announceModelCatalogChanged, availableOnDevices, 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, readDeviceModelFiles, readGgufHeader, readModelCatalog, readModelIdentities, readPublicModel, reconcileModelFavoriteIds, reportModelDiagnostic, requireCanonicalCapabilities, requiredArtifactIds, requiredArtifacts, resetSuccessionLogForTests, resolveDeviceModel, 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, toDeviceModelFile, validateModelSet, variantVerdict, withModelClass };
2052
- export type { AdminOffering, AdminPricingBand, ArtifactUse, AuditCapabilitiesRecord, AvailabilityVariant, CapabilitiesParseContext, CatalogKeyColumn, CatalogKindInput, CatalogModel, CatalogTier, CheapestAdequateOptions, CompatStatus, ContentType, CuratedCatalogClient, CuratedModel, CuratedPresentation, CuratedRows, CuratedVariant, DecidedBy, DeviceAvailability, DeviceGraphics, DeviceHardware, DeviceModelFile, DeviceModelResolution, DeviceServedSettings, DiscoveryAmbiguity, DiscoveryArtifact, DiscoveryMedia, DiscoveryResult, DiscoveryRuntime, DiscoverySource, DiscoveryVariant, FeatureBucket, FeatureBucketMeta, FeatureKey, FitCapabilities, GgufHeader, GgufRead, GgufTensor, GroupedFeatures, HardwareClass, HardwareClasses, HeaderFlag, HeaderJudgement, HeaderRefusal, Interaction, InteractionMode, LocalArtifact, LocalCatalogClient, LocalCatalogQuery, LocalCompatRow, LocalModel, LocalModelCatalog, LocalPresentation, LocalRuntime, LocalRuntimeBuild, LocalVariant, Modality, ModelCapabilities, ModelCatalog, ModelCatalogChangePayload, ModelCatalogClient, ModelCatalogConfig, ModelCatalogDirectiveSubscribe, ModelCatalogErrorSink, ModelCatalogPgError, ModelCatalogPgResult, ModelCatalogQuery, ModelCatalogState, ModelCatalogVariant, ModelCatalogVariantState, ModelClassConfig, ModelClassEntry, ModelClassIndex, ModelDiagnosticEntry, ModelDiagnosticsSink, ModelFavorites, ModelFavoritesCache, ModelFavoritesClient, ModelFavoritesConfig, ModelFavoritesErrorSink, ModelFavoritesState, ModelFetchScope, ModelFileHandle, ModelFit, ModelIdentityRow, ModelJson, ModelLabelSource, ModelNeed, ModelOfferingRow, ModelOption, ModelOptionWithLifecycle, ModelRecord, ModelRecordFetchType, ModelRecords, ModelRecordsClient, ModelRecordsConfig, ModelRecordsState, ModelSelectionPurpose, ModelSetEnv, ModelSetFacts, ModelSetValidation, NewAgentModel, NoSelection, OrderedLike, PreferredModelChoice, PreferredModelChoicePorts, PriceTier, PricedCatalogModel, PublicModelClient, RankOptions, RankedVariant, ReadModelCatalogOptions, SelectDevice, Selection, ShardName, SuccessionClient, SuccessionIndex, SuccessionNode, VariantDiscovery, VariantVerdict, Want };
2090
+ export { CHAT_DEFAULT_MODEL_KNOB, CONTENT_TYPES, DEFAULT_CAPABILITIES, DEVICE_FILE_USABLE_STATES, DEVICE_OWN_FILE_USABLE_STATES, DEVICE_RULE_TWIN_FIXTURE, DEVICE_SERVED_SETTINGS_KEY, DEVICE_SERVED_VIA, DeviceModelUnavailableError, EMPTY_MODEL_RECORDS, EXCLUDED_REVIEW_STATES, FEATURE_BUCKETS, FEATURE_BUCKET_ORDER, FEATURE_KEYS, FILE_TYPE_QUANT, GgufError, INPUT_MODALITIES, INTERACTION_MODES, MAX_SUCCESSION_HOPS, MODEL_CATALOG_KINDS, MODEL_FAVORITE_ENTITY_TYPE, NEW_AGENT_DEFAULT_MODEL_KNOB, OUTPUT_MODALITIES, SERVICE_LABEL, SERVICE_LABEL_PLURAL, TOP_TIER_COST_RATING, TOP_TIER_LOCKED_TITLE, acceptsTextInstructions, announceModelCatalogChanged, availableOnDevices, blendedPrice, buildModelClassIndex, buildSuccessionIndex, catalogFingerprint, catalogForApp, catalogKeyColumn, catalogKindOf, cheapestAdequateModel, classConfigKey, collapseTiersByClass, costRatingTier, costRatingWord, createModelCatalog, createModelFavorites, createModelRecords, curatedList, displayOrderOf, favoriteIdsEqual, fetchCuratedModels, findNonCanonicalCapabilityValues, fitsMemory, fitsNeed, fitsThisComputer, followModelCatalogChanges, followModelCatalogVisibility, 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, readDeviceModelFiles, readGgufHeader, readModelCatalog, readModelCatalogStamp, readModelIdentities, readPublicModel, reconcileModelFavoriteIds, reportModelDiagnostic, requireCanonicalCapabilities, requiredArtifactIds, requiredArtifacts, resetSuccessionLogForTests, resolveDeviceModel, 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, toDeviceModelFile, validateModelSet, variantVerdict, withModelClass };
2091
+ export type { AdminOffering, AdminPricingBand, ArtifactUse, AuditCapabilitiesRecord, AvailabilityVariant, CapabilitiesParseContext, CatalogKeyColumn, CatalogKindInput, CatalogModel, CatalogTier, CheapestAdequateOptions, CompatStatus, ContentType, CuratedCatalogClient, CuratedModel, CuratedPresentation, CuratedRows, CuratedVariant, DecidedBy, DeviceAvailability, DeviceGraphics, DeviceHardware, DeviceModelFile, DeviceModelResolution, DeviceServedSettings, DiscoveryAmbiguity, DiscoveryArtifact, DiscoveryMedia, DiscoveryResult, DiscoveryRuntime, DiscoverySource, DiscoveryVariant, FeatureBucket, FeatureBucketMeta, FeatureKey, FitCapabilities, GgufHeader, GgufRead, GgufTensor, GroupedFeatures, HardwareClass, HardwareClasses, HeaderFlag, HeaderJudgement, HeaderRefusal, Interaction, InteractionMode, LocalArtifact, LocalCatalogClient, LocalCatalogQuery, LocalCompatRow, LocalModel, LocalModelCatalog, LocalPresentation, LocalRuntime, LocalRuntimeBuild, LocalVariant, Modality, ModelCapabilities, ModelCatalog, ModelCatalogChangePayload, ModelCatalogClient, ModelCatalogConfig, ModelCatalogDirectiveSubscribe, ModelCatalogErrorSink, ModelCatalogPgError, ModelCatalogPgResult, ModelCatalogQuery, ModelCatalogState, ModelCatalogVariant, ModelCatalogVariantState, ModelCatalogVisibilityOptions, ModelClassConfig, ModelClassEntry, ModelClassIndex, ModelDiagnosticEntry, ModelDiagnosticsSink, ModelFavorites, ModelFavoritesCache, ModelFavoritesClient, ModelFavoritesConfig, ModelFavoritesErrorSink, ModelFavoritesState, ModelFetchScope, ModelFileHandle, ModelFit, ModelIdentityRow, ModelJson, ModelLabelSource, ModelNeed, ModelOfferingRow, ModelOption, ModelOptionWithLifecycle, ModelRecord, ModelRecordFetchType, ModelRecords, ModelRecordsClient, ModelRecordsConfig, ModelRecordsState, ModelSelectionPurpose, ModelSetEnv, ModelSetFacts, ModelSetValidation, NewAgentModel, NoSelection, OrderedLike, PreferredModelChoice, PreferredModelChoicePorts, PriceTier, PricedCatalogModel, PublicModelClient, RankOptions, RankedVariant, ReadModelCatalogOptions, SelectDevice, Selection, ShardName, SuccessionClient, SuccessionIndex, SuccessionNode, VariantDiscovery, VariantVerdict, Want };
@@ -641,6 +641,8 @@ interface RecordsQuery extends PromiseLike<PgResult> {
641
641
  nullsFirst?: boolean;
642
642
  }): RecordsQuery;
643
643
  maybeSingle(): PromiseLike<PgResult>;
644
+ /** Only the catalog stamp (`readModelCatalogStamp`) uses it. */
645
+ limit?(count: number): RecordsQuery;
644
646
  }
645
647
  interface ModelRecordsClient {
646
648
  schema(name: string): {
@@ -791,6 +793,12 @@ declare function readPublicModel(client: PublicModelClient, key: string): Promis
791
793
  } | null;
792
794
  error: string | null;
793
795
  }>;
796
+ /**
797
+ * THE catalog stamp: the newest `ai.model_definition.updated_at` (one row, one column) — the cheap read a
798
+ * tab makes to learn whether the catalog changed while it was not listening (catalog/changes.ts
799
+ * `followModelCatalogVisibility`). Null when the catalog is empty. Throws the database's error.
800
+ */
801
+ declare function readModelCatalogStamp(client: ModelRecordsClient): Promise<string | null>;
794
802
 
795
803
  /**
796
804
  * `@ai-matrx/agents/models` — `createModelCatalog`: the headless model catalog store.
@@ -2032,7 +2040,12 @@ declare function modelDisplayLabel(row: ModelLabelSource | null | undefined): st
2032
2040
  * `@ai-matrx/agents/models/react`). No surface keeps a model cache that misses the event.
2033
2041
  *
2034
2042
  * The hub lives on `globalThis` so two bundled copies of this module share one set of listeners.
2043
+ *
2044
+ * A tab that is not listening (the bus room not joined yet, or asleep) catches up when it comes to the
2045
+ * front: `followModelCatalogVisibility({ client })` reads the catalog stamp when the tab hides and again
2046
+ * when it shows or takes focus (throttled), and announces a change when the stamp moved.
2035
2047
  */
2048
+
2036
2049
  /** A `catalog_changed` payload: `{ catalog?: "models" | ..., change?, model_id? }`. */
2037
2050
  type ModelCatalogChangePayload = Record<string, unknown>;
2038
2051
  /** The bus client's subscribe call: `(action, handler) => unsubscribe` (the web's `registerDirectiveHandler`). */
@@ -2047,6 +2060,32 @@ declare function modelCatalogRevision(): number;
2047
2060
  declare function announceModelCatalogChanged(payload?: ModelCatalogChangePayload): void;
2048
2061
  /** Bind the host's bus client: every `catalog_changed` it delivers reaches every model reader. Returns the unbind. */
2049
2062
  declare function followModelCatalogChanges(subscribe: ModelCatalogDirectiveSubscribe): () => void;
2063
+ interface ModelCatalogVisibilityOptions {
2064
+ /** The client the stamp is read with (supabase-js fits as is). */
2065
+ client: ModelRecordsClient;
2066
+ /** At most one stamp read per this many ms when the tab shows / takes focus. Default 20 s. */
2067
+ minIntervalMs?: number;
2068
+ /** The page (tests pass a double); defaults to the global `document` / `window`. */
2069
+ page?: {
2070
+ document: Pick<Document, "visibilityState" | "addEventListener" | "removeEventListener">;
2071
+ window: Pick<Window, "addEventListener" | "removeEventListener">;
2072
+ };
2073
+ /** Where a failed stamp read is said. Default: console.error, tagged. */
2074
+ errorSink?: (event: {
2075
+ code: string;
2076
+ message: string;
2077
+ }) => void;
2078
+ now?: () => number;
2079
+ }
2080
+ /**
2081
+ * When the tab shows or takes focus, read the catalog stamp (one row) and, when it moved since the tab
2082
+ * last knew it, announce a model catalog change through the SAME door as the bus (every catalog re-reads
2083
+ * what it read; every reader following `onModelCatalogChanged` re-reads). The baseline is taken when the
2084
+ * tab hides (or at bind when it starts hidden), and refreshed by every announced change. Reads already in
2085
+ * flight are joined; shows/focuses within `minIntervalMs` share the last read. Returns the unbind. No-op
2086
+ * where there is no document (a server render, a worker).
2087
+ */
2088
+ declare function followModelCatalogVisibility(options: ModelCatalogVisibilityOptions): () => void;
2050
2089
 
2051
- export { CHAT_DEFAULT_MODEL_KNOB, CONTENT_TYPES, DEFAULT_CAPABILITIES, DEVICE_FILE_USABLE_STATES, DEVICE_OWN_FILE_USABLE_STATES, DEVICE_RULE_TWIN_FIXTURE, DEVICE_SERVED_SETTINGS_KEY, DEVICE_SERVED_VIA, DeviceModelUnavailableError, EMPTY_MODEL_RECORDS, EXCLUDED_REVIEW_STATES, FEATURE_BUCKETS, FEATURE_BUCKET_ORDER, FEATURE_KEYS, FILE_TYPE_QUANT, GgufError, INPUT_MODALITIES, INTERACTION_MODES, MAX_SUCCESSION_HOPS, MODEL_CATALOG_KINDS, MODEL_FAVORITE_ENTITY_TYPE, NEW_AGENT_DEFAULT_MODEL_KNOB, OUTPUT_MODALITIES, SERVICE_LABEL, SERVICE_LABEL_PLURAL, TOP_TIER_COST_RATING, TOP_TIER_LOCKED_TITLE, acceptsTextInstructions, announceModelCatalogChanged, availableOnDevices, 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, readDeviceModelFiles, readGgufHeader, readModelCatalog, readModelIdentities, readPublicModel, reconcileModelFavoriteIds, reportModelDiagnostic, requireCanonicalCapabilities, requiredArtifactIds, requiredArtifacts, resetSuccessionLogForTests, resolveDeviceModel, 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, toDeviceModelFile, validateModelSet, variantVerdict, withModelClass };
2052
- export type { AdminOffering, AdminPricingBand, ArtifactUse, AuditCapabilitiesRecord, AvailabilityVariant, CapabilitiesParseContext, CatalogKeyColumn, CatalogKindInput, CatalogModel, CatalogTier, CheapestAdequateOptions, CompatStatus, ContentType, CuratedCatalogClient, CuratedModel, CuratedPresentation, CuratedRows, CuratedVariant, DecidedBy, DeviceAvailability, DeviceGraphics, DeviceHardware, DeviceModelFile, DeviceModelResolution, DeviceServedSettings, DiscoveryAmbiguity, DiscoveryArtifact, DiscoveryMedia, DiscoveryResult, DiscoveryRuntime, DiscoverySource, DiscoveryVariant, FeatureBucket, FeatureBucketMeta, FeatureKey, FitCapabilities, GgufHeader, GgufRead, GgufTensor, GroupedFeatures, HardwareClass, HardwareClasses, HeaderFlag, HeaderJudgement, HeaderRefusal, Interaction, InteractionMode, LocalArtifact, LocalCatalogClient, LocalCatalogQuery, LocalCompatRow, LocalModel, LocalModelCatalog, LocalPresentation, LocalRuntime, LocalRuntimeBuild, LocalVariant, Modality, ModelCapabilities, ModelCatalog, ModelCatalogChangePayload, ModelCatalogClient, ModelCatalogConfig, ModelCatalogDirectiveSubscribe, ModelCatalogErrorSink, ModelCatalogPgError, ModelCatalogPgResult, ModelCatalogQuery, ModelCatalogState, ModelCatalogVariant, ModelCatalogVariantState, ModelClassConfig, ModelClassEntry, ModelClassIndex, ModelDiagnosticEntry, ModelDiagnosticsSink, ModelFavorites, ModelFavoritesCache, ModelFavoritesClient, ModelFavoritesConfig, ModelFavoritesErrorSink, ModelFavoritesState, ModelFetchScope, ModelFileHandle, ModelFit, ModelIdentityRow, ModelJson, ModelLabelSource, ModelNeed, ModelOfferingRow, ModelOption, ModelOptionWithLifecycle, ModelRecord, ModelRecordFetchType, ModelRecords, ModelRecordsClient, ModelRecordsConfig, ModelRecordsState, ModelSelectionPurpose, ModelSetEnv, ModelSetFacts, ModelSetValidation, NewAgentModel, NoSelection, OrderedLike, PreferredModelChoice, PreferredModelChoicePorts, PriceTier, PricedCatalogModel, PublicModelClient, RankOptions, RankedVariant, ReadModelCatalogOptions, SelectDevice, Selection, ShardName, SuccessionClient, SuccessionIndex, SuccessionNode, VariantDiscovery, VariantVerdict, Want };
2090
+ export { CHAT_DEFAULT_MODEL_KNOB, CONTENT_TYPES, DEFAULT_CAPABILITIES, DEVICE_FILE_USABLE_STATES, DEVICE_OWN_FILE_USABLE_STATES, DEVICE_RULE_TWIN_FIXTURE, DEVICE_SERVED_SETTINGS_KEY, DEVICE_SERVED_VIA, DeviceModelUnavailableError, EMPTY_MODEL_RECORDS, EXCLUDED_REVIEW_STATES, FEATURE_BUCKETS, FEATURE_BUCKET_ORDER, FEATURE_KEYS, FILE_TYPE_QUANT, GgufError, INPUT_MODALITIES, INTERACTION_MODES, MAX_SUCCESSION_HOPS, MODEL_CATALOG_KINDS, MODEL_FAVORITE_ENTITY_TYPE, NEW_AGENT_DEFAULT_MODEL_KNOB, OUTPUT_MODALITIES, SERVICE_LABEL, SERVICE_LABEL_PLURAL, TOP_TIER_COST_RATING, TOP_TIER_LOCKED_TITLE, acceptsTextInstructions, announceModelCatalogChanged, availableOnDevices, blendedPrice, buildModelClassIndex, buildSuccessionIndex, catalogFingerprint, catalogForApp, catalogKeyColumn, catalogKindOf, cheapestAdequateModel, classConfigKey, collapseTiersByClass, costRatingTier, costRatingWord, createModelCatalog, createModelFavorites, createModelRecords, curatedList, displayOrderOf, favoriteIdsEqual, fetchCuratedModels, findNonCanonicalCapabilityValues, fitsMemory, fitsNeed, fitsThisComputer, followModelCatalogChanges, followModelCatalogVisibility, 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, readDeviceModelFiles, readGgufHeader, readModelCatalog, readModelCatalogStamp, readModelIdentities, readPublicModel, reconcileModelFavoriteIds, reportModelDiagnostic, requireCanonicalCapabilities, requiredArtifactIds, requiredArtifacts, resetSuccessionLogForTests, resolveDeviceModel, 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, toDeviceModelFile, validateModelSet, variantVerdict, withModelClass };
2091
+ export type { AdminOffering, AdminPricingBand, ArtifactUse, AuditCapabilitiesRecord, AvailabilityVariant, CapabilitiesParseContext, CatalogKeyColumn, CatalogKindInput, CatalogModel, CatalogTier, CheapestAdequateOptions, CompatStatus, ContentType, CuratedCatalogClient, CuratedModel, CuratedPresentation, CuratedRows, CuratedVariant, DecidedBy, DeviceAvailability, DeviceGraphics, DeviceHardware, DeviceModelFile, DeviceModelResolution, DeviceServedSettings, DiscoveryAmbiguity, DiscoveryArtifact, DiscoveryMedia, DiscoveryResult, DiscoveryRuntime, DiscoverySource, DiscoveryVariant, FeatureBucket, FeatureBucketMeta, FeatureKey, FitCapabilities, GgufHeader, GgufRead, GgufTensor, GroupedFeatures, HardwareClass, HardwareClasses, HeaderFlag, HeaderJudgement, HeaderRefusal, Interaction, InteractionMode, LocalArtifact, LocalCatalogClient, LocalCatalogQuery, LocalCompatRow, LocalModel, LocalModelCatalog, LocalPresentation, LocalRuntime, LocalRuntimeBuild, LocalVariant, Modality, ModelCapabilities, ModelCatalog, ModelCatalogChangePayload, ModelCatalogClient, ModelCatalogConfig, ModelCatalogDirectiveSubscribe, ModelCatalogErrorSink, ModelCatalogPgError, ModelCatalogPgResult, ModelCatalogQuery, ModelCatalogState, ModelCatalogVariant, ModelCatalogVariantState, ModelCatalogVisibilityOptions, ModelClassConfig, ModelClassEntry, ModelClassIndex, ModelDiagnosticEntry, ModelDiagnosticsSink, ModelFavorites, ModelFavoritesCache, ModelFavoritesClient, ModelFavoritesConfig, ModelFavoritesErrorSink, ModelFavoritesState, ModelFetchScope, ModelFileHandle, ModelFit, ModelIdentityRow, ModelJson, ModelLabelSource, ModelNeed, ModelOfferingRow, ModelOption, ModelOptionWithLifecycle, ModelRecord, ModelRecordFetchType, ModelRecords, ModelRecordsClient, ModelRecordsConfig, ModelRecordsState, ModelSelectionPurpose, ModelSetEnv, ModelSetFacts, ModelSetValidation, NewAgentModel, NoSelection, OrderedLike, PreferredModelChoice, PreferredModelChoicePorts, PriceTier, PricedCatalogModel, PublicModelClient, RankOptions, RankedVariant, ReadModelCatalogOptions, SelectDevice, Selection, ShardName, SuccessionClient, SuccessionIndex, SuccessionNode, VariantDiscovery, VariantVerdict, Want };
@@ -759,42 +759,6 @@ 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
762
  // models/label.ts
799
763
  function modelDisplayLabel(row) {
800
764
  if (!row) return void 0;
@@ -1386,6 +1350,102 @@ async function readPublicModel(client, key) {
1386
1350
  if (error) return { row: null, error: error.message };
1387
1351
  return { row: data ?? null, error: null };
1388
1352
  }
1353
+ async function readModelCatalogStamp(client) {
1354
+ const q = client.schema("ai").from("model_definition").select("updated_at").order("updated_at", { ascending: false, nullsFirst: false });
1355
+ if (!q.limit) throw new Error("readModelCatalogStamp: the client has no .limit() (a supabase-js client does)");
1356
+ const { data, error } = await q.limit(1).maybeSingle();
1357
+ if (error) throw new Error(error.message);
1358
+ const stamp = data?.updated_at;
1359
+ return typeof stamp === "string" ? stamp : null;
1360
+ }
1361
+
1362
+ // models/catalog/changes.ts
1363
+ var KEY2 = /* @__PURE__ */ Symbol.for("ai-matrx.agents.model-catalog-changes");
1364
+ function hub() {
1365
+ const g = globalThis;
1366
+ return g[KEY2] ??= { listeners: /* @__PURE__ */ new Set(), revision: 0 };
1367
+ }
1368
+ function isModelCatalogChange(payload) {
1369
+ const catalog = payload?.["catalog"];
1370
+ return catalog === void 0 || catalog === null || catalog === "models";
1371
+ }
1372
+ function onModelCatalogChanged(listener) {
1373
+ const h = hub();
1374
+ h.listeners.add(listener);
1375
+ return () => {
1376
+ h.listeners.delete(listener);
1377
+ };
1378
+ }
1379
+ function modelCatalogRevision() {
1380
+ return hub().revision;
1381
+ }
1382
+ function announceModelCatalogChanged(payload = {}) {
1383
+ if (!isModelCatalogChange(payload)) return;
1384
+ const h = hub();
1385
+ h.revision += 1;
1386
+ for (const l of [...h.listeners]) {
1387
+ try {
1388
+ l(payload);
1389
+ } catch (err) {
1390
+ console.error("[@ai-matrx/agents/models] a catalog_changed listener threw", err);
1391
+ }
1392
+ }
1393
+ }
1394
+ function followModelCatalogChanges(subscribe) {
1395
+ return subscribe("catalog_changed", (payload) => announceModelCatalogChanged(payload ?? {}));
1396
+ }
1397
+ function followModelCatalogVisibility(options) {
1398
+ const page = options.page ?? (typeof document !== "undefined" && typeof window !== "undefined" ? { document, window } : null);
1399
+ if (!page) return () => {
1400
+ };
1401
+ const minIntervalMs = options.minIntervalMs ?? 2e4;
1402
+ const now = options.now ?? (() => Date.now());
1403
+ const sink = options.errorSink ?? ((e) => {
1404
+ console.error(`[@ai-matrx/agents/models] ${e.code}: ${e.message}`);
1405
+ });
1406
+ let baseline;
1407
+ let inFlight = null;
1408
+ let lastShownRead = -Infinity;
1409
+ const stamp = () => inFlight ??= readModelCatalogStamp(options.client).finally(() => {
1410
+ inFlight = null;
1411
+ });
1412
+ const fail = (err) => sink({ code: "model-catalog-stamp-failed", message: err instanceof Error ? err.message : String(err) });
1413
+ const learnBaseline = () => {
1414
+ if (baseline !== void 0) return;
1415
+ void stamp().then((s4) => {
1416
+ if (baseline === void 0) baseline = s4;
1417
+ }, fail);
1418
+ };
1419
+ const onHidden = () => {
1420
+ if (page.document.visibilityState === "hidden") learnBaseline();
1421
+ };
1422
+ const onShown = () => {
1423
+ if (page.document.visibilityState === "hidden") return;
1424
+ const t = now();
1425
+ if (t - lastShownRead < minIntervalMs) return;
1426
+ lastShownRead = t;
1427
+ const known = baseline;
1428
+ if (known === void 0) return learnBaseline();
1429
+ void stamp().then((s4) => {
1430
+ if (s4 === known) return;
1431
+ baseline = s4;
1432
+ announceModelCatalogChanged({ catalog: "models", change: "catch_up_visible" });
1433
+ }, fail);
1434
+ };
1435
+ const offChanges = onModelCatalogChanged(() => {
1436
+ baseline = void 0;
1437
+ if (page.document.visibilityState === "hidden") learnBaseline();
1438
+ });
1439
+ const onVisibility = () => page.document.visibilityState === "hidden" ? onHidden() : onShown();
1440
+ page.document.addEventListener("visibilitychange", onVisibility);
1441
+ page.window.addEventListener("focus", onShown);
1442
+ if (page.document.visibilityState === "hidden") learnBaseline();
1443
+ return () => {
1444
+ offChanges();
1445
+ page.document.removeEventListener("visibilitychange", onVisibility);
1446
+ page.window.removeEventListener("focus", onShown);
1447
+ };
1448
+ }
1389
1449
 
1390
1450
  // models/catalog/store.ts
1391
1451
  var IDLE = { status: "idle", models: [], error: null, loadedAt: null };
@@ -3031,6 +3091,7 @@ export {
3031
3091
  fitsNeed,
3032
3092
  fitsThisComputer,
3033
3093
  followModelCatalogChanges,
3094
+ followModelCatalogVisibility,
3034
3095
  formatMemoryBytes,
3035
3096
  fromDiscovery,
3036
3097
  ggufEngine,
@@ -3079,6 +3140,7 @@ export {
3079
3140
  readDeviceModelFiles,
3080
3141
  readGgufHeader,
3081
3142
  readModelCatalog,
3143
+ readModelCatalogStamp,
3082
3144
  readModelIdentities,
3083
3145
  readPublicModel,
3084
3146
  reconcileModelFavoriteIds,
@@ -675,7 +675,12 @@ declare function ModelListDropdown({ value: savedValue, localModels: localModels
675
675
  * `@ai-matrx/agents/models/react`). No surface keeps a model cache that misses the event.
676
676
  *
677
677
  * The hub lives on `globalThis` so two bundled copies of this module share one set of listeners.
678
+ *
679
+ * A tab that is not listening (the bus room not joined yet, or asleep) catches up when it comes to the
680
+ * front: `followModelCatalogVisibility({ client })` reads the catalog stamp when the tab hides and again
681
+ * when it shows or takes focus (throttled), and announces a change when the stamp moved.
678
682
  */
683
+
679
684
  /** A `catalog_changed` payload: `{ catalog?: "models" | ..., change?, model_id? }`. */
680
685
  type ModelCatalogChangePayload = Record<string, unknown>;
681
686
 
@@ -675,7 +675,12 @@ declare function ModelListDropdown({ value: savedValue, localModels: localModels
675
675
  * `@ai-matrx/agents/models/react`). No surface keeps a model cache that misses the event.
676
676
  *
677
677
  * The hub lives on `globalThis` so two bundled copies of this module share one set of listeners.
678
+ *
679
+ * A tab that is not listening (the bus room not joined yet, or asleep) catches up when it comes to the
680
+ * front: `followModelCatalogVisibility({ client })` reads the catalog stamp when the tab hides and again
681
+ * when it shows or takes focus (throttled), and announces a change when the stamp moved.
678
682
  */
683
+
679
684
  /** A `catalog_changed` payload: `{ catalog?: "models" | ..., change?, model_id? }`. */
680
685
  type ModelCatalogChangePayload = Record<string, unknown>;
681
686
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-matrx/agents",
3
- "version": "0.59.270",
3
+ "version": "0.59.271",
4
4
  "description": "Portable AI Matrx agent stream protocol, event projection, and safe result-presentation primitives.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -518,5 +518,5 @@
518
518
  "optional": true
519
519
  }
520
520
  },
521
- "gitHead": "8a4ed656ea91ae1934870745a5c95f91f24da863"
521
+ "gitHead": "2c48e6413332bdf7ea9f92366e3ad0e4129ddd52"
522
522
  }