@ai-matrx/agents 0.59.268 → 0.59.269

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.
@@ -247,13 +247,13 @@ function setModelDiagnosticsSink(sink) {
247
247
  }
248
248
  function reportModelDiagnostic(dedupeKey, entry) {
249
249
  try {
250
- const s3 = state();
251
- if (s3.reported.has(dedupeKey)) return;
252
- s3.reported.add(dedupeKey);
250
+ const s4 = state();
251
+ if (s4.reported.has(dedupeKey)) return;
252
+ s4.reported.add(dedupeKey);
253
253
  console.warn(`[parseCapabilities] ${entry.message}`);
254
- if (s3.sink) s3.sink(entry);
255
- else if (!s3.announced) {
256
- s3.announced = true;
254
+ if (s4.sink) s4.sink(entry);
255
+ else if (!s4.announced) {
256
+ s4.announced = true;
257
257
  console.error(
258
258
  "[@ai-matrx/agents/models] No diagnostics sink is bound \u2014 capability-vocabulary drift reaches the console only. Call setModelDiagnosticsSink(...) with your error store."
259
259
  );
@@ -926,13 +926,13 @@ function createModelRecords(config) {
926
926
  if (known.fetchScope === "active" || known.fetchScope === "all") {
927
927
  jobs.push(
928
928
  readOptions().then(
929
- (rows3) => update((s3) => {
930
- const entities = { ...s3.entities };
929
+ (rows3) => update((s4) => {
930
+ const entities = { ...s4.entities };
931
931
  for (const row of rows3) {
932
932
  const existing = entities[row.id];
933
933
  entities[row.id] = { ...emptyRecord(), ...existing, ...row, _fetchType: existing?._fetchType === "full" ? "full" : "options" };
934
934
  }
935
- return withIdLists({ ...s3, entities, lastFetched: Date.now(), error: null });
935
+ return withIdLists({ ...s4, entities, lastFetched: Date.now(), error: null });
936
936
  }),
937
937
  (err) => fail("model-records-refresh-options-failed", messageOf(err))
938
938
  )
@@ -942,9 +942,9 @@ function createModelRecords(config) {
942
942
  if (record._fetchType !== "full") continue;
943
943
  jobs.push(
944
944
  readFullModel(id).then(
945
- (fresh) => update((s3) => {
946
- const existing = s3.entities[fresh.id];
947
- return withIdLists({ ...s3, entities: { ...s3.entities, [fresh.id]: { ...emptyRecord(), ...existing, ...fresh, maker: fresh.maker ?? existing?.maker ?? null, _fetchType: "full" } } });
945
+ (fresh) => update((s4) => {
946
+ const existing = s4.entities[fresh.id];
947
+ return withIdLists({ ...s4, entities: { ...s4.entities, [fresh.id]: { ...emptyRecord(), ...existing, ...fresh, maker: fresh.maker ?? existing?.maker ?? null, _fetchType: "full" } } });
948
948
  }),
949
949
  (err) => fail("model-records-refresh-model-failed", `${id}: ${messageOf(err)}`, { modelId: id })
950
950
  )
@@ -955,7 +955,7 @@ function createModelRecords(config) {
955
955
  Promise.resolve(ai().from("model_definition").select("id, name, common_name, is_deprecated").eq(catalogKeyColumn(id), id).maybeSingle()).then(
956
956
  ({ data, error }) => {
957
957
  if (error) throw error;
958
- if (data) update((s3) => ({ ...s3, identityById: { ...s3.identityById, [id]: data } }));
958
+ if (data) update((s4) => ({ ...s4, identityById: { ...s4.identityById, [id]: data } }));
959
959
  }
960
960
  ).catch((err) => fail("model-records-refresh-identity-failed", `${id}: ${messageOf(err)}`, { modelId: id }))
961
961
  );
@@ -963,7 +963,7 @@ function createModelRecords(config) {
963
963
  if (known.modelClassStatus === "succeeded") {
964
964
  jobs.push(
965
965
  readOfferings().then(
966
- (rows3) => update((s3) => ({ ...s3, modelClassIndex: buildModelClassIndex(rows3) })),
966
+ (rows3) => update((s4) => ({ ...s4, modelClassIndex: buildModelClassIndex(rows3) })),
967
967
  (err) => fail("model-records-refresh-classes-failed", messageOf(err))
968
968
  )
969
969
  );
@@ -971,11 +971,11 @@ function createModelRecords(config) {
971
971
  for (const id of classesReadFor) {
972
972
  jobs.push(
973
973
  readOfferings(id).then(
974
- (rows3) => update((s3) => {
975
- if (s3.modelClassStatus === "succeeded") return s3;
974
+ (rows3) => update((s4) => {
975
+ if (s4.modelClassStatus === "succeeded") return s4;
976
976
  const slice = buildModelClassIndex(rows3);
977
- const base = s3.modelClassIndex ?? { classesByModelId: {}, classOfOffering: {} };
978
- return { ...s3, modelClassIndex: { classesByModelId: { ...base.classesByModelId, ...slice.classesByModelId }, classOfOffering: { ...base.classOfOffering, ...slice.classOfOffering } } };
977
+ const base = s4.modelClassIndex ?? { classesByModelId: {}, classOfOffering: {} };
978
+ return { ...s4, modelClassIndex: { classesByModelId: { ...base.classesByModelId, ...slice.classesByModelId }, classOfOffering: { ...base.classOfOffering, ...slice.classOfOffering } } };
979
979
  }),
980
980
  (err) => fail("model-records-refresh-classes-failed", `${id}: ${messageOf(err)}`, { modelId: id })
981
981
  )
@@ -989,7 +989,7 @@ function createModelRecords(config) {
989
989
  ({ data, error }) => {
990
990
  if (error) throw error;
991
991
  const next = data && typeof data === "object" && !Array.isArray(data) ? data : null;
992
- update((s3) => ({ ...s3, classConfigByOffering: { ...s3.classConfigByOffering, [key]: { modelId: cfg.modelId, controls: next?.controls ?? null, constraints: next?.constraints ?? null } } }));
992
+ update((s4) => ({ ...s4, classConfigByOffering: { ...s4.classConfigByOffering, [key]: { modelId: cfg.modelId, controls: next?.controls ?? null, constraints: next?.constraints ?? null } } }));
993
993
  }
994
994
  ).catch((err) => fail("model-records-refresh-class-config-failed", `${key}: ${messageOf(err)}`))
995
995
  );
@@ -1035,29 +1035,29 @@ function createModelRecords(config) {
1035
1035
  loadOptions() {
1036
1036
  if (state2.fetchScope === "active" || state2.fetchScope === "all") return Promise.resolve();
1037
1037
  return once("options", async () => {
1038
- update((s3) => ({ ...s3, isLoading: true, error: null }));
1038
+ update((s4) => ({ ...s4, isLoading: true, error: null }));
1039
1039
  try {
1040
1040
  const rows3 = await readOptions();
1041
- update((s3) => {
1042
- const entities = { ...s3.entities };
1041
+ update((s4) => {
1042
+ const entities = { ...s4.entities };
1043
1043
  for (const row of rows3) {
1044
1044
  const existing = entities[row.id];
1045
1045
  if (existing?._fetchType === "full") continue;
1046
1046
  entities[row.id] = { ...emptyRecord(), ...existing, ...row, _fetchType: "options" };
1047
1047
  }
1048
1048
  return withIdLists({
1049
- ...s3,
1049
+ ...s4,
1050
1050
  entities,
1051
1051
  isLoading: false,
1052
1052
  error: null,
1053
- fetchScope: s3.fetchScope !== "all" && s3.fetchScope !== "deprecated" ? "active" : s3.fetchScope,
1053
+ fetchScope: s4.fetchScope !== "all" && s4.fetchScope !== "deprecated" ? "active" : s4.fetchScope,
1054
1054
  lastFetched: Date.now()
1055
1055
  });
1056
1056
  });
1057
1057
  } catch (err) {
1058
1058
  const message = messageOf(err);
1059
1059
  errorSink({ code: "model-records-options-load-failed", message });
1060
- update((s3) => ({ ...s3, isLoading: false, error: message }));
1060
+ update((s4) => ({ ...s4, isLoading: false, error: message }));
1061
1061
  }
1062
1062
  });
1063
1063
  },
@@ -1067,23 +1067,23 @@ function createModelRecords(config) {
1067
1067
  return inFlight.get(`model:${modelId}`) ?? Promise.resolve();
1068
1068
  }
1069
1069
  return once(`model:${modelId}`, async () => {
1070
- update((s3) => {
1071
- const detailErrorById = { ...s3.detailErrorById };
1070
+ update((s4) => {
1071
+ const detailErrorById = { ...s4.detailErrorById };
1072
1072
  delete detailErrorById[modelId];
1073
- return { ...s3, detailStatusById: { ...s3.detailStatusById, [modelId]: "loading" }, detailErrorById };
1073
+ return { ...s4, detailStatusById: { ...s4.detailStatusById, [modelId]: "loading" }, detailErrorById };
1074
1074
  });
1075
1075
  try {
1076
1076
  const record = await readFullModel(modelId);
1077
- update((s3) => {
1078
- const existing = s3.entities[record.id];
1079
- const detailErrorById = { ...s3.detailErrorById };
1077
+ update((s4) => {
1078
+ const existing = s4.entities[record.id];
1079
+ const detailErrorById = { ...s4.detailErrorById };
1080
1080
  delete detailErrorById[modelId];
1081
1081
  return withIdLists({
1082
- ...s3,
1083
- detailStatusById: { ...s3.detailStatusById, [modelId]: "succeeded" },
1082
+ ...s4,
1083
+ detailStatusById: { ...s4.detailStatusById, [modelId]: "succeeded" },
1084
1084
  detailErrorById,
1085
1085
  entities: {
1086
- ...s3.entities,
1086
+ ...s4.entities,
1087
1087
  [record.id]: { ...emptyRecord(), ...existing, ...record, maker: record.maker ?? existing?.maker ?? null, _fetchType: "full" }
1088
1088
  }
1089
1089
  });
@@ -1091,21 +1091,21 @@ function createModelRecords(config) {
1091
1091
  } catch (err) {
1092
1092
  const message = `Model configuration ${modelId}: ${messageOf(err)}`;
1093
1093
  errorSink({ code: "model-records-model-load-failed", message, context: { modelId } });
1094
- update((s3) => ({
1095
- ...s3,
1096
- detailStatusById: { ...s3.detailStatusById, [modelId]: "failed" },
1097
- detailErrorById: { ...s3.detailErrorById, [modelId]: message }
1094
+ update((s4) => ({
1095
+ ...s4,
1096
+ detailStatusById: { ...s4.detailStatusById, [modelId]: "failed" },
1097
+ detailErrorById: { ...s4.detailErrorById, [modelId]: message }
1098
1098
  }));
1099
1099
  }
1100
1100
  });
1101
1101
  },
1102
1102
  retryModel(modelId) {
1103
- update((s3) => {
1104
- const detailStatusById = { ...s3.detailStatusById };
1105
- const detailErrorById = { ...s3.detailErrorById };
1103
+ update((s4) => {
1104
+ const detailStatusById = { ...s4.detailStatusById };
1105
+ const detailErrorById = { ...s4.detailErrorById };
1106
1106
  delete detailStatusById[modelId];
1107
1107
  delete detailErrorById[modelId];
1108
- return { ...s3, detailStatusById, detailErrorById };
1108
+ return { ...s4, detailStatusById, detailErrorById };
1109
1109
  });
1110
1110
  },
1111
1111
  loadClassConfig(modelId, offeringId) {
@@ -1113,37 +1113,37 @@ function createModelRecords(config) {
1113
1113
  const status = state2.classConfigStatusByOffering[key];
1114
1114
  if (status === "loading" || status === "succeeded") return inFlight.get(`class:${key}`) ?? Promise.resolve();
1115
1115
  return once(`class:${key}`, async () => {
1116
- update((s3) => ({ ...s3, classConfigStatusByOffering: { ...s3.classConfigStatusByOffering, [key]: "loading" } }));
1116
+ update((s4) => ({ ...s4, classConfigStatusByOffering: { ...s4.classConfigStatusByOffering, [key]: "loading" } }));
1117
1117
  try {
1118
1118
  const { data, error } = await ai().rpc("resolve_model_config", { p_model_id: modelId, p_offering_id: offeringId });
1119
1119
  if (error) throw error;
1120
1120
  const cfg = data && typeof data === "object" && !Array.isArray(data) ? data : null;
1121
- update((s3) => ({
1122
- ...s3,
1123
- classConfigStatusByOffering: { ...s3.classConfigStatusByOffering, [key]: "succeeded" },
1121
+ update((s4) => ({
1122
+ ...s4,
1123
+ classConfigStatusByOffering: { ...s4.classConfigStatusByOffering, [key]: "succeeded" },
1124
1124
  classConfigByOffering: {
1125
- ...s3.classConfigByOffering,
1125
+ ...s4.classConfigByOffering,
1126
1126
  [key]: { modelId, controls: cfg?.controls ?? null, constraints: cfg?.constraints ?? null }
1127
1127
  }
1128
1128
  }));
1129
1129
  } catch (err) {
1130
1130
  errorSink({ code: "model-records-class-config-failed", message: `Model class configuration ${modelId} / ${offeringId}: ${messageOf(err)}` });
1131
- update((s3) => ({ ...s3, classConfigStatusByOffering: { ...s3.classConfigStatusByOffering, [key]: "failed" } }));
1131
+ update((s4) => ({ ...s4, classConfigStatusByOffering: { ...s4.classConfigStatusByOffering, [key]: "failed" } }));
1132
1132
  }
1133
1133
  });
1134
1134
  },
1135
1135
  loadClasses() {
1136
1136
  if (state2.modelClassStatus === "loading" || state2.modelClassStatus === "succeeded") return inFlight.get("classes") ?? Promise.resolve();
1137
1137
  return once("classes", async () => {
1138
- update((s3) => ({ ...s3, modelClassStatus: "loading" }));
1138
+ update((s4) => ({ ...s4, modelClassStatus: "loading" }));
1139
1139
  try {
1140
1140
  const { data, error } = await ai().from("model_offering").select("model_id, offering_id, served_via, served_via_endpoint_id, priority");
1141
1141
  if (error) throw error;
1142
1142
  const index = buildModelClassIndex(data ?? []);
1143
- update((s3) => ({ ...s3, modelClassStatus: "succeeded", modelClassIndex: index }));
1143
+ update((s4) => ({ ...s4, modelClassStatus: "succeeded", modelClassIndex: index }));
1144
1144
  } catch (err) {
1145
1145
  errorSink({ code: "model-records-classes-failed", message: `Model classes: ${messageOf(err)}` });
1146
- update((s3) => ({ ...s3, modelClassStatus: "failed" }));
1146
+ update((s4) => ({ ...s4, modelClassStatus: "failed" }));
1147
1147
  }
1148
1148
  });
1149
1149
  },
@@ -1158,11 +1158,11 @@ function createModelRecords(config) {
1158
1158
  if (error) throw error;
1159
1159
  const slice = buildModelClassIndex(data ?? []);
1160
1160
  classesReadFor.add(modelId);
1161
- update((s3) => {
1162
- if (s3.modelClassStatus === "succeeded") return s3;
1163
- const base = s3.modelClassIndex ?? { classesByModelId: {}, classOfOffering: {} };
1161
+ update((s4) => {
1162
+ if (s4.modelClassStatus === "succeeded") return s4;
1163
+ const base = s4.modelClassIndex ?? { classesByModelId: {}, classOfOffering: {} };
1164
1164
  return {
1165
- ...s3,
1165
+ ...s4,
1166
1166
  modelClassIndex: {
1167
1167
  classesByModelId: { ...base.classesByModelId, ...slice.classesByModelId },
1168
1168
  classOfOffering: { ...base.classOfOffering, ...slice.classOfOffering }
@@ -1180,20 +1180,20 @@ function createModelRecords(config) {
1180
1180
  return inFlight.get(`identity:${modelId}`) ?? Promise.resolve();
1181
1181
  }
1182
1182
  return once(`identity:${modelId}`, async () => {
1183
- update((s3) => ({ ...s3, identityStatusById: { ...s3.identityStatusById, [modelId]: "loading" } }));
1183
+ update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, [modelId]: "loading" } }));
1184
1184
  try {
1185
1185
  const { data, error } = await ai().from("model_definition").select("id, name, common_name, is_deprecated").eq(catalogKeyColumn(modelId), modelId).maybeSingle();
1186
1186
  if (error) throw error;
1187
1187
  if (!data) throw new Error(`AI model ${modelId} is not available (ai.model_definition has no such row)`);
1188
1188
  const row = data;
1189
- update((s3) => ({
1190
- ...s3,
1191
- identityById: { ...s3.identityById, [row.id]: row, [modelId]: row },
1192
- identityStatusById: { ...s3.identityStatusById, [row.id]: "succeeded", [modelId]: "succeeded" }
1189
+ update((s4) => ({
1190
+ ...s4,
1191
+ identityById: { ...s4.identityById, [row.id]: row, [modelId]: row },
1192
+ identityStatusById: { ...s4.identityStatusById, [row.id]: "succeeded", [modelId]: "succeeded" }
1193
1193
  }));
1194
1194
  } catch (err) {
1195
1195
  errorSink({ code: "model-records-identity-failed", message: messageOf(err), context: { modelId } });
1196
- update((s3) => ({ ...s3, identityStatusById: { ...s3.identityStatusById, [modelId]: "failed" } }));
1196
+ update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, [modelId]: "failed" } }));
1197
1197
  }
1198
1198
  });
1199
1199
  },
@@ -1212,26 +1212,26 @@ function createModelRecords(config) {
1212
1212
  }
1213
1213
  if (wanted.length > 0) {
1214
1214
  const marked = Object.fromEntries(wanted.map((id) => [id, "loading"]));
1215
- update((s3) => ({ ...s3, identityStatusById: { ...s3.identityStatusById, ...marked } }));
1215
+ update((s4) => ({ ...s4, identityStatusById: { ...s4.identityStatusById, ...marked } }));
1216
1216
  const run = (async () => {
1217
1217
  try {
1218
1218
  const { data, error } = await ai().from("model_definition").select("id, name, common_name, is_deprecated").in("id", wanted);
1219
1219
  if (error) throw error;
1220
1220
  const rows3 = Array.isArray(data) ? data : [];
1221
1221
  const got = new Set(rows3.map((r) => r.id));
1222
- update((s3) => ({
1223
- ...s3,
1224
- identityById: { ...s3.identityById, ...Object.fromEntries(rows3.map((r) => [r.id, r])) },
1222
+ update((s4) => ({
1223
+ ...s4,
1224
+ identityById: { ...s4.identityById, ...Object.fromEntries(rows3.map((r) => [r.id, r])) },
1225
1225
  identityStatusById: {
1226
- ...s3.identityStatusById,
1226
+ ...s4.identityStatusById,
1227
1227
  ...Object.fromEntries(wanted.map((id) => [id, got.has(id) ? "succeeded" : "failed"]))
1228
1228
  }
1229
1229
  }));
1230
1230
  } catch (err) {
1231
1231
  errorSink({ code: "model-records-identity-failed", message: messageOf(err), context: { modelIds: wanted } });
1232
- update((s3) => ({
1233
- ...s3,
1234
- identityStatusById: { ...s3.identityStatusById, ...Object.fromEntries(wanted.map((id) => [id, "failed"])) }
1232
+ update((s4) => ({
1233
+ ...s4,
1234
+ identityStatusById: { ...s4.identityStatusById, ...Object.fromEntries(wanted.map((id) => [id, "failed"])) }
1235
1235
  }));
1236
1236
  }
1237
1237
  })();
@@ -1249,11 +1249,11 @@ function createModelRecords(config) {
1249
1249
  if (!classesReadFor.has(modelId) && state2.modelClassStatus !== "succeeded") {
1250
1250
  const slice = buildModelClassIndex(offerings.filter((o) => o.model_id === modelId));
1251
1251
  classesReadFor.add(modelId);
1252
- update((s3) => {
1253
- if (s3.modelClassStatus === "succeeded") return s3;
1254
- const base = s3.modelClassIndex ?? { classesByModelId: {}, classOfOffering: {} };
1252
+ update((s4) => {
1253
+ if (s4.modelClassStatus === "succeeded") return s4;
1254
+ const base = s4.modelClassIndex ?? { classesByModelId: {}, classOfOffering: {} };
1255
1255
  return {
1256
- ...s3,
1256
+ ...s4,
1257
1257
  modelClassIndex: {
1258
1258
  classesByModelId: { ...base.classesByModelId, ...slice.classesByModelId },
1259
1259
  classOfOffering: { ...base.classOfOffering, ...slice.classOfOffering }
@@ -1272,16 +1272,16 @@ function createModelRecords(config) {
1272
1272
  controls: row.controls ?? null,
1273
1273
  capabilities: requireCanonicalCapabilities(row.capabilities, { modelId: row.id, modelName: row.name })
1274
1274
  });
1275
- update((s3) => {
1276
- const existing = s3.entities[record.id];
1277
- const detailErrorById = { ...s3.detailErrorById };
1275
+ update((s4) => {
1276
+ const existing = s4.entities[record.id];
1277
+ const detailErrorById = { ...s4.detailErrorById };
1278
1278
  delete detailErrorById[modelId];
1279
1279
  return withIdLists({
1280
- ...s3,
1281
- detailStatusById: { ...s3.detailStatusById, [modelId]: "succeeded" },
1280
+ ...s4,
1281
+ detailStatusById: { ...s4.detailStatusById, [modelId]: "succeeded" },
1282
1282
  detailErrorById,
1283
1283
  entities: {
1284
- ...s3.entities,
1284
+ ...s4.entities,
1285
1285
  [record.id]: { ...emptyRecord(), ...existing, ...record, maker: record.maker ?? existing?.maker ?? null, _fetchType: "full" }
1286
1286
  }
1287
1287
  });
@@ -1291,14 +1291,14 @@ function createModelRecords(config) {
1291
1291
  }
1292
1292
  },
1293
1293
  hydrate({ models, fetchType, fetchScope, lastFetched }) {
1294
- update((s3) => {
1295
- const entities = { ...s3.entities };
1294
+ update((s4) => {
1295
+ const entities = { ...s4.entities };
1296
1296
  for (const raw of models) {
1297
1297
  const existing = entities[raw.id];
1298
1298
  if (existing?._fetchType === "full" && fetchType === "options") continue;
1299
1299
  entities[raw.id] = { ...emptyRecord(), ...normalizeModel({ ...raw, controls: raw.controls ?? null }), _fetchType: fetchType };
1300
1300
  }
1301
- return withIdLists({ ...s3, entities, fetchScope, lastFetched, isLoading: false, error: null });
1301
+ return withIdLists({ ...s4, entities, fetchScope, lastFetched, isLoading: false, error: null });
1302
1302
  });
1303
1303
  }
1304
1304
  };
@@ -1318,28 +1318,28 @@ function memo(fn) {
1318
1318
  var UNSET = /* @__PURE__ */ Symbol("unset");
1319
1319
  var activeKey = null;
1320
1320
  var activeOut = [];
1321
- function selectActiveModels(s3) {
1322
- if (!activeKey || activeKey[0] !== s3.entities || activeKey[1] !== s3.activeIds) {
1323
- activeKey = [s3.entities, s3.activeIds];
1324
- activeOut = s3.activeIds.map((id) => s3.entities[id]).filter((m) => !!m);
1321
+ function selectActiveModels(s4) {
1322
+ if (!activeKey || activeKey[0] !== s4.entities || activeKey[1] !== s4.activeIds) {
1323
+ activeKey = [s4.entities, s4.activeIds];
1324
+ activeOut = s4.activeIds.map((id) => s4.entities[id]).filter((m) => !!m);
1325
1325
  }
1326
1326
  return activeOut;
1327
1327
  }
1328
1328
  var deprecatedKey = null;
1329
1329
  var deprecatedOut = [];
1330
- function selectDeprecatedModels(s3) {
1331
- if (!deprecatedKey || deprecatedKey[0] !== s3.entities || deprecatedKey[1] !== s3.deprecatedIds) {
1332
- deprecatedKey = [s3.entities, s3.deprecatedIds];
1333
- deprecatedOut = s3.deprecatedIds.map((id) => s3.entities[id]).filter((m) => !!m);
1330
+ function selectDeprecatedModels(s4) {
1331
+ if (!deprecatedKey || deprecatedKey[0] !== s4.entities || deprecatedKey[1] !== s4.deprecatedIds) {
1332
+ deprecatedKey = [s4.entities, s4.deprecatedIds];
1333
+ deprecatedOut = s4.deprecatedIds.map((id) => s4.entities[id]).filter((m) => !!m);
1334
1334
  }
1335
1335
  return deprecatedOut;
1336
1336
  }
1337
1337
  var allOf = memo((entities) => Object.values(entities));
1338
- var selectAllModels = (s3) => allOf(s3.entities);
1338
+ var selectAllModels = (s4) => allOf(s4.entities);
1339
1339
  var optionsOf = memo(
1340
1340
  (models) => models.map((m) => ({ value: m.id, label: modelDisplayLabel(m) || m.id, maker: m.maker, costRating: m.cost_rating, speedRating: m.speed_rating }))
1341
1341
  );
1342
- var selectModelOptions = (s3) => optionsOf(selectActiveModels(s3));
1342
+ var selectModelOptions = (s4) => optionsOf(selectActiveModels(s4));
1343
1343
  var allOptionsOf = memo(
1344
1344
  (entities) => Object.values(entities).map((m) => ({
1345
1345
  value: m.id,
@@ -1350,30 +1350,30 @@ var allOptionsOf = memo(
1350
1350
  successorId: m.successor_id ?? null
1351
1351
  }))
1352
1352
  );
1353
- var selectAllModelOptions = (s3) => allOptionsOf(s3.entities);
1354
- var selectModelById = (s3, modelId) => modelId ? s3.entities[modelId] : void 0;
1355
- function selectModelLabelById(s3, modelId) {
1353
+ var selectAllModelOptions = (s4) => allOptionsOf(s4.entities);
1354
+ var selectModelById = (s4, modelId) => modelId ? s4.entities[modelId] : void 0;
1355
+ function selectModelLabelById(s4, modelId) {
1356
1356
  if (!modelId) return void 0;
1357
- const m = s3.entities[modelId];
1358
- const identity = s3.identityById[modelId];
1357
+ const m = s4.entities[modelId];
1358
+ const identity = s4.identityById[modelId];
1359
1359
  return modelDisplayLabel(m) || modelDisplayLabel(identity);
1360
1360
  }
1361
- var selectModelNameById = (s3, modelId) => modelId ? s3.entities[modelId]?.name : void 0;
1362
- var selectModelClassName = (s3, modelId, offeringId) => modelClassName(s3.modelClassIndex, modelId, offeringId);
1363
- function selectModelLabelWithClass(s3, modelId, offeringId) {
1364
- const label = selectModelLabelById(s3, modelId);
1365
- return label ? withModelClass(label, selectModelClassName(s3, modelId, offeringId)) : void 0;
1366
- }
1367
- var selectModelControls = (s3, modelId) => s3.entities[modelId]?.controls;
1368
- var selectModelFetchType = (s3, modelId) => s3.entities[modelId]?._fetchType;
1369
- var selectModelFullyLoaded = (s3, modelId) => !!modelId && s3.entities[modelId]?._fetchType === "full";
1370
- var selectModelDetailError = (s3, modelId) => modelId && s3.detailErrorById[modelId] || null;
1371
- var selectModelClassConfig = (s3, modelId, offeringId) => modelId && offeringId ? s3.classConfigByOffering[classConfigKey(modelId, offeringId)] : void 0;
1372
- var selectActiveModelsReady = (s3) => (s3.fetchScope === "active" || s3.fetchScope === "all") && s3.activeIds.length > 0;
1373
- var selectDeprecatedModelsReady = (s3) => (s3.fetchScope === "deprecated" || s3.fetchScope === "all") && s3.deprecatedIds.length > 0;
1374
- var selectAllModelsReady = (s3) => s3.fetchScope === "all";
1375
- var selectModelIdentityById = (s3, modelId) => s3.identityById[modelId];
1376
- var selectModelIdentityLookupStatus = (s3, modelId) => s3.identityStatusById[modelId] ?? "idle";
1361
+ var selectModelNameById = (s4, modelId) => modelId ? s4.entities[modelId]?.name : void 0;
1362
+ var selectModelClassName = (s4, modelId, offeringId) => modelClassName(s4.modelClassIndex, modelId, offeringId);
1363
+ function selectModelLabelWithClass(s4, modelId, offeringId) {
1364
+ const label = selectModelLabelById(s4, modelId);
1365
+ return label ? withModelClass(label, selectModelClassName(s4, modelId, offeringId)) : void 0;
1366
+ }
1367
+ var selectModelControls = (s4, modelId) => s4.entities[modelId]?.controls;
1368
+ var selectModelFetchType = (s4, modelId) => s4.entities[modelId]?._fetchType;
1369
+ var selectModelFullyLoaded = (s4, modelId) => !!modelId && s4.entities[modelId]?._fetchType === "full";
1370
+ var selectModelDetailError = (s4, modelId) => modelId && s4.detailErrorById[modelId] || null;
1371
+ var selectModelClassConfig = (s4, modelId, offeringId) => modelId && offeringId ? s4.classConfigByOffering[classConfigKey(modelId, offeringId)] : void 0;
1372
+ var selectActiveModelsReady = (s4) => (s4.fetchScope === "active" || s4.fetchScope === "all") && s4.activeIds.length > 0;
1373
+ var selectDeprecatedModelsReady = (s4) => (s4.fetchScope === "deprecated" || s4.fetchScope === "all") && s4.deprecatedIds.length > 0;
1374
+ var selectAllModelsReady = (s4) => s4.fetchScope === "all";
1375
+ var selectModelIdentityById = (s4, modelId) => s4.identityById[modelId];
1376
+ var selectModelIdentityLookupStatus = (s4, modelId) => s4.identityStatusById[modelId] ?? "idle";
1377
1377
  async function readModelIdentities(client, modelIds) {
1378
1378
  const ids = [...new Set(modelIds.filter(Boolean))];
1379
1379
  if (ids.length === 0) return [];
@@ -1673,9 +1673,9 @@ function createModelCatalog(config) {
1673
1673
  };
1674
1674
  },
1675
1675
  load(variant) {
1676
- const s3 = state2[variant];
1677
- if (s3.status === "idle" || s3.status === "error") return run(variant);
1678
- if (s3.status === "ready" && s3.loadedAt !== null && Date.now() - s3.loadedAt > staleAfterMs) return run(variant);
1676
+ const s4 = state2[variant];
1677
+ if (s4.status === "idle" || s4.status === "error") return run(variant);
1678
+ if (s4.status === "ready" && s4.loadedAt !== null && Date.now() - s4.loadedAt > staleAfterMs) return run(variant);
1679
1679
  return inFlight[variant] ?? Promise.resolve();
1680
1680
  },
1681
1681
  reload: (variant) => run(variant)
@@ -1863,9 +1863,9 @@ function placement(v, runtimes, device) {
1863
1863
  }
1864
1864
  return "not_compatible";
1865
1865
  }
1866
- function formatMemoryBytes(n2) {
1867
- const g = n2 / 2 ** 30;
1868
- if (g < 1) return `${Math.max(1, Math.round(n2 / 2 ** 20))} MB`;
1866
+ function formatMemoryBytes(n3) {
1867
+ const g = n3 / 2 ** 30;
1868
+ if (g < 1) return `${Math.max(1, Math.round(n3 / 2 ** 20))} MB`;
1869
1869
  return `${g < 10 ? Math.round(g * 10) / 10 : Math.round(g)} GB`;
1870
1870
  }
1871
1871
  function describe(c, decided, runnerUp, device) {
@@ -2192,41 +2192,41 @@ var Cursor = class {
2192
2192
  this.read = read;
2193
2193
  this.size = size;
2194
2194
  }
2195
- async need(n2) {
2196
- if (this.pos + n2 > this.size) throw new GgufError("the file ends inside its header");
2197
- if (this.pos + n2 > MAX_HEADER) throw new GgufError("its header is larger than any real model's");
2198
- if (this.pos >= this.base && this.pos + n2 <= this.base + this.buf.length) return;
2199
- const len = Math.min(Math.max(n2, CHUNK), this.size - this.pos);
2195
+ async need(n3) {
2196
+ if (this.pos + n3 > this.size) throw new GgufError("the file ends inside its header");
2197
+ if (this.pos + n3 > MAX_HEADER) throw new GgufError("its header is larger than any real model's");
2198
+ if (this.pos >= this.base && this.pos + n3 <= this.base + this.buf.length) return;
2199
+ const len = Math.min(Math.max(n3, CHUNK), this.size - this.pos);
2200
2200
  this.buf = await this.read(this.pos, len);
2201
2201
  this.base = this.pos;
2202
- if (this.buf.length < n2) throw new GgufError("the file ends inside its header");
2202
+ if (this.buf.length < n3) throw new GgufError("the file ends inside its header");
2203
2203
  }
2204
- async view(n2) {
2205
- await this.need(n2);
2206
- const v = new DataView(this.buf.buffer, this.buf.byteOffset + (this.pos - this.base), n2);
2207
- this.pos += n2;
2204
+ async view(n3) {
2205
+ await this.need(n3);
2206
+ const v = new DataView(this.buf.buffer, this.buf.byteOffset + (this.pos - this.base), n3);
2207
+ this.pos += n3;
2208
2208
  return v;
2209
2209
  }
2210
2210
  async u32() {
2211
2211
  return (await this.view(4)).getUint32(0, true);
2212
2212
  }
2213
2213
  async u64() {
2214
- const n2 = (await this.view(8)).getBigUint64(0, true);
2215
- if (n2 > BigInt(Number.MAX_SAFE_INTEGER)) throw new GgufError("a size in its header is out of range");
2216
- return Number(n2);
2214
+ const n3 = (await this.view(8)).getBigUint64(0, true);
2215
+ if (n3 > BigInt(Number.MAX_SAFE_INTEGER)) throw new GgufError("a size in its header is out of range");
2216
+ return Number(n3);
2217
2217
  }
2218
2218
  async str() {
2219
2219
  const len = await this.u64();
2220
2220
  if (len > MAX_STRING) throw new GgufError("a text value in its header is out of range");
2221
2221
  await this.need(len);
2222
2222
  const at = this.pos - this.base;
2223
- const s3 = new TextDecoder().decode(this.buf.subarray(at, at + len));
2223
+ const s4 = new TextDecoder().decode(this.buf.subarray(at, at + len));
2224
2224
  this.pos += len;
2225
- return s3;
2225
+ return s4;
2226
2226
  }
2227
- async skip(n2) {
2228
- if (this.pos + n2 > this.size) throw new GgufError("the file ends inside its header");
2229
- this.pos += n2;
2227
+ async skip(n3) {
2228
+ if (this.pos + n3 > this.size) throw new GgufError("the file ends inside its header");
2229
+ this.pos += n3;
2230
2230
  }
2231
2231
  async scalar(type) {
2232
2232
  switch (type) {
@@ -2278,11 +2278,11 @@ async function readGgufHeader(read, size) {
2278
2278
  const type = await c.u32();
2279
2279
  if (type === 9) {
2280
2280
  const elem = await c.u32();
2281
- const n2 = await c.u64();
2282
- if (n2 > 64 * MAX_COUNT) throw new GgufError("an array in its header is out of range");
2283
- arrays[key] = n2;
2284
- if (elem === 8) for (let j = 0; j < n2; j++) await c.str();
2285
- else if (SCALAR_BYTES[elem] !== void 0) await c.skip(n2 * SCALAR_BYTES[elem]);
2281
+ const n3 = await c.u64();
2282
+ if (n3 > 64 * MAX_COUNT) throw new GgufError("an array in its header is out of range");
2283
+ arrays[key] = n3;
2284
+ if (elem === 8) for (let j = 0; j < n3; j++) await c.str();
2285
+ else if (SCALAR_BYTES[elem] !== void 0) await c.skip(n3 * SCALAR_BYTES[elem]);
2286
2286
  else throw new GgufError(`its header has an array of unknown type ${elem}`);
2287
2287
  } else kv[key] = await c.scalar(type);
2288
2288
  }
@@ -2312,20 +2312,20 @@ async function readGgufHeader(read, size) {
2312
2312
  return { version, kv, array_lengths: arrays, tensors, data_offset: dataOffset, data_end: end };
2313
2313
  }
2314
2314
  function ggufFacts(h) {
2315
- const s3 = (k) => typeof h.kv[k] === "string" ? h.kv[k] : null;
2316
- const n2 = (k) => typeof h.kv[k] === "number" ? h.kv[k] : null;
2317
- const arch = s3("general.architecture");
2318
- const ft = n2("general.file_type");
2315
+ const s4 = (k) => typeof h.kv[k] === "string" ? h.kv[k] : null;
2316
+ const n3 = (k) => typeof h.kv[k] === "number" ? h.kv[k] : null;
2317
+ const arch = s4("general.architecture");
2318
+ const ft = n3("general.file_type");
2319
2319
  const params = h.tensors.reduce((t, x) => t + x.dims.reduce((a, b) => a * b, 1), 0);
2320
2320
  return {
2321
2321
  architecture: arch,
2322
- name: s3("general.name"),
2322
+ name: s4("general.name"),
2323
2323
  quant: ft !== null ? FILE_TYPE_QUANT[ft] ?? `FTYPE_${ft}` : null,
2324
- context_length: arch ? n2(`${arch}.context_length`) : null,
2324
+ context_length: arch ? n3(`${arch}.context_length`) : null,
2325
2325
  parameter_count: params > 0 ? params : null,
2326
- split_count: n2("split.count") ?? 1,
2327
- split_index: n2("split.no") ?? 0,
2328
- type: s3("general.type"),
2326
+ split_count: n3("split.count") ?? 1,
2327
+ split_index: n3("split.no") ?? 0,
2328
+ type: s4("general.type"),
2329
2329
  has_tokenizer: typeof h.kv["tokenizer.ggml.model"] === "string"
2330
2330
  };
2331
2331
  }
@@ -2366,16 +2366,16 @@ var isHelperGguf = (name) => HELPER_RE.test(name);
2366
2366
  var isProjectorName = (name) => name.toLowerCase().includes("mmproj");
2367
2367
  var quantFromName = (name) => QUANT_RE.exec(name.split("/").pop() ?? name)?.[1]?.toUpperCase() ?? null;
2368
2368
  function splitSetProblems(names) {
2369
- const shards = names.map(shardName).filter((s3) => s3 !== null);
2369
+ const shards = names.map(shardName).filter((s4) => s4 !== null);
2370
2370
  if (!shards.length) return [];
2371
- const counts = [...new Set(shards.map((s3) => s3.count))];
2371
+ const counts = [...new Set(shards.map((s4) => s4.count))];
2372
2372
  if (counts.length !== 1) return [`its parts disagree on how many parts there are (${counts.sort((a, b) => a - b).join(", ")})`];
2373
- const n2 = counts[0];
2374
- const have = shards.map((s3) => s3.index);
2375
- const missing = Array.from({ length: n2 }, (_, i) => i + 1).filter((i) => !have.includes(i));
2373
+ const n3 = counts[0];
2374
+ const have = shards.map((s4) => s4.index);
2375
+ const missing = Array.from({ length: n3 }, (_, i) => i + 1).filter((i) => !have.includes(i));
2376
2376
  const dupes = [...new Set(have.filter((i, k) => have.indexOf(i) !== k))].sort((a, b) => a - b);
2377
2377
  const out = [];
2378
- if (missing.length) out.push(`part${missing.length === 1 ? "" : "s"} ${missing.map((i) => `${i} of ${n2}`).join(", ")} ${missing.length === 1 ? "is" : "are"} missing`);
2378
+ if (missing.length) out.push(`part${missing.length === 1 ? "" : "s"} ${missing.map((i) => `${i} of ${n3}`).join(", ")} ${missing.length === 1 ? "is" : "are"} missing`);
2379
2379
  if (dupes.length) out.push(`part${dupes.length === 1 ? "" : "s"} ${dupes.join(", ")} appear${dupes.length === 1 ? "s" : ""} twice`);
2380
2380
  return out;
2381
2381
  }
@@ -2412,8 +2412,8 @@ async function validateModelSet(primary, siblings, env) {
2412
2412
  let parts = [primary];
2413
2413
  if (shard) {
2414
2414
  if (shard.index !== 1) return { ok: false, reason: `This is part ${shard.index} of ${shard.count} of a split model; add its first part, ${shardFileName(shard.stem, 1, shard.count)}.` };
2415
- const same = [primary, ...siblings.filter((s3) => s3 !== primary && shardName(s3.name)?.stem === shard.stem)];
2416
- const problems = splitSetProblems(same.map((s3) => s3.name));
2415
+ const same = [primary, ...siblings.filter((s4) => s4 !== primary && shardName(s4.name)?.stem === shard.stem)];
2416
+ const problems = splitSetProblems(same.map((s4) => s4.name));
2417
2417
  if (problems.length) return { ok: false, reason: `It is a model in ${shard.count} parts and ${problems.join("; ")} (every part has to be in the same folder).` };
2418
2418
  parts = same.sort((a, b) => (shardName(a.name)?.index ?? 0) - (shardName(b.name)?.index ?? 0));
2419
2419
  }
@@ -2446,7 +2446,7 @@ async function validateModelSet(primary, siblings, env) {
2446
2446
  if (end !== null && p.size < end) return { ok: false, reason: `${parts.length > 1 ? `Part ${i + 1} of ${parts.length} is incomplete` : "The file is incomplete"}: it is ${formatMemoryBytes(p.size)} and its header says ${formatMemoryBytes(end)} (a download that stopped early?).` };
2447
2447
  }
2448
2448
  const quant = f.quant ?? quantFromName(primary.name) ?? "unknown";
2449
- const projectorFiles = siblings.filter((s3) => s3 !== primary && extOf(s3.name) === ".gguf" && isProjectorName(s3.name));
2449
+ const projectorFiles = siblings.filter((s4) => s4 !== primary && extOf(s4.name) === ".gguf" && isProjectorName(s4.name));
2450
2450
  const pickedName = pickProjector(quant === "unknown" ? null : quant, projectorFiles.map((p) => p.name));
2451
2451
  let projector = null;
2452
2452
  const picked = projectorFiles.find((p) => p.name === pickedName);
@@ -2568,6 +2568,77 @@ function curatedList(models, { offeredOnly }) {
2568
2568
 
2569
2569
  // models/catalog-reader.ts
2570
2570
  import { sha256Hex } from "@ai-matrx/kit/sha256";
2571
+
2572
+ // models/device-models.ts
2573
+ var DEVICE_SERVED_VIA = "device";
2574
+ var DEVICE_FILE_USABLE_STATES = Object.freeze(["present"]);
2575
+ var DEVICE_OWN_FILE_USABLE_STATES = Object.freeze(["present", "unmatched"]);
2576
+ var s2 = (v) => typeof v === "string" ? v : "";
2577
+ var sn2 = (v) => typeof v === "string" ? v : null;
2578
+ var n = (v) => typeof v === "number" && Number.isFinite(v) ? v : typeof v === "string" && v.trim() !== "" && Number.isFinite(Number(v)) ? Number(v) : null;
2579
+ function toDeviceModelFile(r) {
2580
+ return {
2581
+ id: s2(r.id),
2582
+ app_instance_id: s2(r.app_instance_id),
2583
+ root_id: s2(r.root_id),
2584
+ rel_path: sn2(r.rel_path),
2585
+ size_bytes: n(r.size_bytes),
2586
+ sha256: sn2(r.sha256),
2587
+ artifact_id: sn2(r.artifact_id),
2588
+ state: s2(r.state),
2589
+ organization_id: sn2(r.organization_id)
2590
+ };
2591
+ }
2592
+ async function readDeviceModelFiles(client, opts = {}) {
2593
+ const db = client;
2594
+ let q = db.schema("ai").from("device_model_file").select("*").is("deleted_at", null).in("state", opts.states ?? DEVICE_FILE_USABLE_STATES);
2595
+ if (opts.appInstanceIds) q = q.in("app_instance_id", opts.appInstanceIds);
2596
+ const { data, error } = await q;
2597
+ if (error) throw new Error(`ai.device_model_file: ${error.message}`);
2598
+ return (data ?? []).filter((r) => r.deleted_at == null).map(toDeviceModelFile);
2599
+ }
2600
+ function requiredArtifactIds(v) {
2601
+ const core = v.artifacts.filter((a) => a.required_for.includes("core"));
2602
+ return (core.length ? core : v.artifacts).map((a) => a.id);
2603
+ }
2604
+ function availableOnDevices(variants, files) {
2605
+ const usable = files.filter((f) => f.artifact_id && DEVICE_FILE_USABLE_STATES.includes(f.state));
2606
+ const out = /* @__PURE__ */ new Map();
2607
+ for (const v of variants) {
2608
+ const order = new Map(v.artifacts.map((a, i) => [a.id, i]));
2609
+ const required = requiredArtifactIds(v);
2610
+ const byDevice = /* @__PURE__ */ new Map();
2611
+ for (const f of usable)
2612
+ if (order.has(f.artifact_id)) {
2613
+ const list2 = byDevice.get(f.app_instance_id) ?? [];
2614
+ list2.push(f);
2615
+ byDevice.set(f.app_instance_id, list2);
2616
+ }
2617
+ const list = [];
2618
+ for (const device of [...byDevice.keys()].sort()) {
2619
+ const held = byDevice.get(device);
2620
+ const have = new Set(held.map((f) => f.artifact_id));
2621
+ if (!required.length || !required.every((id) => have.has(id))) continue;
2622
+ list.push({
2623
+ app_instance_id: device,
2624
+ variant_id: v.id,
2625
+ device_model_file_ids: held.slice().sort((a, b) => order.get(a.artifact_id) - order.get(b.artifact_id) || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)).map((f) => f.id)
2626
+ });
2627
+ }
2628
+ out.set(v.id, list);
2629
+ }
2630
+ return out;
2631
+ }
2632
+ var DeviceModelUnavailableError = class extends Error {
2633
+ code;
2634
+ constructor(code, message) {
2635
+ super(message);
2636
+ this.name = "DeviceModelUnavailableError";
2637
+ this.code = code;
2638
+ }
2639
+ };
2640
+
2641
+ // models/catalog-reader.ts
2571
2642
  var MODEL_CATALOG_KINDS = /* @__PURE__ */ new Set([
2572
2643
  "llm_model",
2573
2644
  "whisper_model",
@@ -2605,14 +2676,17 @@ function catalogFingerprint(catalog) {
2605
2676
  const { catalog_fingerprint: _omit, ...rest } = catalog;
2606
2677
  return sha256Hex(canonicalJson(rest));
2607
2678
  }
2608
- var s2 = (v) => typeof v === "string" ? v : "";
2609
- var sn2 = (v) => typeof v === "string" ? v : null;
2610
- var n = (v) => typeof v === "number" && Number.isFinite(v) ? v : typeof v === "string" && v.trim() !== "" && Number.isFinite(Number(v)) ? Number(v) : null;
2679
+ var s3 = (v) => typeof v === "string" ? v : "";
2680
+ var sn3 = (v) => typeof v === "string" ? v : null;
2681
+ var n2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : typeof v === "string" && v.trim() !== "" && Number.isFinite(Number(v)) ? Number(v) : null;
2611
2682
  var live2 = (r) => r.deleted_at == null;
2612
2683
  var byCodepoint2 = (a, b) => a < b ? -1 : a > b ? 1 : 0;
2613
- var byId = (rows3) => new Map(rows3.map((r) => [s2(r.id), r]));
2684
+ var byId = (rows3) => new Map(rows3.map((r) => [s3(r.id), r]));
2614
2685
  var strings = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
2615
2686
  async function readModelCatalog(client, opts = {}) {
2687
+ return readCatalogWith(client, opts, null);
2688
+ }
2689
+ async function readCatalogWith(client, opts, preread) {
2616
2690
  const db = client;
2617
2691
  const offeredOnly = opts.offeredOnly ?? true;
2618
2692
  const ai = db.schema("ai");
@@ -2630,20 +2704,29 @@ async function readModelCatalog(client, opts = {}) {
2630
2704
  ]);
2631
2705
  const curated = curatedList(shapeCuratedModels(raw), { offeredOnly });
2632
2706
  const listed = new Set(curated.map((m) => m.id));
2633
- const variants = raw.variants.filter((v) => listed.has(s2(v.model_id)) && (offeredOnly ? s2(v.review_state) === "offered" : isListedVariant(v)));
2707
+ const variants = raw.variants.filter((v) => listed.has(s3(v.model_id)) && (offeredOnly ? s3(v.review_state) === "offered" : isListedVariant(v)));
2634
2708
  const entryIds = [...new Set(curated.flatMap((m) => m.variants.flatMap((v) => v.catalog_entry_ids)))].sort(byCodepoint2);
2635
2709
  const warnings = [];
2636
- const [artifacts, entries] = await Promise.all([
2637
- variants.length ? all("model_artifact", (q) => q.in("variant_id", variants.map((v) => s2(v.id)))) : Promise.resolve([]),
2710
+ const devices = opts.devices ? opts.devices === true ? {} : opts.devices : null;
2711
+ const [artifacts, entries, deviceFiles] = await Promise.all([
2712
+ variants.length ? all("model_artifact", (q) => q.in("variant_id", variants.map((v) => s3(v.id)))) : Promise.resolve([]),
2638
2713
  entryIds.length ? Promise.resolve(db.schema("public").from("catalog_entries").select("id,kind").in("id", entryIds)).then(({ data, error }) => {
2639
2714
  if (error) {
2640
2715
  warnings.push(`public.catalog_entries: ${error.message} (models placed by what they do)`);
2641
2716
  return [];
2642
2717
  }
2643
2718
  return data ?? [];
2644
- }) : Promise.resolve([])
2719
+ }) : Promise.resolve([]),
2720
+ devices ? preread ? Promise.resolve(preread) : readDeviceModelFiles(client, devices.appInstanceIds ? { appInstanceIds: devices.appInstanceIds } : {}) : Promise.resolve(null)
2645
2721
  ]);
2646
- const kindOfEntry = new Map(entries.map((e) => [s2(e.id), s2(e.kind)]));
2722
+ const placement2 = deviceFiles ? availableOnDevices(
2723
+ variants.map((v) => ({
2724
+ id: s3(v.id),
2725
+ artifacts: artifacts.filter((a) => s3(a.variant_id) === s3(v.id)).map((a) => ({ id: s3(a.id), required_for: strings(a.required_for) }))
2726
+ })),
2727
+ deviceFiles
2728
+ ) : null;
2729
+ const kindOfEntry = new Map(entries.map((e) => [s3(e.id), s3(e.kind)]));
2647
2730
  const stamps = [];
2648
2731
  const stamp = (r) => {
2649
2732
  if (r && typeof r.updated_at === "string") stamps.push(r.updated_at);
@@ -2651,84 +2734,87 @@ async function readModelCatalog(client, opts = {}) {
2651
2734
  const modelById = byId(raw.models);
2652
2735
  const providerById = byId(raw.providers);
2653
2736
  const upstreamById = byId(upstreams);
2654
- const presentationOf = new Map(raw.presentations.map((p) => [s2(p.model_id), p]));
2737
+ const presentationOf = new Map(raw.presentations.map((p) => [s3(p.model_id), p]));
2655
2738
  const models = curated.flatMap((c) => {
2656
2739
  const m = modelById.get(c.id);
2657
2740
  if (!m) return [];
2658
2741
  stamp(m);
2659
2742
  const p = presentationOf.get(c.id);
2660
2743
  stamp(p);
2661
- const maker = providerById.get(s2(m.provider_id));
2744
+ const maker = providerById.get(s3(m.provider_id));
2662
2745
  return [
2663
2746
  {
2664
- id: s2(m.id),
2665
- name: s2(m.name),
2666
- desktop_id: sn2(m.metadata?.desktop_id),
2667
- common_name: sn2(m.common_name),
2668
- maker: sn2(maker?.name),
2669
- maker_logo_file_id: sn2(maker?.logo_file_id),
2670
- description: sn2(m.description),
2747
+ id: s3(m.id),
2748
+ name: s3(m.name),
2749
+ desktop_id: sn3(m.metadata?.desktop_id),
2750
+ common_name: sn3(m.common_name),
2751
+ maker: sn3(maker?.name),
2752
+ maker_logo_file_id: sn3(maker?.logo_file_id),
2753
+ description: sn3(m.description),
2671
2754
  capabilities: m.capabilities ?? {},
2672
2755
  kind: catalogKindOf(c, kindOfEntry),
2673
2756
  offered: c.offered,
2674
2757
  updated_at: c.updated_at,
2675
2758
  presentation: p ? {
2676
- summary: sn2(p.summary),
2677
- symbol: sn2(p.symbol),
2678
- color: sn2(p.color),
2679
- shelf: sn2(p.shelf),
2680
- shelf_title: sn2(p.shelf_title),
2681
- shelf_subtitle: sn2(p.shelf_subtitle),
2682
- display_order: n(p.display_order),
2683
- page: sn2(p.page),
2684
- params_label: sn2(p.params_label),
2759
+ summary: sn3(p.summary),
2760
+ symbol: sn3(p.symbol),
2761
+ color: sn3(p.color),
2762
+ shelf: sn3(p.shelf),
2763
+ shelf_title: sn3(p.shelf_title),
2764
+ shelf_subtitle: sn3(p.shelf_subtitle),
2765
+ display_order: n2(p.display_order),
2766
+ page: sn3(p.page),
2767
+ params_label: sn3(p.params_label),
2685
2768
  tags: strings(p.tags),
2686
2769
  links: Array.isArray(p.links) ? p.links : [],
2687
- thumbnail_file_id: sn2(p.thumbnail_file_id),
2688
- hero_file_id: sn2(p.hero_file_id),
2770
+ thumbnail_file_id: sn3(p.thumbnail_file_id),
2771
+ hero_file_id: sn3(p.hero_file_id),
2689
2772
  gallery_file_ids: strings(p.gallery_file_ids).filter((f) => f.length > 0),
2690
2773
  facts: p.facts && typeof p.facts === "object" && !Array.isArray(p.facts) ? p.facts : {}
2691
2774
  } : null,
2692
- variants: variants.filter((v) => s2(v.model_id) === c.id).sort((a, b) => byCodepoint2(s2(a.key), s2(b.key))).map((v) => {
2775
+ variants: variants.filter((v) => s3(v.model_id) === c.id).sort((a, b) => byCodepoint2(s3(a.key), s3(b.key))).map((v) => {
2693
2776
  stamp(v);
2694
- const up = upstreamById.get(s2(v.upstream_id));
2777
+ const up = upstreamById.get(s3(v.upstream_id));
2695
2778
  stamp(up);
2696
- const arts = artifacts.filter((a) => s2(a.variant_id) === s2(v.id)).sort((a, b) => (n(a.shard_index) ?? 0) - (n(b.shard_index) ?? 0) || byCodepoint2(s2(a.path_in_repo), s2(b.path_in_repo)));
2779
+ const arts = artifacts.filter((a) => s3(a.variant_id) === s3(v.id)).sort((a, b) => (n2(a.shard_index) ?? 0) - (n2(b.shard_index) ?? 0) || byCodepoint2(s3(a.path_in_repo), s3(b.path_in_repo)));
2697
2780
  arts.forEach(stamp);
2698
2781
  return {
2699
- id: s2(v.id),
2700
- key: s2(v.key),
2701
- catalog_key: `${s2(m.name)}/${s2(v.key)}`,
2702
- review_state: s2(v.review_state),
2782
+ id: s3(v.id),
2783
+ key: s3(v.key),
2784
+ catalog_key: `${s3(m.name)}/${s3(v.key)}`,
2785
+ review_state: s3(v.review_state),
2703
2786
  catalog_entry_ids: strings(v.metadata?.catalog_entry_ids),
2704
2787
  is_default: v.is_default === true,
2705
- format: s2(v.format),
2706
- quant: s2(v.quant),
2707
- architecture: s2(v.architecture),
2708
- chat_template_kind: s2(v.chat_template_kind),
2788
+ format: s3(v.format),
2789
+ quant: s3(v.quant),
2790
+ architecture: s3(v.architecture),
2791
+ chat_template_kind: s3(v.chat_template_kind),
2709
2792
  os_arch: strings(v.os_arch),
2710
- selection_rank: n(v.selection_rank) ?? Number.MAX_SAFE_INTEGER,
2793
+ selection_rank: n2(v.selection_rank) ?? Number.MAX_SAFE_INTEGER,
2711
2794
  // internal-bound: a row with no rank sorts last (the column is NOT NULL; this only orders a malformed row)
2712
- license: sn2(v.license_id),
2713
- gated: s2(v.gated),
2714
- total_bytes: n(v.total_bytes),
2715
- memory_bytes_min: n(v.memory_bytes_min),
2716
- memory_bytes_recommended: n(v.memory_bytes_recommended),
2795
+ license: sn3(v.license_id),
2796
+ gated: s3(v.gated),
2797
+ total_bytes: n2(v.total_bytes),
2798
+ memory_bytes_min: n2(v.memory_bytes_min),
2799
+ memory_bytes_recommended: n2(v.memory_bytes_recommended),
2717
2800
  tested_on_current_runtime: v.tested_on_current_runtime === true,
2718
- min_app_version: sn2(v.min_app_version),
2719
- upstream: up ? { kind: s2(up.upstream_kind), repo: sn2(up.repo), revision: sn2(up.revision) } : null,
2801
+ min_app_version: sn3(v.min_app_version),
2802
+ upstream: up ? { kind: s3(up.upstream_kind), repo: sn3(up.repo), revision: sn3(up.revision) } : null,
2720
2803
  artifacts: arts.map((a) => ({
2721
- role: s2(a.role),
2722
- path: s2(a.path_in_repo),
2723
- url: s2(a.url),
2724
- size_bytes: n(a.size_bytes) ?? 0,
2725
- sha256: s2(a.sha256),
2804
+ role: s3(a.role),
2805
+ path: s3(a.path_in_repo),
2806
+ url: s3(a.url),
2807
+ size_bytes: n2(a.size_bytes) ?? 0,
2808
+ sha256: s3(a.sha256),
2726
2809
  required_for: strings(a.required_for).sort(),
2727
- shard_index: n(a.shard_index),
2728
- shard_count: n(a.shard_count)
2810
+ shard_index: n2(a.shard_index),
2811
+ shard_count: n2(a.shard_count)
2729
2812
  }))
2730
2813
  };
2731
- })
2814
+ }),
2815
+ ...placement2 ? {
2816
+ available_on: variants.filter((v) => s3(v.model_id) === c.id).sort((a, b) => byCodepoint2(s3(a.key), s3(b.key))).flatMap((v) => placement2.get(s3(v.id)) ?? [])
2817
+ } : {}
2732
2818
  }
2733
2819
  ];
2734
2820
  });
@@ -2736,36 +2822,36 @@ async function readModelCatalog(client, opts = {}) {
2736
2822
  $schema: "matrx.model-catalog.snapshot/v1",
2737
2823
  catalog_updated_at: "",
2738
2824
  models,
2739
- runtimes: runtimes.sort((a, b) => byCodepoint2(s2(a.key), s2(b.key))).map((rt) => {
2825
+ runtimes: runtimes.sort((a, b) => byCodepoint2(s3(a.key), s3(b.key))).map((rt) => {
2740
2826
  stamp(rt);
2741
2827
  return {
2742
- key: s2(rt.key),
2743
- name: s2(rt.name),
2744
- upstream_repo: sn2(rt.upstream_repo),
2745
- builds: builds.filter((b) => s2(b.runtime_id) === s2(rt.id)).map((b) => {
2828
+ key: s3(rt.key),
2829
+ name: s3(rt.name),
2830
+ upstream_repo: sn3(rt.upstream_repo),
2831
+ builds: builds.filter((b) => s3(b.runtime_id) === s3(rt.id)).map((b) => {
2746
2832
  stamp(b);
2747
2833
  const known = b.metadata?.known_architectures;
2748
2834
  return {
2749
- id: s2(b.id),
2750
- version: s2(b.runtime_version),
2751
- os: s2(b.os),
2752
- arch: s2(b.arch),
2753
- accelerator: s2(b.accelerator),
2754
- build_kind: s2(b.build_kind),
2755
- url: sn2(b.url),
2756
- size_bytes: n(b.size_bytes),
2757
- sha256: sn2(b.sha256),
2835
+ id: s3(b.id),
2836
+ version: s3(b.runtime_version),
2837
+ os: s3(b.os),
2838
+ arch: s3(b.arch),
2839
+ accelerator: s3(b.accelerator),
2840
+ build_kind: s3(b.build_kind),
2841
+ url: sn3(b.url),
2842
+ size_bytes: n2(b.size_bytes),
2843
+ sha256: sn3(b.sha256),
2758
2844
  is_active: b.is_active === true,
2759
- min_app_version: sn2(b.min_app_version),
2845
+ min_app_version: sn3(b.min_app_version),
2760
2846
  // The architectures this pinned llama-server build loads (runtime_build.metadata): the own-model judge reads it.
2761
2847
  known_architectures: Array.isArray(known) ? known.map(String).sort() : null,
2762
- compat: compat.filter((c) => s2(c.runtime_build_id) === s2(b.id)).map((c) => ({
2763
- hardware_class: s2(c.hardware_class),
2764
- format: s2(c.format),
2765
- quant: s2(c.quant),
2766
- architecture: s2(c.architecture),
2767
- chat_template_kind: s2(c.chat_template_kind),
2768
- status: s2(c.status)
2848
+ compat: compat.filter((c) => s3(c.runtime_build_id) === s3(b.id)).map((c) => ({
2849
+ hardware_class: s3(c.hardware_class),
2850
+ format: s3(c.format),
2851
+ quant: s3(c.quant),
2852
+ architecture: s3(c.architecture),
2853
+ chat_template_kind: s3(c.chat_template_kind),
2854
+ status: s3(c.status)
2769
2855
  }))
2770
2856
  };
2771
2857
  })
@@ -2777,6 +2863,37 @@ async function readModelCatalog(client, opts = {}) {
2777
2863
  out.catalog_fingerprint = catalogFingerprint(out);
2778
2864
  return out;
2779
2865
  }
2866
+ async function resolveDeviceModel(client, req) {
2867
+ const files = await readDeviceModelFiles(client, {
2868
+ appInstanceIds: [req.app_instance_id],
2869
+ states: [.../* @__PURE__ */ new Set([...DEVICE_FILE_USABLE_STATES, ...DEVICE_OWN_FILE_USABLE_STATES])]
2870
+ });
2871
+ const own = files.find((f) => f.id === req.local_model_id);
2872
+ if (own) {
2873
+ if (own.artifact_id)
2874
+ throw new DeviceModelUnavailableError("not_own_file", `device model file ${own.id} is catalog artifact ${own.artifact_id}; pass its model id, not the file id`);
2875
+ if (!DEVICE_OWN_FILE_USABLE_STATES.includes(own.state))
2876
+ throw new DeviceModelUnavailableError("file_not_usable", `own model file ${own.id} on computer ${req.app_instance_id} is ${own.state}`);
2877
+ return { own: true, file: own };
2878
+ }
2879
+ const usable = files.filter((f) => DEVICE_FILE_USABLE_STATES.includes(f.state));
2880
+ const catalog = await readCatalogWith(client, { offeredOnly: false, devices: { appInstanceIds: [req.app_instance_id] } }, usable);
2881
+ const model = catalog.models.find((m) => m.id === req.local_model_id);
2882
+ if (!model)
2883
+ throw new DeviceModelUnavailableError("unknown_model", `no catalog model or own model file ${req.local_model_id} on computer ${req.app_instance_id}`);
2884
+ const label = model.common_name || model.name;
2885
+ if (req.variant_id && !model.variants.some((v) => v.id === req.variant_id))
2886
+ throw new DeviceModelUnavailableError("unknown_variant", `${label} has no listed variant ${req.variant_id}`);
2887
+ const held = (model.available_on ?? []).filter((a) => a.app_instance_id === req.app_instance_id && (!req.variant_id || a.variant_id === req.variant_id));
2888
+ const variants = held.map((a) => ({ a, v: model.variants.find((v) => v.id === a.variant_id) })).sort((x, y) => Number(y.v.is_default) - Number(x.v.is_default) || x.v.selection_rank - y.v.selection_rank || byCodepoint2(x.v.key, y.v.key));
2889
+ const pick = variants[0];
2890
+ if (!pick) {
2891
+ const which = req.variant_id ? ` (${model.variants.find((v) => v.id === req.variant_id).key})` : "";
2892
+ throw new DeviceModelUnavailableError("not_on_device", `computer ${req.app_instance_id} does not hold every required file of ${label}${which}`);
2893
+ }
2894
+ const byFileId = new Map(usable.map((f) => [f.id, f]));
2895
+ return { own: false, model, variant: pick.v, files: pick.a.device_model_file_ids.map((id) => byFileId.get(id)) };
2896
+ }
2780
2897
 
2781
2898
  // models/fit.ts
2782
2899
  var featuresOf = (c) => {
@@ -2861,6 +2978,10 @@ export {
2861
2978
  CHAT_DEFAULT_MODEL_KNOB,
2862
2979
  CONTENT_TYPES,
2863
2980
  DEFAULT_CAPABILITIES,
2981
+ DEVICE_FILE_USABLE_STATES,
2982
+ DEVICE_OWN_FILE_USABLE_STATES,
2983
+ DEVICE_SERVED_VIA,
2984
+ DeviceModelUnavailableError,
2864
2985
  EMPTY_MODEL_RECORDS,
2865
2986
  EXCLUDED_REVIEW_STATES,
2866
2987
  FEATURE_BUCKETS,
@@ -2881,6 +3002,7 @@ export {
2881
3002
  TOP_TIER_LOCKED_TITLE,
2882
3003
  acceptsTextInstructions,
2883
3004
  announceModelCatalogChanged,
3005
+ availableOnDevices,
2884
3006
  blendedPrice,
2885
3007
  buildModelClassIndex,
2886
3008
  buildSuccessionIndex,
@@ -2950,6 +3072,7 @@ export {
2950
3072
  quantFromName,
2951
3073
  rankVariants,
2952
3074
  readCuratedRows,
3075
+ readDeviceModelFiles,
2953
3076
  readGgufHeader,
2954
3077
  readModelCatalog,
2955
3078
  readModelIdentities,
@@ -2957,8 +3080,10 @@ export {
2957
3080
  reconcileModelFavoriteIds,
2958
3081
  reportModelDiagnostic,
2959
3082
  requireCanonicalCapabilities,
3083
+ requiredArtifactIds,
2960
3084
  requiredArtifacts,
2961
3085
  resetSuccessionLogForTests,
3086
+ resolveDeviceModel,
2962
3087
  resolveLiveModelId,
2963
3088
  resolveModelMapKnob,
2964
3089
  resolveNewAgentModelId,
@@ -2997,6 +3122,7 @@ export {
2997
3122
  splitSetProblems,
2998
3123
  takesInput,
2999
3124
  toAuditRecord,
3125
+ toDeviceModelFile,
3000
3126
  validateModelSet,
3001
3127
  variantVerdict,
3002
3128
  withModelClass