@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.
@@ -837,6 +837,9 @@ function withIdLists(state2) {
837
837
  const sort = (ids) => ids.sort((a, b) => label(a).localeCompare(label(b)));
838
838
  return { ...state2, activeIds: sort(active), deprecatedIds: sort(deprecated) };
839
839
  }
840
+ var IDENTITY_COLUMNS = "id, name, common_name, is_deprecated";
841
+ var OPTION_COLUMN_LIST = ["id", "name", "common_name", "maker", "cost_rating", "speed_rating", "is_primary", "capabilities", "is_deprecated", "retired_at", "successor_id"];
842
+ var OPTION_COLUMNS = OPTION_COLUMN_LIST.join(", ");
840
843
  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";
841
844
  function createModelRecords(config) {
842
845
  if (!config?.client) throw new Error("createModelRecords: `client` (a Supabase client) is required.");
@@ -861,13 +864,10 @@ function createModelRecords(config) {
861
864
  inFlight.set(key, p);
862
865
  return p;
863
866
  };
864
- async function readFullModel(modelId) {
865
- const relation = await isSignedIn() ? "model_config" : "model_public";
866
- const { data, error } = await ai().from(relation).select(MODEL_CONFIG_COLUMNS).eq(catalogKeyColumn(modelId), modelId).maybeSingle();
867
- if (error) throw error;
868
- const row = data;
869
- if (!row) throw new Error(`AI model ${modelId} is not available (ai.${relation} has no such row)`);
870
- if (!row.id || !row.name) throw new Error(`ai.${relation} returned a row without id/name for model ${modelId}`);
867
+ function toFullRecord(raw, relation, asked) {
868
+ const row = raw;
869
+ if (!row) throw new Error(`AI model ${asked} is not available (ai.${relation} has no such row)`);
870
+ if (!row.id || !row.name) throw new Error(`ai.${relation} returned a row without id/name for model ${asked}`);
871
871
  const read = {
872
872
  ...row,
873
873
  id: row.id,
@@ -877,74 +877,146 @@ function createModelRecords(config) {
877
877
  };
878
878
  return normalizeModel(read);
879
879
  }
880
- async function readOfferings(modelId) {
880
+ async function readFullModel(modelId) {
881
+ const relation = await isSignedIn() ? "model_config" : "model_public";
882
+ const { data, error } = await ai().from(relation).select(MODEL_CONFIG_COLUMNS).eq(catalogKeyColumn(modelId), modelId).maybeSingle();
883
+ if (error) throw error;
884
+ return toFullRecord(data, relation, modelId);
885
+ }
886
+ async function readOfferings(modelIds) {
881
887
  const q = ai().from("model_offering").select("model_id, offering_id, served_via, served_via_endpoint_id, priority");
882
- const { data, error } = await (modelId ? q.eq("model_id", modelId) : q);
888
+ const { data, error } = await (modelIds ? modelIds.length === 1 ? q.eq("model_id", modelIds[0]) : q.in("model_id", modelIds) : q);
883
889
  if (error) throw error;
884
890
  return data ?? [];
885
891
  }
886
- async function rereadKnown() {
892
+ const mergeClassSlice = (rows3) => update((s4) => {
893
+ if (s4.modelClassStatus === "succeeded") return s4;
894
+ const slice = buildModelClassIndex(rows3);
895
+ const base = s4.modelClassIndex ?? { classesByModelId: {}, classOfOffering: {} };
896
+ return { ...s4, modelClassIndex: { classesByModelId: { ...base.classesByModelId, ...slice.classesByModelId }, classOfOffering: { ...base.classOfOffering, ...slice.classOfOffering } } };
897
+ });
898
+ let identityTick = null;
899
+ async function readIdentitiesNow(wanted) {
900
+ try {
901
+ const { data, error } = await ai().from("model_definition").select(IDENTITY_COLUMNS).in("id", wanted);
902
+ if (error) throw error;
903
+ const rows3 = Array.isArray(data) ? data : [];
904
+ const got = new Set(rows3.map((r) => r.id));
905
+ const missing = wanted.filter((id) => !got.has(id));
906
+ 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 } });
907
+ update((s4) => ({
908
+ ...s4,
909
+ identityById: { ...s4.identityById, ...Object.fromEntries(rows3.map((r) => [r.id, r])) },
910
+ identityStatusById: { ...s4.identityStatusById, ...Object.fromEntries(wanted.map((id) => [id, got.has(id) ? "succeeded" : "failed"])) }
911
+ }));
912
+ } catch (err) {
913
+ errorSink({ code: "model-records-identity-failed", message: messageOf(err), context: { modelIds: wanted } });
914
+ update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, ...Object.fromEntries(wanted.map((id) => [id, "failed"])) } }));
915
+ }
916
+ }
917
+ function queueIdentity(id) {
918
+ if (!identityTick) {
919
+ const ids = /* @__PURE__ */ new Set();
920
+ const done = Promise.resolve().then(() => {
921
+ identityTick = null;
922
+ return readIdentitiesNow([...ids]);
923
+ });
924
+ identityTick = { ids, done };
925
+ void done.finally(() => {
926
+ for (const queued of ids) if (inFlight.get(`identity:${queued}`) === done) inFlight.delete(`identity:${queued}`);
927
+ });
928
+ }
929
+ identityTick.ids.add(id);
930
+ inFlight.set(`identity:${id}`, identityTick.done);
931
+ update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, [id]: "loading" } }));
932
+ return identityTick.done;
933
+ }
934
+ async function rereadKnown(listed) {
887
935
  const known = state2;
888
936
  const jobs = [];
889
937
  const fail = (code, message, context) => errorSink({ code, message: `Catalog refresh: ${message}`, ...context ? { context } : {} });
890
- if (known.fetchScope === "active" || known.fetchScope === "all") {
891
- jobs.push(
892
- readOptions().then(
893
- (rows3) => update((s4) => {
938
+ const fed = listed ? listed.then((f) => f, () => null) : Promise.resolve(null);
939
+ const optionsWereRead = known.fetchScope === "active" || known.fetchScope === "all";
940
+ const freshOptions = optionsWereRead ? fed.then((f) => f ? optionRowsOf(f.publicRows, f.offeringRows) : readOptions()) : Promise.resolve(null);
941
+ jobs.push(
942
+ freshOptions.then(
943
+ (rows3) => {
944
+ if (!rows3) return;
945
+ update((s4) => {
894
946
  const entities = { ...s4.entities };
895
947
  for (const row of rows3) {
896
948
  const existing = entities[row.id];
897
949
  entities[row.id] = { ...emptyRecord(), ...existing, ...row, _fetchType: existing?._fetchType === "full" ? "full" : "options" };
898
950
  }
899
951
  return withIdLists({ ...s4, entities, lastFetched: Date.now(), error: null });
900
- }),
901
- (err) => fail("model-records-refresh-options-failed", messageOf(err))
902
- )
903
- );
904
- }
905
- for (const [id, record] of Object.entries(known.entities)) {
906
- if (record._fetchType !== "full") continue;
952
+ });
953
+ },
954
+ (err) => fail("model-records-refresh-options-failed", messageOf(err))
955
+ )
956
+ );
957
+ const fullIds = Object.entries(known.entities).filter(([, r]) => r._fetchType === "full").map(([id]) => id);
958
+ if (fullIds.length > 0) {
907
959
  jobs.push(
908
- readFullModel(id).then(
909
- (fresh) => update((s4) => {
910
- const existing = s4.entities[fresh.id];
911
- return withIdLists({ ...s4, entities: { ...s4.entities, [fresh.id]: { ...emptyRecord(), ...existing, ...fresh, maker: fresh.maker ?? existing?.maker ?? null, _fetchType: "full" } } });
912
- }),
913
- (err) => fail("model-records-refresh-model-failed", `${id}: ${messageOf(err)}`, { modelId: id })
914
- )
960
+ (async () => {
961
+ const relation = await isSignedIn() ? "model_config" : "model_public";
962
+ const { data, error } = await ai().from(relation).select(MODEL_CONFIG_COLUMNS).in("id", fullIds);
963
+ if (error) throw error;
964
+ const byId2 = new Map((Array.isArray(data) ? data : []).map((r) => [r.id, r]));
965
+ for (const id of fullIds) {
966
+ try {
967
+ const fresh = toFullRecord(byId2.get(id) ?? null, relation, id);
968
+ update((s4) => {
969
+ const existing = s4.entities[fresh.id];
970
+ return withIdLists({ ...s4, entities: { ...s4.entities, [fresh.id]: { ...emptyRecord(), ...existing, ...fresh, maker: fresh.maker ?? existing?.maker ?? null, _fetchType: "full" } } });
971
+ });
972
+ } catch (err) {
973
+ fail("model-records-refresh-model-failed", `${id}: ${messageOf(err)}`, { modelId: id });
974
+ }
975
+ }
976
+ })().catch((err) => fail("model-records-refresh-model-failed", `${fullIds.join(", ")}: ${messageOf(err)}`, { modelIds: fullIds }))
915
977
  );
916
978
  }
917
- for (const id of Object.keys(known.identityById)) {
979
+ const identityKeys = Object.entries(known.identityById);
980
+ if (identityKeys.length > 0) {
918
981
  jobs.push(
919
- Promise.resolve(ai().from("model_definition").select("id, name, common_name, is_deprecated").eq(catalogKeyColumn(id), id).maybeSingle()).then(
920
- ({ data, error }) => {
982
+ (async () => {
983
+ const [f, opts] = await Promise.all([fed, freshOptions.catch(() => null)]);
984
+ const covered = /* @__PURE__ */ new Map();
985
+ for (const r of [...f?.publicRows ?? [], ...opts ?? []]) {
986
+ if (typeof r.id === "string" && typeof r.name === "string") {
987
+ covered.set(r.id, { id: r.id, name: r.name, common_name: r.common_name ?? null, is_deprecated: r.is_deprecated ?? null });
988
+ }
989
+ }
990
+ const wanted = [...new Set(identityKeys.map(([, row]) => row.id))].filter((id) => !covered.has(id) && known.entities[id]?._fetchType !== "full");
991
+ if (wanted.length > 0) {
992
+ const { data, error } = await ai().from("model_definition").select(IDENTITY_COLUMNS).in("id", wanted);
921
993
  if (error) throw error;
922
- if (data) update((s4) => ({ ...s4, identityById: { ...s4.identityById, [id]: data } }));
994
+ for (const row of Array.isArray(data) ? data : []) covered.set(row.id, row);
995
+ }
996
+ const next = {};
997
+ for (const [key, row] of identityKeys) {
998
+ const fresh = covered.get(row.id);
999
+ if (fresh) next[key] = fresh;
923
1000
  }
924
- ).catch((err) => fail("model-records-refresh-identity-failed", `${id}: ${messageOf(err)}`, { modelId: id }))
1001
+ if (Object.keys(next).length > 0) update((s4) => ({ ...s4, identityById: { ...s4.identityById, ...next } }));
1002
+ })().catch((err) => fail("model-records-refresh-identity-failed", messageOf(err)))
925
1003
  );
926
1004
  }
927
1005
  if (known.modelClassStatus === "succeeded") {
928
1006
  jobs.push(
929
- readOfferings().then(
1007
+ fed.then((f) => f?.offeringRows ?? readOfferings()).then(
930
1008
  (rows3) => update((s4) => ({ ...s4, modelClassIndex: buildModelClassIndex(rows3) })),
931
1009
  (err) => fail("model-records-refresh-classes-failed", messageOf(err))
932
1010
  )
933
1011
  );
934
- } else {
935
- for (const id of classesReadFor) {
936
- jobs.push(
937
- readOfferings(id).then(
938
- (rows3) => update((s4) => {
939
- if (s4.modelClassStatus === "succeeded") return s4;
940
- const slice = buildModelClassIndex(rows3);
941
- const base = s4.modelClassIndex ?? { classesByModelId: {}, classOfOffering: {} };
942
- return { ...s4, modelClassIndex: { classesByModelId: { ...base.classesByModelId, ...slice.classesByModelId }, classOfOffering: { ...base.classOfOffering, ...slice.classOfOffering } } };
943
- }),
944
- (err) => fail("model-records-refresh-classes-failed", `${id}: ${messageOf(err)}`, { modelId: id })
945
- )
946
- );
947
- }
1012
+ } else if (classesReadFor.size > 0) {
1013
+ const ids = [...classesReadFor];
1014
+ jobs.push(
1015
+ fed.then((f) => f ? f.offeringRows.filter((o) => o.model_id !== null && classesReadFor.has(o.model_id)) : readOfferings(ids)).then(
1016
+ (rows3) => mergeClassSlice(rows3),
1017
+ (err) => fail("model-records-refresh-classes-failed", `${ids.join(", ")}: ${messageOf(err)}`, { modelIds: ids })
1018
+ )
1019
+ );
948
1020
  }
949
1021
  for (const [key, cfg] of Object.entries(known.classConfigByOffering)) {
950
1022
  const offeringId = key.slice(cfg.modelId.length + 1);
@@ -962,16 +1034,29 @@ function createModelRecords(config) {
962
1034
  }
963
1035
  let refreshing = null;
964
1036
  let refreshAgain = null;
965
- function refreshAll() {
1037
+ function refreshAll(listed) {
966
1038
  if (refreshing) return refreshAgain ??= refreshing.then(() => {
967
1039
  refreshAgain = null;
968
1040
  return refreshAll();
969
1041
  });
970
- refreshing = rereadKnown().finally(() => {
1042
+ refreshing = rereadKnown(listed).finally(() => {
971
1043
  refreshing = null;
972
1044
  });
973
1045
  return refreshing;
974
1046
  }
1047
+ function optionRowsOf(publicRows, offeringRows) {
1048
+ const routable = new Set(offeringRows.map((r) => r.model_id).filter((id) => typeof id === "string" && id.length > 0));
1049
+ return publicRows.filter((r) => typeof r.id === "string" && typeof r.name === "string" && routable.has(r.id)).map((r) => {
1050
+ const picked = {};
1051
+ for (const c of OPTION_COLUMN_LIST) if (c in r) picked[c] = r[c];
1052
+ return {
1053
+ ...picked,
1054
+ id: r.id,
1055
+ name: r.name,
1056
+ capabilities: requireCanonicalCapabilities(r.capabilities, { modelId: r.id, modelName: r.name })
1057
+ };
1058
+ });
1059
+ }
975
1060
  async function readOptions() {
976
1061
  const { data: offeringRows, error: offeringError } = await ai().from("model_offering").select("model_id");
977
1062
  if (offeringError) throw offeringError;
@@ -981,12 +1066,9 @@ function createModelRecords(config) {
981
1066
  )
982
1067
  ];
983
1068
  if (routableIds.length === 0) return [];
984
- 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 });
1069
+ const { data, error } = await ai().from("model_public").select(OPTION_COLUMNS).in("id", routableIds).order("common_name", { ascending: true, nullsFirst: false });
985
1070
  if (error) throw error;
986
- return (data ?? []).filter((r) => typeof r.id === "string" && typeof r.name === "string").map((r) => ({
987
- ...r,
988
- capabilities: requireCanonicalCapabilities(r.capabilities, { modelId: r.id, modelName: r.name })
989
- }));
1071
+ return optionRowsOf(data ?? [], routableIds.map((model_id) => ({ model_id })));
990
1072
  }
991
1073
  const api = {
992
1074
  getState: () => state2,
@@ -1143,10 +1225,11 @@ function createModelRecords(config) {
1143
1225
  if (!modelId || state2.entities[modelId] || state2.identityById[modelId] || status === "loading" || status === "succeeded" || status === "failed") {
1144
1226
  return inFlight.get(`identity:${modelId}`) ?? Promise.resolve();
1145
1227
  }
1228
+ if (catalogKeyColumn(modelId) === "id") return queueIdentity(modelId);
1146
1229
  return once(`identity:${modelId}`, async () => {
1147
1230
  update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, [modelId]: "loading" } }));
1148
1231
  try {
1149
- const { data, error } = await ai().from("model_definition").select("id, name, common_name, is_deprecated").eq(catalogKeyColumn(modelId), modelId).maybeSingle();
1232
+ const { data, error } = await ai().from("model_definition").select(IDENTITY_COLUMNS).eq(catalogKeyColumn(modelId), modelId).maybeSingle();
1150
1233
  if (error) throw error;
1151
1234
  if (!data) throw new Error(`AI model ${modelId} is not available (ai.model_definition has no such row)`);
1152
1235
  const row = data;
@@ -1162,52 +1245,10 @@ function createModelRecords(config) {
1162
1245
  });
1163
1246
  },
1164
1247
  loadIdentities(modelIds) {
1165
- const asked = (id) => {
1166
- const status = state2.identityStatusById[id];
1167
- return Boolean(state2.entities[id] || state2.identityById[id] || status === "loading" || status === "succeeded" || status === "failed");
1168
- };
1169
1248
  const ids = [...new Set(modelIds.filter((id) => Boolean(id)))];
1170
- const waits = [];
1171
- const wanted = [];
1172
- for (const id of ids) {
1173
- if (catalogKeyColumn(id) !== "id") waits.push(api.loadIdentity(id));
1174
- else if (asked(id)) waits.push(inFlight.get(`identity:${id}`) ?? Promise.resolve());
1175
- else wanted.push(id);
1176
- }
1177
- if (wanted.length > 0) {
1178
- const marked = Object.fromEntries(wanted.map((id) => [id, "loading"]));
1179
- update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, ...marked } }));
1180
- const run = (async () => {
1181
- try {
1182
- const { data, error } = await ai().from("model_definition").select("id, name, common_name, is_deprecated").in("id", wanted);
1183
- if (error) throw error;
1184
- const rows3 = Array.isArray(data) ? data : [];
1185
- const got = new Set(rows3.map((r) => r.id));
1186
- update((s4) => ({
1187
- ...s4,
1188
- identityById: { ...s4.identityById, ...Object.fromEntries(rows3.map((r) => [r.id, r])) },
1189
- identityStatusById: {
1190
- ...s4.identityStatusById,
1191
- ...Object.fromEntries(wanted.map((id) => [id, got.has(id) ? "succeeded" : "failed"]))
1192
- }
1193
- }));
1194
- } catch (err) {
1195
- errorSink({ code: "model-records-identity-failed", message: messageOf(err), context: { modelIds: wanted } });
1196
- update((s4) => ({
1197
- ...s4,
1198
- identityStatusById: { ...s4.identityStatusById, ...Object.fromEntries(wanted.map((id) => [id, "failed"])) }
1199
- }));
1200
- }
1201
- })();
1202
- for (const id of wanted) inFlight.set(`identity:${id}`, run);
1203
- void run.finally(() => {
1204
- for (const id of wanted) if (inFlight.get(`identity:${id}`) === run) inFlight.delete(`identity:${id}`);
1205
- });
1206
- waits.push(run);
1207
- }
1208
- return Promise.all(waits).then(() => void 0);
1249
+ return Promise.all(ids.map((id) => api.loadIdentity(id))).then(() => void 0);
1209
1250
  },
1210
- refresh: () => refreshAll(),
1251
+ refresh: (listed) => refreshAll(listed),
1211
1252
  seedModel({ modelId, config: config2, offerings }) {
1212
1253
  if (!modelId) return;
1213
1254
  if (!classesReadFor.has(modelId) && state2.modelClassStatus !== "succeeded") {
@@ -1656,7 +1697,7 @@ function aiRpc(client, fn) {
1656
1697
  if (!scope.rpc) throw new Error(`The model catalog client's schema("ai") has no rpc(), so ${fn} cannot be called.`);
1657
1698
  return scope.rpc(fn);
1658
1699
  }
1659
- async function readCatalog(client, variant) {
1700
+ async function readCatalog(client, variant, onListRows) {
1660
1701
  if (variant === "admin") {
1661
1702
  const [rpcRes, offeringsRes] = await Promise.all([
1662
1703
  aiRpc(client, "admin_model_catalog"),
@@ -1707,6 +1748,7 @@ async function readCatalog(client, variant) {
1707
1748
  ]);
1708
1749
  if (publicRes.error) throw publicRes.error;
1709
1750
  if (offeringRes.error) throw offeringRes.error;
1751
+ onListRows?.({ publicRows: rows(publicRes.data), offeringRows: rows(offeringRes.data) });
1710
1752
  const tiersByModel = /* @__PURE__ */ new Map();
1711
1753
  for (const r of rows(offeringRes.data)) {
1712
1754
  const modelId = str(r.model_id);
@@ -1746,14 +1788,14 @@ function createModelCatalog(config) {
1746
1788
  state2 = { ...state2, [variant]: next };
1747
1789
  for (const l of [...listeners]) l();
1748
1790
  };
1749
- const run = (variant) => {
1791
+ const run = (variant, onListRows) => {
1750
1792
  const pending = inFlight[variant];
1751
1793
  if (pending) return pending;
1752
1794
  const prior = state2[variant];
1753
1795
  if (prior.status === "idle" || prior.status === "error") set(variant, { ...prior, status: "loading", error: null });
1754
1796
  const job = (async () => {
1755
1797
  try {
1756
- const models = await readCatalog(client, variant);
1798
+ const models = await readCatalog(client, variant, onListRows);
1757
1799
  set(variant, { status: "ready", models, error: null, loadedAt: Date.now() });
1758
1800
  } catch (err) {
1759
1801
  const message = messageOf2(err);
@@ -1773,7 +1815,15 @@ function createModelCatalog(config) {
1773
1815
  });
1774
1816
  const refresh = async () => {
1775
1817
  const variants = Object.keys(state2).filter((v) => state2[v].status !== "idle");
1776
- await Promise.all([records.refresh(), ...variants.map((v) => run(v))]);
1818
+ let listed;
1819
+ const runs = variants.map((v) => {
1820
+ if (v !== "user" || inFlight.user) return run(v);
1821
+ let give = () => {
1822
+ };
1823
+ listed = new Promise((resolve) => give = resolve);
1824
+ return run("user", (rows3) => give(rows3)).finally(() => give(null));
1825
+ });
1826
+ await Promise.all([records.refresh(listed), ...runs]);
1777
1827
  };
1778
1828
  const stopFollowing = config.followChanges === false ? () => {
1779
1829
  } : onModelCatalogChanged(() => void refresh());
@@ -288,6 +288,15 @@ interface ModelRecord {
288
288
  published_to_web_by: string | null;
289
289
  }
290
290
  type ModelIdentityRow = Pick<ModelRecord, "id" | "name" | "common_name" | "is_deprecated">;
291
+ /**
292
+ * What the picker list's read of the catalog returned, raw: every `ai.model_public` row (with at least the
293
+ * options columns) and every `ai.model_offering` row (`ModelOfferingRow` columns). `records.refresh(listed)`
294
+ * answers the options list, the identities and the class index from it instead of reading them again.
295
+ */
296
+ interface ModelCatalogListRows {
297
+ publicRows: Array<Record<string, unknown>>;
298
+ offeringRows: ModelOfferingRow[];
299
+ }
291
300
  type ModelClassConfig = {
292
301
  modelId: string;
293
302
  controls: ModelJson | null;
@@ -343,8 +352,14 @@ interface ModelRecords {
343
352
  * the class index and slices, every pinned class's controls — and swap each in when it answers. The old
344
353
  * rows stay on screen until then (no blank pill, no flicker); a failed re-read keeps them and reports
345
354
  * through the error sink. Reads nothing that was never read. Never throws.
355
+ *
356
+ * Few requests, however many labels are mounted: full records are ONE `in (...)` read, identities are
357
+ * ONE `in (...)` read of only the ids no list row covers, model class slices are ONE read. `listed` is
358
+ * the picker list's own fresh re-read of `ai.model_public` + `ai.model_offering` (the catalog passes it,
359
+ * `createModelCatalog().refresh()`): when given, the options list, the identities it covers and the class
360
+ * index are answered from it, so one edit costs the page that one list read.
346
361
  */
347
- refresh(): Promise<void>;
362
+ refresh(listed?: Promise<ModelCatalogListRows | null>): Promise<void>;
348
363
  /**
349
364
  * Answer `loadModel` + `loadModelClasses` for one model from rows a batched
350
365
  * route read already returned (`chat.route_seed`): its `ai.model_config` row
@@ -288,6 +288,15 @@ interface ModelRecord {
288
288
  published_to_web_by: string | null;
289
289
  }
290
290
  type ModelIdentityRow = Pick<ModelRecord, "id" | "name" | "common_name" | "is_deprecated">;
291
+ /**
292
+ * What the picker list's read of the catalog returned, raw: every `ai.model_public` row (with at least the
293
+ * options columns) and every `ai.model_offering` row (`ModelOfferingRow` columns). `records.refresh(listed)`
294
+ * answers the options list, the identities and the class index from it instead of reading them again.
295
+ */
296
+ interface ModelCatalogListRows {
297
+ publicRows: Array<Record<string, unknown>>;
298
+ offeringRows: ModelOfferingRow[];
299
+ }
291
300
  type ModelClassConfig = {
292
301
  modelId: string;
293
302
  controls: ModelJson | null;
@@ -343,8 +352,14 @@ interface ModelRecords {
343
352
  * the class index and slices, every pinned class's controls — and swap each in when it answers. The old
344
353
  * rows stay on screen until then (no blank pill, no flicker); a failed re-read keeps them and reports
345
354
  * through the error sink. Reads nothing that was never read. Never throws.
355
+ *
356
+ * Few requests, however many labels are mounted: full records are ONE `in (...)` read, identities are
357
+ * ONE `in (...)` read of only the ids no list row covers, model class slices are ONE read. `listed` is
358
+ * the picker list's own fresh re-read of `ai.model_public` + `ai.model_offering` (the catalog passes it,
359
+ * `createModelCatalog().refresh()`): when given, the options list, the identities it covers and the class
360
+ * index are answered from it, so one edit costs the page that one list read.
346
361
  */
347
- refresh(): Promise<void>;
362
+ refresh(listed?: Promise<ModelCatalogListRows | null>): Promise<void>;
348
363
  /**
349
364
  * Answer `loadModel` + `loadModelClasses` for one model from rows a batched
350
365
  * route read already returned (`chat.route_seed`): its `ai.model_config` row
@@ -1107,6 +1107,7 @@ var import_design_system10 = require("@ai-matrx/design-system");
1107
1107
  var import_data_table = require("@ai-matrx/design-system/data-table");
1108
1108
  var import_design_system11 = require("@ai-matrx/design-system");
1109
1109
  var import_text_case4 = require("@ai-matrx/kit/text-case");
1110
+ var import_error_sentence = require("@ai-matrx/design-system/error-sentence");
1110
1111
  var import_jsx_runtime14 = require("react/jsx-runtime");
1111
1112
  var SILENT = { success: () => {
1112
1113
  }, error: () => {
@@ -1275,7 +1276,7 @@ function ContractBreak({ version, ports }) {
1275
1276
  await ports.onDeclare(version.version_number, kind);
1276
1277
  } catch (error) {
1277
1278
  setDeclared(previous);
1278
- ports.notify.error(`Couldn't record the contract break: ${error instanceof Error ? error.message : String(error)}`);
1279
+ ports.notify.error("Break not recorded", { description: (0, import_error_sentence.peopleErrorSentence)(error, null, { card: true }) });
1279
1280
  return;
1280
1281
  }
1281
1282
  {
@@ -97,7 +97,10 @@ interface VersionHistoryLoad {
97
97
  /** Where the timeline says a result: the host's toasts (sonner on the web). */
98
98
  interface VersionTimelineNotify {
99
99
  success: (message: string) => void;
100
- error: (message: string) => void;
100
+ /** A card: a short title and an optional line (design-system NOTICE_TEXT_BUDGET). */
101
+ error: (message: string, options?: {
102
+ description?: string;
103
+ }) => void;
101
104
  }
102
105
  interface VersionHistoryTimelineProps {
103
106
  agentId: string;
@@ -97,7 +97,10 @@ interface VersionHistoryLoad {
97
97
  /** Where the timeline says a result: the host's toasts (sonner on the web). */
98
98
  interface VersionTimelineNotify {
99
99
  success: (message: string) => void;
100
- error: (message: string) => void;
100
+ /** A card: a short title and an optional line (design-system NOTICE_TEXT_BUDGET). */
101
+ error: (message: string, options?: {
102
+ description?: string;
103
+ }) => void;
101
104
  }
102
105
  interface VersionHistoryTimelineProps {
103
106
  agentId: string;
@@ -1111,6 +1111,7 @@ import {
1111
1111
  } from "@ai-matrx/design-system/data-table";
1112
1112
  import { RecordId } from "@ai-matrx/design-system";
1113
1113
  import { humanizeIdentifier as humanizeIdentifier4 } from "@ai-matrx/kit/text-case";
1114
+ import { peopleErrorSentence } from "@ai-matrx/design-system/error-sentence";
1114
1115
  import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1115
1116
  var SILENT = { success: () => {
1116
1117
  }, error: () => {
@@ -1279,7 +1280,7 @@ function ContractBreak({ version, ports }) {
1279
1280
  await ports.onDeclare(version.version_number, kind);
1280
1281
  } catch (error) {
1281
1282
  setDeclared(previous);
1282
- ports.notify.error(`Couldn't record the contract break: ${error instanceof Error ? error.message : String(error)}`);
1283
+ ports.notify.error("Break not recorded", { description: peopleErrorSentence(error, null, { card: true }) });
1283
1284
  return;
1284
1285
  }
1285
1286
  {
@@ -0,0 +1,20 @@
1
+ # `@ai-matrx/agents/generated` — the server's client contract
2
+
3
+ Every file here is GENERATED from the aidream server source. Never edit one by hand.
4
+
5
+ - `uv run python scripts/generate_types.py` (from the aidream root) writes the stream events,
6
+ LLM enums/params, source attribution, workflow events and canvas chat files.
7
+ - `uv run python scripts/generate_types.py api --direct` writes `openapi.json`
8
+ through `../scripts/typegen/write-api-types.mjs`: contract pin, then the drop guard over every
9
+ client checkout beside aidream, then normalization. A refusal writes
10
+ nothing (DD-128). `scripts/generate_types.py --check` runs
11
+ `../scripts/typegen/check-api-types-fresh.mjs`: a hand-edited `openapi.json`
12
+ fails it (release health `frontend-type-snapshots`).
13
+ - `scripts/mandates_generate.py` writes `provision-offers.ts`.
14
+ - `hr-contracts.api-types.ts`: `node apps/shared/matrx-agents/scripts/typegen/gen-hr-contracts.mjs`.
15
+
16
+ The TypeScript view of the contract is `@ai-matrx/api/types` (`components`, `paths`), generated by the api package from `api-contract/`.
17
+
18
+ Clients import `@ai-matrx/agents/generated/<name>` and keep NO copy (chat package
19
+ independence P15, 2026-10-02). A server change and its types land in the same aidream commit;
20
+ the publish train ships them.