@ai-matrx/agents 0.59.274 → 0.59.276

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,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.59.276
4
+
5
+ - `./models`: one catalog edit costs a page at most two requests, however many model labels are mounted.
6
+ `records.refresh(listed?)` re-reads full records in ONE `in (...)` read, identities in ONE `in (...)`
7
+ read of only the ids no list row covers, and model class slices in ONE read; `createModelCatalog().refresh()`
8
+ passes its picker list's fresh `model_public` + `model_offering` rows (`ModelCatalogListRows`), which answer
9
+ the records' options list, identities and class index, so an edit is that one list read. `loadIdentity`
10
+ calls for ids in the same tick (N labels mounting) coalesce into one `in (...)` read, shared with
11
+ `loadIdentities`; a name-shaped key still reads alone. Live 2026-10-24 (web f8dc2956): one rename cost the
12
+ chat page a list read plus one single-id `model_definition` read per label. Guard:
13
+ `models/react/catalog-changed-request-count.test.tsx`. Consumer action: none (the argument is optional).
14
+
15
+ ## 0.59.275
16
+
17
+ Automatic changed-only republish (docs/metadata drift since the last published version; verified: `git diff 6393a0ff9a cf96f35563 -- apps/shared/matrx-agents/src`
18
+ has no shipped-source changes outside tests, fixtures and docs). No consumer action required.
19
+
3
20
  ## 0.59.274
4
21
 
5
22
  - `./models`: a burst of `catalog_changed` is ONE re-read. New `coalesceModelCatalogChanges(deliver, options?)`:
@@ -1021,6 +1021,9 @@ function withIdLists(state2) {
1021
1021
  const sort = (ids) => ids.sort((a, b) => label(a).localeCompare(label(b)));
1022
1022
  return { ...state2, activeIds: sort(active), deprecatedIds: sort(deprecated) };
1023
1023
  }
1024
+ var IDENTITY_COLUMNS = "id, name, common_name, is_deprecated";
1025
+ var OPTION_COLUMN_LIST = ["id", "name", "common_name", "maker", "cost_rating", "speed_rating", "is_primary", "capabilities", "is_deprecated", "retired_at", "successor_id"];
1026
+ var OPTION_COLUMNS = OPTION_COLUMN_LIST.join(", ");
1024
1027
  var MODEL_CONFIG_COLUMNS = "id, name, common_name, maker, cost_rating, speed_rating, is_premium, is_primary, context_window, max_tokens, capabilities, controls, constraints, is_deprecated, retired_at, successor_id";
1025
1028
  function createModelRecords(config) {
1026
1029
  if (!config?.client) throw new Error("createModelRecords: `client` (a Supabase client) is required.");
@@ -1045,13 +1048,10 @@ function createModelRecords(config) {
1045
1048
  inFlight.set(key, p);
1046
1049
  return p;
1047
1050
  };
1048
- async function readFullModel(modelId) {
1049
- const relation = await isSignedIn() ? "model_config" : "model_public";
1050
- const { data, error } = await ai().from(relation).select(MODEL_CONFIG_COLUMNS).eq(catalogKeyColumn(modelId), modelId).maybeSingle();
1051
- if (error) throw error;
1052
- const row = data;
1053
- if (!row) throw new Error(`AI model ${modelId} is not available (ai.${relation} has no such row)`);
1054
- if (!row.id || !row.name) throw new Error(`ai.${relation} returned a row without id/name for model ${modelId}`);
1051
+ function toFullRecord(raw, relation, asked) {
1052
+ const row = raw;
1053
+ if (!row) throw new Error(`AI model ${asked} is not available (ai.${relation} has no such row)`);
1054
+ if (!row.id || !row.name) throw new Error(`ai.${relation} returned a row without id/name for model ${asked}`);
1055
1055
  const read = {
1056
1056
  ...row,
1057
1057
  id: row.id,
@@ -1061,74 +1061,146 @@ function createModelRecords(config) {
1061
1061
  };
1062
1062
  return normalizeModel(read);
1063
1063
  }
1064
- async function readOfferings(modelId) {
1064
+ async function readFullModel(modelId) {
1065
+ const relation = await isSignedIn() ? "model_config" : "model_public";
1066
+ const { data, error } = await ai().from(relation).select(MODEL_CONFIG_COLUMNS).eq(catalogKeyColumn(modelId), modelId).maybeSingle();
1067
+ if (error) throw error;
1068
+ return toFullRecord(data, relation, modelId);
1069
+ }
1070
+ async function readOfferings(modelIds) {
1065
1071
  const q = ai().from("model_offering").select("model_id, offering_id, served_via, served_via_endpoint_id, priority");
1066
- const { data, error } = await (modelId ? q.eq("model_id", modelId) : q);
1072
+ const { data, error } = await (modelIds ? modelIds.length === 1 ? q.eq("model_id", modelIds[0]) : q.in("model_id", modelIds) : q);
1067
1073
  if (error) throw error;
1068
1074
  return data ?? [];
1069
1075
  }
1070
- async function rereadKnown() {
1076
+ const mergeClassSlice = (rows3) => update((s4) => {
1077
+ if (s4.modelClassStatus === "succeeded") return s4;
1078
+ const slice = buildModelClassIndex(rows3);
1079
+ const base = s4.modelClassIndex ?? { classesByModelId: {}, classOfOffering: {} };
1080
+ return { ...s4, modelClassIndex: { classesByModelId: { ...base.classesByModelId, ...slice.classesByModelId }, classOfOffering: { ...base.classOfOffering, ...slice.classOfOffering } } };
1081
+ });
1082
+ let identityTick = null;
1083
+ async function readIdentitiesNow(wanted) {
1084
+ try {
1085
+ const { data, error } = await ai().from("model_definition").select(IDENTITY_COLUMNS).in("id", wanted);
1086
+ if (error) throw error;
1087
+ const rows3 = Array.isArray(data) ? data : [];
1088
+ const got = new Set(rows3.map((r) => r.id));
1089
+ const missing = wanted.filter((id) => !got.has(id));
1090
+ if (missing.length > 0) errorSink({ code: "model-records-identity-failed", message: `AI model ${missing.join(", ")} is not available (ai.model_definition has no such row)`, context: { modelIds: missing } });
1091
+ update((s4) => ({
1092
+ ...s4,
1093
+ identityById: { ...s4.identityById, ...Object.fromEntries(rows3.map((r) => [r.id, r])) },
1094
+ identityStatusById: { ...s4.identityStatusById, ...Object.fromEntries(wanted.map((id) => [id, got.has(id) ? "succeeded" : "failed"])) }
1095
+ }));
1096
+ } catch (err) {
1097
+ errorSink({ code: "model-records-identity-failed", message: messageOf(err), context: { modelIds: wanted } });
1098
+ update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, ...Object.fromEntries(wanted.map((id) => [id, "failed"])) } }));
1099
+ }
1100
+ }
1101
+ function queueIdentity(id) {
1102
+ if (!identityTick) {
1103
+ const ids = /* @__PURE__ */ new Set();
1104
+ const done = Promise.resolve().then(() => {
1105
+ identityTick = null;
1106
+ return readIdentitiesNow([...ids]);
1107
+ });
1108
+ identityTick = { ids, done };
1109
+ void done.finally(() => {
1110
+ for (const queued of ids) if (inFlight.get(`identity:${queued}`) === done) inFlight.delete(`identity:${queued}`);
1111
+ });
1112
+ }
1113
+ identityTick.ids.add(id);
1114
+ inFlight.set(`identity:${id}`, identityTick.done);
1115
+ update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, [id]: "loading" } }));
1116
+ return identityTick.done;
1117
+ }
1118
+ async function rereadKnown(listed) {
1071
1119
  const known = state2;
1072
1120
  const jobs = [];
1073
1121
  const fail = (code, message, context) => errorSink({ code, message: `Catalog refresh: ${message}`, ...context ? { context } : {} });
1074
- if (known.fetchScope === "active" || known.fetchScope === "all") {
1075
- jobs.push(
1076
- readOptions().then(
1077
- (rows3) => update((s4) => {
1122
+ const fed = listed ? listed.then((f) => f, () => null) : Promise.resolve(null);
1123
+ const optionsWereRead = known.fetchScope === "active" || known.fetchScope === "all";
1124
+ const freshOptions = optionsWereRead ? fed.then((f) => f ? optionRowsOf(f.publicRows, f.offeringRows) : readOptions()) : Promise.resolve(null);
1125
+ jobs.push(
1126
+ freshOptions.then(
1127
+ (rows3) => {
1128
+ if (!rows3) return;
1129
+ update((s4) => {
1078
1130
  const entities = { ...s4.entities };
1079
1131
  for (const row of rows3) {
1080
1132
  const existing = entities[row.id];
1081
1133
  entities[row.id] = { ...emptyRecord(), ...existing, ...row, _fetchType: existing?._fetchType === "full" ? "full" : "options" };
1082
1134
  }
1083
1135
  return withIdLists({ ...s4, entities, lastFetched: Date.now(), error: null });
1084
- }),
1085
- (err) => fail("model-records-refresh-options-failed", messageOf(err))
1086
- )
1087
- );
1088
- }
1089
- for (const [id, record] of Object.entries(known.entities)) {
1090
- if (record._fetchType !== "full") continue;
1136
+ });
1137
+ },
1138
+ (err) => fail("model-records-refresh-options-failed", messageOf(err))
1139
+ )
1140
+ );
1141
+ const fullIds = Object.entries(known.entities).filter(([, r]) => r._fetchType === "full").map(([id]) => id);
1142
+ if (fullIds.length > 0) {
1091
1143
  jobs.push(
1092
- readFullModel(id).then(
1093
- (fresh) => update((s4) => {
1094
- const existing = s4.entities[fresh.id];
1095
- return withIdLists({ ...s4, entities: { ...s4.entities, [fresh.id]: { ...emptyRecord(), ...existing, ...fresh, maker: fresh.maker ?? existing?.maker ?? null, _fetchType: "full" } } });
1096
- }),
1097
- (err) => fail("model-records-refresh-model-failed", `${id}: ${messageOf(err)}`, { modelId: id })
1098
- )
1144
+ (async () => {
1145
+ const relation = await isSignedIn() ? "model_config" : "model_public";
1146
+ const { data, error } = await ai().from(relation).select(MODEL_CONFIG_COLUMNS).in("id", fullIds);
1147
+ if (error) throw error;
1148
+ const byId2 = new Map((Array.isArray(data) ? data : []).map((r) => [r.id, r]));
1149
+ for (const id of fullIds) {
1150
+ try {
1151
+ const fresh = toFullRecord(byId2.get(id) ?? null, relation, id);
1152
+ update((s4) => {
1153
+ const existing = s4.entities[fresh.id];
1154
+ return withIdLists({ ...s4, entities: { ...s4.entities, [fresh.id]: { ...emptyRecord(), ...existing, ...fresh, maker: fresh.maker ?? existing?.maker ?? null, _fetchType: "full" } } });
1155
+ });
1156
+ } catch (err) {
1157
+ fail("model-records-refresh-model-failed", `${id}: ${messageOf(err)}`, { modelId: id });
1158
+ }
1159
+ }
1160
+ })().catch((err) => fail("model-records-refresh-model-failed", `${fullIds.join(", ")}: ${messageOf(err)}`, { modelIds: fullIds }))
1099
1161
  );
1100
1162
  }
1101
- for (const id of Object.keys(known.identityById)) {
1163
+ const identityKeys = Object.entries(known.identityById);
1164
+ if (identityKeys.length > 0) {
1102
1165
  jobs.push(
1103
- Promise.resolve(ai().from("model_definition").select("id, name, common_name, is_deprecated").eq(catalogKeyColumn(id), id).maybeSingle()).then(
1104
- ({ data, error }) => {
1166
+ (async () => {
1167
+ const [f, opts] = await Promise.all([fed, freshOptions.catch(() => null)]);
1168
+ const covered = /* @__PURE__ */ new Map();
1169
+ for (const r of [...f?.publicRows ?? [], ...opts ?? []]) {
1170
+ if (typeof r.id === "string" && typeof r.name === "string") {
1171
+ covered.set(r.id, { id: r.id, name: r.name, common_name: r.common_name ?? null, is_deprecated: r.is_deprecated ?? null });
1172
+ }
1173
+ }
1174
+ const wanted = [...new Set(identityKeys.map(([, row]) => row.id))].filter((id) => !covered.has(id) && known.entities[id]?._fetchType !== "full");
1175
+ if (wanted.length > 0) {
1176
+ const { data, error } = await ai().from("model_definition").select(IDENTITY_COLUMNS).in("id", wanted);
1105
1177
  if (error) throw error;
1106
- if (data) update((s4) => ({ ...s4, identityById: { ...s4.identityById, [id]: data } }));
1178
+ for (const row of Array.isArray(data) ? data : []) covered.set(row.id, row);
1179
+ }
1180
+ const next = {};
1181
+ for (const [key, row] of identityKeys) {
1182
+ const fresh = covered.get(row.id);
1183
+ if (fresh) next[key] = fresh;
1107
1184
  }
1108
- ).catch((err) => fail("model-records-refresh-identity-failed", `${id}: ${messageOf(err)}`, { modelId: id }))
1185
+ if (Object.keys(next).length > 0) update((s4) => ({ ...s4, identityById: { ...s4.identityById, ...next } }));
1186
+ })().catch((err) => fail("model-records-refresh-identity-failed", messageOf(err)))
1109
1187
  );
1110
1188
  }
1111
1189
  if (known.modelClassStatus === "succeeded") {
1112
1190
  jobs.push(
1113
- readOfferings().then(
1191
+ fed.then((f) => f?.offeringRows ?? readOfferings()).then(
1114
1192
  (rows3) => update((s4) => ({ ...s4, modelClassIndex: buildModelClassIndex(rows3) })),
1115
1193
  (err) => fail("model-records-refresh-classes-failed", messageOf(err))
1116
1194
  )
1117
1195
  );
1118
- } else {
1119
- for (const id of classesReadFor) {
1120
- jobs.push(
1121
- readOfferings(id).then(
1122
- (rows3) => update((s4) => {
1123
- if (s4.modelClassStatus === "succeeded") return s4;
1124
- const slice = buildModelClassIndex(rows3);
1125
- const base = s4.modelClassIndex ?? { classesByModelId: {}, classOfOffering: {} };
1126
- return { ...s4, modelClassIndex: { classesByModelId: { ...base.classesByModelId, ...slice.classesByModelId }, classOfOffering: { ...base.classOfOffering, ...slice.classOfOffering } } };
1127
- }),
1128
- (err) => fail("model-records-refresh-classes-failed", `${id}: ${messageOf(err)}`, { modelId: id })
1129
- )
1130
- );
1131
- }
1196
+ } else if (classesReadFor.size > 0) {
1197
+ const ids = [...classesReadFor];
1198
+ jobs.push(
1199
+ fed.then((f) => f ? f.offeringRows.filter((o) => o.model_id !== null && classesReadFor.has(o.model_id)) : readOfferings(ids)).then(
1200
+ (rows3) => mergeClassSlice(rows3),
1201
+ (err) => fail("model-records-refresh-classes-failed", `${ids.join(", ")}: ${messageOf(err)}`, { modelIds: ids })
1202
+ )
1203
+ );
1132
1204
  }
1133
1205
  for (const [key, cfg] of Object.entries(known.classConfigByOffering)) {
1134
1206
  const offeringId = key.slice(cfg.modelId.length + 1);
@@ -1146,16 +1218,29 @@ function createModelRecords(config) {
1146
1218
  }
1147
1219
  let refreshing = null;
1148
1220
  let refreshAgain = null;
1149
- function refreshAll() {
1221
+ function refreshAll(listed) {
1150
1222
  if (refreshing) return refreshAgain ??= refreshing.then(() => {
1151
1223
  refreshAgain = null;
1152
1224
  return refreshAll();
1153
1225
  });
1154
- refreshing = rereadKnown().finally(() => {
1226
+ refreshing = rereadKnown(listed).finally(() => {
1155
1227
  refreshing = null;
1156
1228
  });
1157
1229
  return refreshing;
1158
1230
  }
1231
+ function optionRowsOf(publicRows, offeringRows) {
1232
+ const routable = new Set(offeringRows.map((r) => r.model_id).filter((id) => typeof id === "string" && id.length > 0));
1233
+ return publicRows.filter((r) => typeof r.id === "string" && typeof r.name === "string" && routable.has(r.id)).map((r) => {
1234
+ const picked = {};
1235
+ for (const c of OPTION_COLUMN_LIST) if (c in r) picked[c] = r[c];
1236
+ return {
1237
+ ...picked,
1238
+ id: r.id,
1239
+ name: r.name,
1240
+ capabilities: requireCanonicalCapabilities(r.capabilities, { modelId: r.id, modelName: r.name })
1241
+ };
1242
+ });
1243
+ }
1159
1244
  async function readOptions() {
1160
1245
  const { data: offeringRows, error: offeringError } = await ai().from("model_offering").select("model_id");
1161
1246
  if (offeringError) throw offeringError;
@@ -1165,12 +1250,9 @@ function createModelRecords(config) {
1165
1250
  )
1166
1251
  ];
1167
1252
  if (routableIds.length === 0) return [];
1168
- const { data, error } = await ai().from("model_public").select("id, name, common_name, maker, cost_rating, speed_rating, is_primary, capabilities, is_deprecated, retired_at, successor_id").in("id", routableIds).order("common_name", { ascending: true, nullsFirst: false });
1253
+ const { data, error } = await ai().from("model_public").select(OPTION_COLUMNS).in("id", routableIds).order("common_name", { ascending: true, nullsFirst: false });
1169
1254
  if (error) throw error;
1170
- return (data ?? []).filter((r) => typeof r.id === "string" && typeof r.name === "string").map((r) => ({
1171
- ...r,
1172
- capabilities: requireCanonicalCapabilities(r.capabilities, { modelId: r.id, modelName: r.name })
1173
- }));
1255
+ return optionRowsOf(data ?? [], routableIds.map((model_id) => ({ model_id })));
1174
1256
  }
1175
1257
  const api = {
1176
1258
  getState: () => state2,
@@ -1327,10 +1409,11 @@ function createModelRecords(config) {
1327
1409
  if (!modelId || state2.entities[modelId] || state2.identityById[modelId] || status === "loading" || status === "succeeded" || status === "failed") {
1328
1410
  return inFlight.get(`identity:${modelId}`) ?? Promise.resolve();
1329
1411
  }
1412
+ if (catalogKeyColumn(modelId) === "id") return queueIdentity(modelId);
1330
1413
  return once(`identity:${modelId}`, async () => {
1331
1414
  update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, [modelId]: "loading" } }));
1332
1415
  try {
1333
- const { data, error } = await ai().from("model_definition").select("id, name, common_name, is_deprecated").eq(catalogKeyColumn(modelId), modelId).maybeSingle();
1416
+ const { data, error } = await ai().from("model_definition").select(IDENTITY_COLUMNS).eq(catalogKeyColumn(modelId), modelId).maybeSingle();
1334
1417
  if (error) throw error;
1335
1418
  if (!data) throw new Error(`AI model ${modelId} is not available (ai.model_definition has no such row)`);
1336
1419
  const row = data;
@@ -1346,52 +1429,10 @@ function createModelRecords(config) {
1346
1429
  });
1347
1430
  },
1348
1431
  loadIdentities(modelIds) {
1349
- const asked = (id) => {
1350
- const status = state2.identityStatusById[id];
1351
- return Boolean(state2.entities[id] || state2.identityById[id] || status === "loading" || status === "succeeded" || status === "failed");
1352
- };
1353
1432
  const ids = [...new Set(modelIds.filter((id) => Boolean(id)))];
1354
- const waits = [];
1355
- const wanted = [];
1356
- for (const id of ids) {
1357
- if (catalogKeyColumn(id) !== "id") waits.push(api.loadIdentity(id));
1358
- else if (asked(id)) waits.push(inFlight.get(`identity:${id}`) ?? Promise.resolve());
1359
- else wanted.push(id);
1360
- }
1361
- if (wanted.length > 0) {
1362
- const marked = Object.fromEntries(wanted.map((id) => [id, "loading"]));
1363
- update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, ...marked } }));
1364
- const run = (async () => {
1365
- try {
1366
- const { data, error } = await ai().from("model_definition").select("id, name, common_name, is_deprecated").in("id", wanted);
1367
- if (error) throw error;
1368
- const rows3 = Array.isArray(data) ? data : [];
1369
- const got = new Set(rows3.map((r) => r.id));
1370
- update((s4) => ({
1371
- ...s4,
1372
- identityById: { ...s4.identityById, ...Object.fromEntries(rows3.map((r) => [r.id, r])) },
1373
- identityStatusById: {
1374
- ...s4.identityStatusById,
1375
- ...Object.fromEntries(wanted.map((id) => [id, got.has(id) ? "succeeded" : "failed"]))
1376
- }
1377
- }));
1378
- } catch (err) {
1379
- errorSink({ code: "model-records-identity-failed", message: messageOf(err), context: { modelIds: wanted } });
1380
- update((s4) => ({
1381
- ...s4,
1382
- identityStatusById: { ...s4.identityStatusById, ...Object.fromEntries(wanted.map((id) => [id, "failed"])) }
1383
- }));
1384
- }
1385
- })();
1386
- for (const id of wanted) inFlight.set(`identity:${id}`, run);
1387
- void run.finally(() => {
1388
- for (const id of wanted) if (inFlight.get(`identity:${id}`) === run) inFlight.delete(`identity:${id}`);
1389
- });
1390
- waits.push(run);
1391
- }
1392
- return Promise.all(waits).then(() => void 0);
1433
+ return Promise.all(ids.map((id) => api.loadIdentity(id))).then(() => void 0);
1393
1434
  },
1394
- refresh: () => refreshAll(),
1435
+ refresh: (listed) => refreshAll(listed),
1395
1436
  seedModel({ modelId, config: config2, offerings }) {
1396
1437
  if (!modelId) return;
1397
1438
  if (!classesReadFor.has(modelId) && state2.modelClassStatus !== "succeeded") {
@@ -1840,7 +1881,7 @@ function aiRpc(client, fn) {
1840
1881
  if (!scope.rpc) throw new Error(`The model catalog client's schema("ai") has no rpc(), so ${fn} cannot be called.`);
1841
1882
  return scope.rpc(fn);
1842
1883
  }
1843
- async function readCatalog(client, variant) {
1884
+ async function readCatalog(client, variant, onListRows) {
1844
1885
  if (variant === "admin") {
1845
1886
  const [rpcRes, offeringsRes] = await Promise.all([
1846
1887
  aiRpc(client, "admin_model_catalog"),
@@ -1891,6 +1932,7 @@ async function readCatalog(client, variant) {
1891
1932
  ]);
1892
1933
  if (publicRes.error) throw publicRes.error;
1893
1934
  if (offeringRes.error) throw offeringRes.error;
1935
+ onListRows?.({ publicRows: rows(publicRes.data), offeringRows: rows(offeringRes.data) });
1894
1936
  const tiersByModel = /* @__PURE__ */ new Map();
1895
1937
  for (const r of rows(offeringRes.data)) {
1896
1938
  const modelId = str(r.model_id);
@@ -1930,14 +1972,14 @@ function createModelCatalog(config) {
1930
1972
  state2 = { ...state2, [variant]: next };
1931
1973
  for (const l of [...listeners]) l();
1932
1974
  };
1933
- const run = (variant) => {
1975
+ const run = (variant, onListRows) => {
1934
1976
  const pending = inFlight[variant];
1935
1977
  if (pending) return pending;
1936
1978
  const prior = state2[variant];
1937
1979
  if (prior.status === "idle" || prior.status === "error") set(variant, { ...prior, status: "loading", error: null });
1938
1980
  const job = (async () => {
1939
1981
  try {
1940
- const models = await readCatalog(client, variant);
1982
+ const models = await readCatalog(client, variant, onListRows);
1941
1983
  set(variant, { status: "ready", models, error: null, loadedAt: Date.now() });
1942
1984
  } catch (err) {
1943
1985
  const message = messageOf2(err);
@@ -1957,7 +1999,15 @@ function createModelCatalog(config) {
1957
1999
  });
1958
2000
  const refresh = async () => {
1959
2001
  const variants = Object.keys(state2).filter((v) => state2[v].status !== "idle");
1960
- await Promise.all([records.refresh(), ...variants.map((v) => run(v))]);
2002
+ let listed;
2003
+ const runs = variants.map((v) => {
2004
+ if (v !== "user" || inFlight.user) return run(v);
2005
+ let give = () => {
2006
+ };
2007
+ listed = new Promise((resolve) => give = resolve);
2008
+ return run("user", (rows3) => give(rows3)).finally(() => give(null));
2009
+ });
2010
+ await Promise.all([records.refresh(listed), ...runs]);
1961
2011
  };
1962
2012
  const stopFollowing = config.followChanges === false ? () => {
1963
2013
  } : onModelCatalogChanged(() => void refresh());
@@ -600,6 +600,15 @@ interface ModelRecord {
600
600
  published_to_web_by: string | null;
601
601
  }
602
602
  type ModelIdentityRow = Pick<ModelRecord, "id" | "name" | "common_name" | "is_deprecated">;
603
+ /**
604
+ * What the picker list's read of the catalog returned, raw: every `ai.model_public` row (with at least the
605
+ * options columns) and every `ai.model_offering` row (`ModelOfferingRow` columns). `records.refresh(listed)`
606
+ * answers the options list, the identities and the class index from it instead of reading them again.
607
+ */
608
+ interface ModelCatalogListRows {
609
+ publicRows: Array<Record<string, unknown>>;
610
+ offeringRows: ModelOfferingRow[];
611
+ }
603
612
  type ModelClassConfig = {
604
613
  modelId: string;
605
614
  controls: ModelJson | null;
@@ -693,8 +702,14 @@ interface ModelRecords {
693
702
  * the class index and slices, every pinned class's controls — and swap each in when it answers. The old
694
703
  * rows stay on screen until then (no blank pill, no flicker); a failed re-read keeps them and reports
695
704
  * through the error sink. Reads nothing that was never read. Never throws.
705
+ *
706
+ * Few requests, however many labels are mounted: full records are ONE `in (...)` read, identities are
707
+ * ONE `in (...)` read of only the ids no list row covers, model class slices are ONE read. `listed` is
708
+ * the picker list's own fresh re-read of `ai.model_public` + `ai.model_offering` (the catalog passes it,
709
+ * `createModelCatalog().refresh()`): when given, the options list, the identities it covers and the class
710
+ * index are answered from it, so one edit costs the page that one list read.
696
711
  */
697
- refresh(): Promise<void>;
712
+ refresh(listed?: Promise<ModelCatalogListRows | null>): Promise<void>;
698
713
  /**
699
714
  * Answer `loadModel` + `loadModelClasses` for one model from rows a batched
700
715
  * route read already returned (`chat.route_seed`): its `ai.model_config` row
@@ -2134,4 +2149,4 @@ interface ModelCatalogVisibilityOptions {
2134
2149
  declare function followModelCatalogVisibility(options: ModelCatalogVisibilityOptions): () => void;
2135
2150
 
2136
2151
  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, LLM_INTERACTIONS, 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, UNKNOWN_MODEL_KIND, acceptsTextInstructions, announceModelCatalogChanged, availableOnDevices, blendedPrice, buildModelClassIndex, buildSuccessionIndex, catalogFingerprint, catalogForApp, catalogKeyColumn, catalogKindOf, catalogKindVerdict, cheapestAdequateModel, classConfigKey, coalesceModelCatalogChanges, 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 };
2137
- export type { AdminOffering, AdminPricingBand, ArtifactUse, AuditCapabilitiesRecord, AvailabilityVariant, CapabilitiesParseContext, CatalogKeyColumn, CatalogKindInput, CatalogKindVerdict, 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, ModelCatalogChangeMeta, ModelCatalogChangePayload, ModelCatalogClient, ModelCatalogCoalesceOptions, 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 };
2152
+ export type { AdminOffering, AdminPricingBand, ArtifactUse, AuditCapabilitiesRecord, AvailabilityVariant, CapabilitiesParseContext, CatalogKeyColumn, CatalogKindInput, CatalogKindVerdict, 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, ModelCatalogChangeMeta, ModelCatalogChangePayload, ModelCatalogClient, ModelCatalogCoalesceOptions, ModelCatalogConfig, ModelCatalogDirectiveSubscribe, ModelCatalogErrorSink, ModelCatalogListRows, 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 };
@@ -600,6 +600,15 @@ interface ModelRecord {
600
600
  published_to_web_by: string | null;
601
601
  }
602
602
  type ModelIdentityRow = Pick<ModelRecord, "id" | "name" | "common_name" | "is_deprecated">;
603
+ /**
604
+ * What the picker list's read of the catalog returned, raw: every `ai.model_public` row (with at least the
605
+ * options columns) and every `ai.model_offering` row (`ModelOfferingRow` columns). `records.refresh(listed)`
606
+ * answers the options list, the identities and the class index from it instead of reading them again.
607
+ */
608
+ interface ModelCatalogListRows {
609
+ publicRows: Array<Record<string, unknown>>;
610
+ offeringRows: ModelOfferingRow[];
611
+ }
603
612
  type ModelClassConfig = {
604
613
  modelId: string;
605
614
  controls: ModelJson | null;
@@ -693,8 +702,14 @@ interface ModelRecords {
693
702
  * the class index and slices, every pinned class's controls — and swap each in when it answers. The old
694
703
  * rows stay on screen until then (no blank pill, no flicker); a failed re-read keeps them and reports
695
704
  * through the error sink. Reads nothing that was never read. Never throws.
705
+ *
706
+ * Few requests, however many labels are mounted: full records are ONE `in (...)` read, identities are
707
+ * ONE `in (...)` read of only the ids no list row covers, model class slices are ONE read. `listed` is
708
+ * the picker list's own fresh re-read of `ai.model_public` + `ai.model_offering` (the catalog passes it,
709
+ * `createModelCatalog().refresh()`): when given, the options list, the identities it covers and the class
710
+ * index are answered from it, so one edit costs the page that one list read.
696
711
  */
697
- refresh(): Promise<void>;
712
+ refresh(listed?: Promise<ModelCatalogListRows | null>): Promise<void>;
698
713
  /**
699
714
  * Answer `loadModel` + `loadModelClasses` for one model from rows a batched
700
715
  * route read already returned (`chat.route_seed`): its `ai.model_config` row
@@ -2134,4 +2149,4 @@ interface ModelCatalogVisibilityOptions {
2134
2149
  declare function followModelCatalogVisibility(options: ModelCatalogVisibilityOptions): () => void;
2135
2150
 
2136
2151
  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, LLM_INTERACTIONS, 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, UNKNOWN_MODEL_KIND, acceptsTextInstructions, announceModelCatalogChanged, availableOnDevices, blendedPrice, buildModelClassIndex, buildSuccessionIndex, catalogFingerprint, catalogForApp, catalogKeyColumn, catalogKindOf, catalogKindVerdict, cheapestAdequateModel, classConfigKey, coalesceModelCatalogChanges, 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 };
2137
- export type { AdminOffering, AdminPricingBand, ArtifactUse, AuditCapabilitiesRecord, AvailabilityVariant, CapabilitiesParseContext, CatalogKeyColumn, CatalogKindInput, CatalogKindVerdict, 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, ModelCatalogChangeMeta, ModelCatalogChangePayload, ModelCatalogClient, ModelCatalogCoalesceOptions, 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 };
2152
+ export type { AdminOffering, AdminPricingBand, ArtifactUse, AuditCapabilitiesRecord, AvailabilityVariant, CapabilitiesParseContext, CatalogKeyColumn, CatalogKindInput, CatalogKindVerdict, 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, ModelCatalogChangeMeta, ModelCatalogChangePayload, ModelCatalogClient, ModelCatalogCoalesceOptions, ModelCatalogConfig, ModelCatalogDirectiveSubscribe, ModelCatalogErrorSink, ModelCatalogListRows, 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 };