privateboard 0.1.19 → 0.1.21

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/dist/server.js CHANGED
@@ -968,6 +968,7 @@ var MODELS = {
968
968
  provider: "anthropic",
969
969
  directApiId: "claude-sonnet-4-6",
970
970
  openrouterId: "anthropic/claude-sonnet-4.6",
971
+ baiId: "claude-sonnet-4-6",
971
972
  displayName: "Sonnet 4.6",
972
973
  contextBudget: 2e5,
973
974
  deck: "balanced \xB7 default"
@@ -977,6 +978,7 @@ var MODELS = {
977
978
  provider: "anthropic",
978
979
  directApiId: "claude-opus-4-6",
979
980
  openrouterId: "anthropic/claude-opus-4.6",
981
+ baiId: "claude-opus-4-6",
980
982
  displayName: "Opus 4.6",
981
983
  contextBudget: 1e6,
982
984
  deck: "deep reasoning \xB7 1M ctx"
@@ -986,6 +988,7 @@ var MODELS = {
986
988
  provider: "anthropic",
987
989
  directApiId: "claude-opus-4-7",
988
990
  openrouterId: "anthropic/claude-opus-4.7",
991
+ baiId: "claude-opus-4-7",
989
992
  displayName: "Opus 4.7",
990
993
  contextBudget: 2e5,
991
994
  deck: "deep reasoning"
@@ -995,6 +998,9 @@ var MODELS = {
995
998
  provider: "anthropic",
996
999
  directApiId: "claude-opus-4-6-fast",
997
1000
  openrouterId: "anthropic/claude-opus-4.6-fast",
1001
+ // No baiId · B.AI's catalog doesn't carry Anthropic's "fast"
1002
+ // variants (only the base models). Routing this through B.AI 503s
1003
+ // with "no available channel". Direct Anthropic key or OR carries it.
998
1004
  displayName: "Opus 4.6 Fast",
999
1005
  contextBudget: 2e5,
1000
1006
  deck: "faster 4.6 \xB7 same intelligence"
@@ -1006,6 +1012,7 @@ var MODELS = {
1006
1012
  // the unsuffixed `claude-haiku-4-5` 404s on the direct API.
1007
1013
  directApiId: "claude-haiku-4-5-20251001",
1008
1014
  openrouterId: "anthropic/claude-haiku-4.5",
1015
+ baiId: "claude-haiku-4-5",
1009
1016
  displayName: "Haiku 4.5",
1010
1017
  contextBudget: 2e5,
1011
1018
  deck: "fast \xB7 low-cost"
@@ -1020,6 +1027,7 @@ var MODELS = {
1020
1027
  provider: "openai",
1021
1028
  directApiId: "gpt-5.5",
1022
1029
  openrouterId: "openai/gpt-5.5",
1030
+ baiId: "gpt-5-5",
1023
1031
  displayName: "GPT-5.5",
1024
1032
  contextBudget: 1e6,
1025
1033
  deck: "flagship \xB7 1M ctx"
@@ -1029,6 +1037,7 @@ var MODELS = {
1029
1037
  provider: "openai",
1030
1038
  directApiId: "gpt-5.4",
1031
1039
  openrouterId: "openai/gpt-5.4",
1040
+ baiId: "gpt-5-4",
1032
1041
  displayName: "GPT-5.4",
1033
1042
  contextBudget: 1e6,
1034
1043
  deck: "general \xB7 1M ctx"
@@ -1038,6 +1047,10 @@ var MODELS = {
1038
1047
  provider: "openai",
1039
1048
  directApiId: "gpt-5.4-mini",
1040
1049
  openrouterId: "openai/gpt-5.4-mini",
1050
+ // No baiId · B.AI's catalog reports "No available channel for
1051
+ // model gpt-5-4-mini" on the OneAPI distributor — the mini
1052
+ // variant isn't routed there. The full `gpt-5-4` IS available on
1053
+ // B.AI; pick that or use direct / OR for the mini tier.
1041
1054
  displayName: "GPT-5.4 Mini",
1042
1055
  contextBudget: 4e5,
1043
1056
  deck: "fast \xB7 400k ctx"
@@ -1048,20 +1061,23 @@ var MODELS = {
1048
1061
  provider: "openai",
1049
1062
  directApiId: "gpt-5.5-pro",
1050
1063
  openrouterId: "openai/gpt-5.5-pro",
1064
+ baiId: "gpt-5-5-pro",
1051
1065
  displayName: "GPT-5.5 Pro",
1052
1066
  contextBudget: 1e6,
1053
1067
  deck: "deep reasoning \xB7 1M ctx",
1054
- openrouterOnly: true
1068
+ viaUniversalOnly: true
1055
1069
  },
1056
1070
  "codex-5-4": {
1057
1071
  v: "codex-5-4",
1058
1072
  provider: "openai",
1059
1073
  directApiId: "gpt-5.3-codex",
1060
1074
  openrouterId: "openai/gpt-5.3-codex",
1075
+ // No baiId · B.AI's catalog doesn't list a codex variant; the
1076
+ // preview only ships via OR. Routing it through B.AI 503s.
1061
1077
  displayName: "ChatGPT Codex 5.4",
1062
1078
  contextBudget: 4e5,
1063
1079
  deck: "code \xB7 agents",
1064
- openrouterOnly: true
1080
+ viaUniversalOnly: true
1065
1081
  },
1066
1082
  // ── Google · current frontier (3.1 Pro / 3 Flash / 3.1 Flash Lite) ──
1067
1083
  // Replaced the legacy gemini-2.5-pro / gemini-2.5-flash entries — all
@@ -1073,6 +1089,7 @@ var MODELS = {
1073
1089
  provider: "google",
1074
1090
  directApiId: "gemini-3.1-pro-preview",
1075
1091
  openrouterId: "google/gemini-3.1-pro-preview",
1092
+ baiId: "gemini-3-1-pro",
1076
1093
  displayName: "Gemini 3.1 Pro",
1077
1094
  contextBudget: 1e6,
1078
1095
  deck: "flagship \xB7 1M ctx"
@@ -1082,6 +1099,7 @@ var MODELS = {
1082
1099
  provider: "google",
1083
1100
  directApiId: "gemini-3-flash-preview",
1084
1101
  openrouterId: "google/gemini-3-flash-preview",
1102
+ baiId: "gemini-3-flash",
1085
1103
  displayName: "Gemini 3 Flash",
1086
1104
  contextBudget: 1e6,
1087
1105
  deck: "frontier flash \xB7 1M ctx"
@@ -1091,6 +1109,11 @@ var MODELS = {
1091
1109
  provider: "google",
1092
1110
  directApiId: "gemini-3.1-flash-lite-preview",
1093
1111
  openrouterId: "google/gemini-3.1-flash-lite-preview",
1112
+ // No baiId · B.AI's catalog only has `gemini-3-1-pro` and
1113
+ // `gemini-3-flash` for the Gemini family — no 3.1 Flash Lite
1114
+ // channel. Earlier mapping to `gemini-3-1-flash` 503'd with
1115
+ // "no available channel for model gemini-3-1-flash". Direct
1116
+ // Google key or OR carries this preview model.
1094
1117
  displayName: "Gemini 3.1 Flash Lite",
1095
1118
  contextBudget: 1e6,
1096
1119
  deck: "fast \xB7 1M ctx"
@@ -1105,6 +1128,7 @@ var MODELS = {
1105
1128
  provider: "xai",
1106
1129
  directApiId: "grok-4.3",
1107
1130
  openrouterId: "x-ai/grok-4.3",
1131
+ baiId: "grok-4-3",
1108
1132
  displayName: "Grok 4.3",
1109
1133
  contextBudget: 1e6,
1110
1134
  deck: "flagship \xB7 1M ctx"
@@ -1114,6 +1138,7 @@ var MODELS = {
1114
1138
  provider: "xai",
1115
1139
  directApiId: "grok-4.1-fast",
1116
1140
  openrouterId: "x-ai/grok-4.1-fast",
1141
+ baiId: "grok-4-1-fast",
1117
1142
  displayName: "Grok 4.1 Fast",
1118
1143
  contextBudget: 256e3,
1119
1144
  deck: "fast \xB7 256k ctx"
@@ -1123,10 +1148,11 @@ var MODELS = {
1123
1148
  provider: "xai",
1124
1149
  directApiId: "grok-4.20",
1125
1150
  openrouterId: "x-ai/grok-4.20",
1151
+ baiId: "grok-4-20",
1126
1152
  displayName: "Grok 4.20",
1127
1153
  contextBudget: 2e6,
1128
1154
  deck: "2M ctx \xB7 big context",
1129
- openrouterOnly: true
1155
+ viaUniversalOnly: true
1130
1156
  },
1131
1157
  // ── DeepSeek (OR-only · no @ai-sdk/deepseek shipped) ──
1132
1158
  "deepseek-v4-pro": {
@@ -1134,10 +1160,11 @@ var MODELS = {
1134
1160
  provider: "deepseek",
1135
1161
  directApiId: "deepseek-v4-pro",
1136
1162
  openrouterId: "deepseek/deepseek-v4-pro",
1163
+ baiId: "deepseek-v4-pro",
1137
1164
  displayName: "DeepSeek V4 Pro",
1138
1165
  contextBudget: 128e3,
1139
1166
  deck: "reasoning \xB7 open weights",
1140
- openrouterOnly: true
1167
+ viaUniversalOnly: true
1141
1168
  },
1142
1169
  // OpenRouter catalog id · deepseek/deepseek-v4-flash ("V4 Flash" — lite tier).
1143
1170
  "deepseek-v4-flash": {
@@ -1145,10 +1172,41 @@ var MODELS = {
1145
1172
  provider: "deepseek",
1146
1173
  directApiId: "deepseek-v4-flash",
1147
1174
  openrouterId: "deepseek/deepseek-v4-flash",
1175
+ baiId: "deepseek-v4-flash",
1148
1176
  displayName: "DeepSeek Lite",
1149
1177
  contextBudget: 1e6,
1150
1178
  deck: "V4 Flash \xB7 fast \xB7 1M ctx",
1151
- openrouterOnly: true
1179
+ viaUniversalOnly: true
1180
+ },
1181
+ // ── Zhipu (Z.AI) · GLM family · OR + B.AI only ──
1182
+ // OpenRouter catalog convention: `z-ai/glm-X.Y`. B.AI uses
1183
+ // hyphenated lowercase: `glm-5-1`. No direct @ai-sdk client ·
1184
+ // viaUniversalOnly skips the direct path.
1185
+ "glm-5-1": {
1186
+ v: "glm-5-1",
1187
+ provider: "zhipu",
1188
+ directApiId: "glm-5.1",
1189
+ openrouterId: "z-ai/glm-5.1",
1190
+ baiId: "glm-5-1",
1191
+ displayName: "GLM 5.1",
1192
+ contextBudget: 2e5,
1193
+ deck: "Zhipu flagship \xB7 200k ctx",
1194
+ viaUniversalOnly: true
1195
+ },
1196
+ // ── Moonshot · Kimi family · OR + B.AI only ──
1197
+ // OpenRouter catalog convention: `moonshotai/kimi-…`. B.AI uses
1198
+ // hyphenated lowercase: `kimi-2-6`. No direct @ai-sdk client ·
1199
+ // viaUniversalOnly skips the direct path.
1200
+ "kimi-2-6": {
1201
+ v: "kimi-2-6",
1202
+ provider: "moonshot",
1203
+ directApiId: "kimi-2.6",
1204
+ openrouterId: "moonshotai/kimi-2.6",
1205
+ baiId: "kimi-2-6",
1206
+ displayName: "Kimi 2.6",
1207
+ contextBudget: 256e3,
1208
+ deck: "Moonshot \xB7 long-context",
1209
+ viaUniversalOnly: true
1152
1210
  }
1153
1211
  };
1154
1212
  function getModel(v) {
@@ -1164,6 +1222,7 @@ function isModelV(v) {
1164
1222
  init_db();
1165
1223
  var VALID_CARRIER_PREFS = /* @__PURE__ */ new Set([
1166
1224
  "openrouter",
1225
+ "bai",
1167
1226
  "anthropic",
1168
1227
  "openai",
1169
1228
  "google",
@@ -2764,6 +2823,7 @@ var NoKeyError = class extends Error {
2764
2823
  provider;
2765
2824
  };
2766
2825
  var OPENROUTER_BASE = "https://openrouter.ai/api/v1";
2826
+ var BAI_BASE = "https://api.b.ai/v1";
2767
2827
  var SENSITIVE_HEADER_NAMES = /* @__PURE__ */ new Set([
2768
2828
  "authorization",
2769
2829
  "x-api-key",
@@ -2891,70 +2951,107 @@ function formatStreamError(e) {
2891
2951
  }
2892
2952
  return String(e);
2893
2953
  }
2894
- function resolveModel(modelV, carrier) {
2954
+ function resolveModel(modelV, carrier, excludeCarriers) {
2895
2955
  const meta = getModel(modelV);
2896
- const orKey = getKey("openrouter");
2897
- const directKey = getKey(meta.provider);
2956
+ const skip = (c) => excludeCarriers?.has(c) === true;
2957
+ const orKey = !skip("openrouter") ? getKey("openrouter") : void 0;
2958
+ const baiKey = !skip("bai") ? getKey("bai") : void 0;
2959
+ const directKey = !skip(meta.provider) ? getKey(meta.provider) : void 0;
2898
2960
  if (carrier === "openrouter" && orKey) {
2899
2961
  process.stderr.write(`[adapter] modelV=${modelV} \u2192 openrouter:${meta.openrouterId} (pinned)
2900
2962
  `);
2901
2963
  return openRouterResolved(meta, orKey);
2902
2964
  }
2903
- if (carrier && carrier !== "openrouter" && carrier === meta.provider) {
2904
- const pinnedKey = getKey(carrier);
2965
+ if (carrier === "bai" && baiKey && meta.baiId) {
2966
+ process.stderr.write(`[adapter] modelV=${modelV} \u2192 bai:${meta.baiId} (pinned)
2967
+ `);
2968
+ return baiResolved(meta, baiKey);
2969
+ }
2970
+ if (carrier && carrier !== "openrouter" && carrier !== "bai" && carrier === meta.provider) {
2971
+ const pinnedKey = !skip(carrier) ? getKey(carrier) : void 0;
2905
2972
  if (pinnedKey) {
2906
2973
  process.stderr.write(`[adapter] modelV=${modelV} \u2192 direct:${meta.provider}/${meta.directApiId} (pinned)
2907
2974
  `);
2908
2975
  return directResolved(meta, pinnedKey);
2909
2976
  }
2910
2977
  }
2911
- if (carrier) {
2978
+ if (carrier && !excludeCarriers?.size) {
2912
2979
  process.stderr.write(
2913
2980
  `[adapter] modelV=${modelV} pinned carrier=${carrier} unreachable; falling back to default routing
2914
2981
  `
2915
2982
  );
2916
2983
  }
2917
- if (meta.openrouterOnly && orKey) {
2984
+ if (meta.viaUniversalOnly && orKey) {
2918
2985
  process.stderr.write(`[adapter] modelV=${modelV} \u2192 openrouter:${meta.openrouterId} (preferred)
2919
2986
  `);
2920
2987
  return openRouterResolved(meta, orKey);
2921
2988
  }
2922
- if (directKey && !meta.openrouterOnly) {
2989
+ if (directKey && !meta.viaUniversalOnly) {
2923
2990
  process.stderr.write(`[adapter] modelV=${modelV} \u2192 direct:${meta.provider}/${meta.directApiId}
2924
2991
  `);
2925
2992
  return directResolved(meta, directKey);
2926
2993
  }
2994
+ if (baiKey && meta.baiId) {
2995
+ process.stderr.write(`[adapter] modelV=${modelV} \u2192 bai:${meta.baiId}
2996
+ `);
2997
+ return baiResolved(meta, baiKey);
2998
+ }
2927
2999
  if (orKey) {
2928
3000
  process.stderr.write(`[adapter] modelV=${modelV} \u2192 openrouter:${meta.openrouterId}
2929
3001
  `);
2930
3002
  return openRouterResolved(meta, orKey);
2931
3003
  }
2932
- if (meta.openrouterOnly && directKey) {
2933
- process.stderr.write(`[adapter] modelV=${modelV} \u2192 direct:${meta.provider}/${meta.directApiId} (openrouterOnly fallback \xB7 no OR key)
3004
+ if (meta.viaUniversalOnly && directKey) {
3005
+ process.stderr.write(`[adapter] modelV=${modelV} \u2192 direct:${meta.provider}/${meta.directApiId} (viaUniversalOnly fallback \xB7 no OR / B.AI key)
2934
3006
  `);
2935
3007
  return directResolved(meta, directKey);
2936
3008
  }
2937
3009
  throw new NoKeyError(meta.provider);
2938
3010
  }
3011
+ function isCarrierAccessDenied(message) {
3012
+ if (!message) return false;
3013
+ const m = message.toLowerCase();
3014
+ if (/\b40[23]\b/.test(m) && /(access|deposit|payment|paid|premium|subscri|quota|balance|fund)/.test(m)) return true;
3015
+ if (/access[_\s-]?denied/.test(m)) return true;
3016
+ if (/deposit\s+required/.test(m)) return true;
3017
+ if (/paid[_\s-]?plan[_\s-]?required/.test(m)) return true;
3018
+ if (/premium[_\s-]?model/.test(m)) return true;
3019
+ if (/insufficient[_\s-]?(?:quota|balance|fund)/.test(m)) return true;
3020
+ if (/quota[_\s-]?exceeded/.test(m)) return true;
3021
+ if (/payment[_\s-]?required/.test(m)) return true;
3022
+ if (/billing/.test(m) && /(disabled|inactive|required|invalid|missing)/.test(m)) return true;
3023
+ if (/model[_\s-]?not[_\s-]?found/.test(m)) return true;
3024
+ if (/no\s+available\s+channel/.test(m)) return true;
3025
+ if (/no\s+endpoints?\s+found/.test(m)) return true;
3026
+ if (/invalid\s+model/.test(m)) return true;
3027
+ if (/model.*(unavailable|not\s+(?:supported|available))/.test(m)) return true;
3028
+ if (/prohibited.*(?:terms?\s+of\s+service|provider)/.test(m)) return true;
3029
+ if (/provider.*terms?\s+of\s+service/.test(m)) return true;
3030
+ if (/violates?\s+(?:our|the)?\s*(?:terms|policy|content)/.test(m)) return true;
3031
+ return false;
3032
+ }
2939
3033
  function directResolved(meta, apiKey) {
2940
3034
  switch (meta.provider) {
2941
3035
  case "anthropic":
2942
3036
  return {
2943
- model: createAnthropic({ apiKey, fetch: makeLoggedFetch("anthropic") })(meta.directApiId)
3037
+ model: createAnthropic({ apiKey, fetch: makeLoggedFetch("anthropic") })(meta.directApiId),
3038
+ carrier: "anthropic"
2944
3039
  };
2945
3040
  case "openai":
2946
3041
  return {
2947
3042
  model: createOpenAI({ apiKey, fetch: makeLoggedFetch("openai") }).responses(meta.directApiId),
2948
3043
  providerOptions: {
2949
3044
  openai: { reasoningEffort: "none" }
2950
- }
3045
+ },
3046
+ carrier: "openai"
2951
3047
  };
2952
3048
  case "google":
2953
3049
  return {
2954
3050
  model: createGoogleGenerativeAI({ apiKey, fetch: makeLoggedFetch("google") })(meta.directApiId),
2955
3051
  providerOptions: {
2956
3052
  google: { thinkingConfig: { thinkingBudget: 0 } }
2957
- }
3053
+ },
3054
+ carrier: "google"
2958
3055
  };
2959
3056
  case "xai": {
2960
3057
  return {
@@ -2962,13 +3059,31 @@ function directResolved(meta, apiKey) {
2962
3059
  apiKey,
2963
3060
  baseURL: "https://api.x.ai/v1",
2964
3061
  fetch: makeLoggedFetch("xai")
2965
- }).responses(meta.directApiId)
3062
+ }).responses(meta.directApiId),
3063
+ carrier: "xai"
2966
3064
  };
2967
3065
  }
2968
3066
  default:
2969
3067
  throw new NoKeyError(meta.provider);
2970
3068
  }
2971
3069
  }
3070
+ function baiResolved(meta, apiKey) {
3071
+ const baiId = meta.baiId;
3072
+ if (!baiId) {
3073
+ throw new NoKeyError(meta.provider);
3074
+ }
3075
+ const compat = createOpenAICompatible({
3076
+ name: "bai",
3077
+ apiKey,
3078
+ baseURL: BAI_BASE,
3079
+ fetch: baiLoggedFetch
3080
+ });
3081
+ return {
3082
+ model: compat.chatModel(baiId),
3083
+ carrier: "bai"
3084
+ };
3085
+ }
3086
+ var baiLoggedFetch = makeLoggedFetch("bai");
2972
3087
  function openRouterResolved(meta, apiKey) {
2973
3088
  const compat = createOpenAICompatible({
2974
3089
  name: "openrouter",
@@ -2993,7 +3108,8 @@ function openRouterResolved(meta, apiKey) {
2993
3108
  openrouter: {
2994
3109
  provider: { allow_fallbacks: false }
2995
3110
  }
2996
- }
3111
+ },
3112
+ carrier: "openrouter"
2997
3113
  };
2998
3114
  }
2999
3115
  var RETRY_MAX_ATTEMPTS = 3;
@@ -3044,6 +3160,8 @@ async function* callLLMStream(req) {
3044
3160
  let attempt = 0;
3045
3161
  let lastTransientMessage = "";
3046
3162
  let yieldedText = false;
3163
+ const triedCarriers = /* @__PURE__ */ new Set();
3164
+ triedCarriers.add(resolved.carrier);
3047
3165
  while (attempt < RETRY_MAX_ATTEMPTS) {
3048
3166
  attempt++;
3049
3167
  if (req.signal?.aborted) {
@@ -3085,6 +3203,21 @@ async function* callLLMStream(req) {
3085
3203
  retriableErrorMessage = msg;
3086
3204
  break;
3087
3205
  }
3206
+ if (!yieldedText && isCarrierAccessDenied(msg)) {
3207
+ try {
3208
+ const next = resolveModel(req.modelV, null, triedCarriers);
3209
+ triedCarriers.add(next.carrier);
3210
+ process.stderr.write(
3211
+ `[adapter] modelV=${req.modelV} carrier=${resolved.carrier} rejected (access denied); retrying via ${next.carrier}
3212
+ `
3213
+ );
3214
+ resolved = next;
3215
+ attempt = 0;
3216
+ retriableErrorMessage = msg;
3217
+ break;
3218
+ } catch {
3219
+ }
3220
+ }
3088
3221
  sawError = true;
3089
3222
  yield { type: "error", message: msg };
3090
3223
  }
@@ -3127,6 +3260,20 @@ async function* callLLMStream(req) {
3127
3260
  lastTransientMessage = msg;
3128
3261
  continue;
3129
3262
  }
3263
+ if (!yieldedText && isCarrierAccessDenied(msg)) {
3264
+ try {
3265
+ const next = resolveModel(req.modelV, null, triedCarriers);
3266
+ triedCarriers.add(next.carrier);
3267
+ process.stderr.write(
3268
+ `[adapter] modelV=${req.modelV} carrier=${resolved.carrier} rejected (access denied / threw); retrying via ${next.carrier}
3269
+ `
3270
+ );
3271
+ resolved = next;
3272
+ attempt = 0;
3273
+ continue;
3274
+ } catch {
3275
+ }
3276
+ }
3130
3277
  yield { type: "error", message: msg };
3131
3278
  return;
3132
3279
  }
@@ -3166,25 +3313,31 @@ async function callLLMWithUsage(req) {
3166
3313
  // src/storage/reconcile-models.ts
3167
3314
  var PRIMARY_BY_CARRIER = {
3168
3315
  openrouter: "opus-4-6-fast",
3316
+ bai: "haiku-4-5",
3169
3317
  anthropic: "haiku-4-5",
3170
3318
  openai: "gpt-5-4-mini",
3171
3319
  google: "gemini-3-1-flash",
3172
3320
  xai: "grok-4-1-fast"
3173
3321
  };
3174
- var CARRIER_PRIORITY = ["openrouter", "anthropic", "openai", "google", "xai"];
3322
+ var CARRIER_PRIORITY = ["openrouter", "bai", "anthropic", "openai", "google", "xai"];
3175
3323
  function reachableModelVs() {
3176
3324
  const out = /* @__PURE__ */ new Set();
3177
3325
  const orKey = !!getKey("openrouter");
3326
+ const baiKey = !!getKey("bai");
3178
3327
  for (const [v, meta] of Object.entries(MODELS)) {
3179
3328
  if (orKey) {
3180
3329
  out.add(v);
3181
3330
  continue;
3182
3331
  }
3183
- if (!meta.openrouterOnly && hasDirectKey(meta.provider)) {
3332
+ if (baiKey && meta.baiId) {
3184
3333
  out.add(v);
3185
3334
  continue;
3186
3335
  }
3187
- if (meta.openrouterOnly && hasDirectKey(meta.provider)) {
3336
+ if (!meta.viaUniversalOnly && hasDirectKey(meta.provider)) {
3337
+ out.add(v);
3338
+ continue;
3339
+ }
3340
+ if (meta.viaUniversalOnly && hasDirectKey(meta.provider)) {
3188
3341
  out.add(v);
3189
3342
  }
3190
3343
  }
@@ -3206,14 +3359,16 @@ function activeCarrier() {
3206
3359
  if (prefs.defaultModelV) {
3207
3360
  const meta = MODELS[prefs.defaultModelV];
3208
3361
  if (meta) {
3209
- if (meta.openrouterOnly && getKey("openrouter")) return "openrouter";
3362
+ if (meta.viaUniversalOnly && getKey("openrouter")) return "openrouter";
3210
3363
  if (hasDirectKey(meta.provider)) return meta.provider;
3364
+ if (getKey("bai") && meta.baiId) return "bai";
3211
3365
  if (getKey("openrouter")) return "openrouter";
3212
3366
  }
3213
3367
  }
3214
3368
  for (const c of CARRIER_PRIORITY) {
3215
3369
  if (c === "openrouter" && getKey("openrouter")) return "openrouter";
3216
- if (c !== "openrouter" && hasDirectKey(c)) return c;
3370
+ if (c === "bai" && getKey("bai")) return "bai";
3371
+ if (c !== "openrouter" && c !== "bai" && hasDirectKey(c)) return c;
3217
3372
  }
3218
3373
  return null;
3219
3374
  }
@@ -3224,8 +3379,18 @@ function reconcileAgentModels(opts = {}) {
3224
3379
  const forcePrimary = opts.forcePrimary === true;
3225
3380
  const switched = [];
3226
3381
  const cleared = [];
3382
+ const orReachable = !!getKey("openrouter");
3383
+ const baiReachable = !!getKey("bai");
3384
+ function carrierKeyReachable(c) {
3385
+ if (c === "openrouter") return orReachable;
3386
+ if (c === "bai") return baiReachable;
3387
+ return hasDirectKey(c);
3388
+ }
3227
3389
  for (const agent of listAllAgents()) {
3228
3390
  const v = (agent.modelV || "").trim();
3391
+ if (agent.carrierPref && !carrierKeyReachable(agent.carrierPref)) {
3392
+ updateAgent(agent.id, { carrierPref: null });
3393
+ }
3229
3394
  if (!forcePrimary && v && reachable.has(v)) continue;
3230
3395
  if (primary && carrier) {
3231
3396
  const isChair = agent.roleKind === "moderator";
@@ -3254,26 +3419,29 @@ function reconcileAgentModels(opts = {}) {
3254
3419
  function getProviderKeyState() {
3255
3420
  const directProviders = /* @__PURE__ */ new Set();
3256
3421
  let hasOpenRouter = false;
3422
+ let hasBai = false;
3257
3423
  for (const meta of listKeyMeta()) {
3258
3424
  if (!meta.configured) continue;
3259
3425
  if (meta.provider === "openrouter") hasOpenRouter = true;
3426
+ else if (meta.provider === "bai") hasBai = true;
3260
3427
  else if (meta.provider === "brave" || meta.provider === "tavily" || meta.provider === "minimax" || meta.provider === "elevenlabs") continue;
3261
3428
  else directProviders.add(meta.provider);
3262
3429
  }
3263
- return { hasOpenRouter, directProviders };
3430
+ return { hasOpenRouter, hasBai, directProviders };
3264
3431
  }
3265
3432
  function availabilityFor(meta, keys) {
3266
- const directReachable = !meta.openrouterOnly && keys.directProviders.has(meta.provider);
3433
+ const directReachable = !meta.viaUniversalOnly && keys.directProviders.has(meta.provider);
3267
3434
  const orReachable = keys.hasOpenRouter && !!meta.openrouterId;
3268
- const reachable = directReachable || orReachable;
3435
+ const baiReachable = keys.hasBai && !!meta.baiId;
3436
+ const reachable = directReachable || orReachable || baiReachable;
3269
3437
  return {
3270
3438
  modelV: meta.v,
3271
3439
  displayName: meta.displayName,
3272
3440
  provider: meta.provider,
3273
3441
  deck: meta.deck,
3274
- routes: { direct: directReachable, openrouter: orReachable },
3442
+ routes: { direct: directReachable, openrouter: orReachable, bai: baiReachable },
3275
3443
  reachable,
3276
- preferredRoute: directReachable ? "direct" : orReachable ? "openrouter" : null
3444
+ preferredRoute: directReachable ? "direct" : baiReachable ? "bai" : orReachable ? "openrouter" : null
3277
3445
  };
3278
3446
  }
3279
3447
  function modelAvailability() {
@@ -3285,7 +3453,7 @@ function reachableModels() {
3285
3453
  }
3286
3454
  function hasAnyModelKey() {
3287
3455
  const keys = getProviderKeyState();
3288
- return keys.hasOpenRouter || keys.directProviders.size > 0;
3456
+ return keys.hasOpenRouter || keys.hasBai || keys.directProviders.size > 0;
3289
3457
  }
3290
3458
  var PROVIDER_FLAGSHIP = {
3291
3459
  anthropic: "opus-4-7",
@@ -3293,7 +3461,10 @@ var PROVIDER_FLAGSHIP = {
3293
3461
  google: "gemini-3-flash",
3294
3462
  xai: "grok-4-3",
3295
3463
  deepseek: "deepseek-v4-pro",
3464
+ zhipu: "glm-5-1",
3465
+ moonshot: "kimi-2-6",
3296
3466
  openrouter: "opus-4-7",
3467
+ bai: "opus-4-7",
3297
3468
  brave: null,
3298
3469
  tavily: null,
3299
3470
  minimax: null,
@@ -3305,7 +3476,13 @@ var PROVIDER_FAST = {
3305
3476
  google: "gemini-3-1-flash",
3306
3477
  xai: "grok-4-1-fast",
3307
3478
  deepseek: "deepseek-v4-flash",
3479
+ // GLM / Kimi · no separate fast/flash tier in our registry yet, so
3480
+ // both providers' "fast pick" falls back to the same flagship that
3481
+ // PROVIDER_FLAGSHIP names. Reachability-via-OR/B.AI carries it.
3482
+ zhipu: "glm-5-1",
3483
+ moonshot: "kimi-2-6",
3308
3484
  openrouter: "opus-4-6-fast",
3485
+ bai: "haiku-4-5",
3309
3486
  brave: null,
3310
3487
  tavily: null,
3311
3488
  minimax: null,
@@ -3321,6 +3498,21 @@ var FAST_POOL_BY_CARRIER = {
3321
3498
  "grok-4-1-fast",
3322
3499
  "deepseek-v4-flash"
3323
3500
  ],
3501
+ // B.AI carries the same brand-spanning fast catalog as OpenRouter ·
3502
+ // identical pool gives a B.AI-only user the same visibly-mixed
3503
+ // director cast (different brand badges per seat) that the OpenRouter
3504
+ // path produces. Members are filtered against reachability inside
3505
+ // `pickRandomFastModel`, so models without a baiId fall out naturally
3506
+ // if B.AI ends up not carrying one of them in practice.
3507
+ bai: [
3508
+ "opus-4-6-fast",
3509
+ "haiku-4-5",
3510
+ "gpt-5-4-mini",
3511
+ "gemini-3-flash",
3512
+ "gemini-3-1-flash",
3513
+ "grok-4-1-fast",
3514
+ "deepseek-v4-flash"
3515
+ ],
3324
3516
  anthropic: ["opus-4-6-fast", "haiku-4-5"],
3325
3517
  openai: ["gpt-5-4-mini"],
3326
3518
  google: ["gemini-3-flash", "gemini-3-1-flash"],
@@ -3348,7 +3540,10 @@ var FLAGSHIP_TIER = /* @__PURE__ */ new Set([
3348
3540
  // xAI
3349
3541
  "grok-4-3",
3350
3542
  // DeepSeek
3351
- "deepseek-v4-pro"
3543
+ "deepseek-v4-pro",
3544
+ // Zhipu · Moonshot · single flagship each (both OR + B.AI routed).
3545
+ "glm-5-1",
3546
+ "kimi-2-6"
3352
3547
  ]);
3353
3548
  function effectiveDefaultModel() {
3354
3549
  const prefs = getPrefs();
@@ -3378,6 +3573,12 @@ function defaultModelFor(keys = getProviderKeyState()) {
3378
3573
  const opus = reachable.find((m) => m.modelV === "opus-4-7");
3379
3574
  if (opus) return opus.modelV;
3380
3575
  }
3576
+ if (keys.hasBai) {
3577
+ const fast = reachable.find((m) => m.modelV === "haiku-4-5");
3578
+ if (fast) return fast.modelV;
3579
+ const opus = reachable.find((m) => m.modelV === "opus-4-7");
3580
+ if (opus) return opus.modelV;
3581
+ }
3381
3582
  for (const provider of keys.directProviders) {
3382
3583
  const fast = PROVIDER_FAST[provider];
3383
3584
  if (fast && reachable.find((m) => m.modelV === fast)) return fast;
@@ -3390,6 +3591,8 @@ function defaultModelFor(keys = getProviderKeyState()) {
3390
3591
  }
3391
3592
  var CHEAP_BY_CARRIER = {
3392
3593
  openrouter: "haiku-4-5",
3594
+ bai: "haiku-4-5",
3595
+ // B.AI carries Haiku 4.5 (claude-haiku-4-5)
3393
3596
  anthropic: "sonnet-4-6",
3394
3597
  // only direct-routable Claude
3395
3598
  openai: "gpt-5-4-mini",
@@ -6905,7 +7108,7 @@ function agentsRouter() {
6905
7108
  patch.carrierPref = null;
6906
7109
  } else if (typeof b.carrierPref === "string") {
6907
7110
  const v = b.carrierPref.trim();
6908
- const allowed = /* @__PURE__ */ new Set(["openrouter", "anthropic", "openai", "google", "xai"]);
7111
+ const allowed = /* @__PURE__ */ new Set(["openrouter", "bai", "anthropic", "openai", "google", "xai"]);
6909
7112
  if (!allowed.has(v)) {
6910
7113
  return c.json({ error: `unknown carrier: ${v}` }, 400);
6911
7114
  }
@@ -14437,6 +14640,7 @@ function defaultVoiceForProvider(provider) {
14437
14640
  // src/routes/keys.ts
14438
14641
  var PROVIDERS = /* @__PURE__ */ new Set([
14439
14642
  "openrouter",
14643
+ "bai",
14440
14644
  "anthropic",
14441
14645
  "openai",
14442
14646
  "google",
@@ -14449,6 +14653,7 @@ var PROVIDERS = /* @__PURE__ */ new Set([
14449
14653
  ]);
14450
14654
  var LLM_PROVIDERS = /* @__PURE__ */ new Set([
14451
14655
  "openrouter",
14656
+ "bai",
14452
14657
  "anthropic",
14453
14658
  "openai",
14454
14659
  "google",
@@ -22570,7 +22775,7 @@ function voicesRouter() {
22570
22775
  init_paths();
22571
22776
 
22572
22777
  // src/version.ts
22573
- var VERSION = "0.1.19";
22778
+ var VERSION = "0.1.21";
22574
22779
 
22575
22780
  // src/server.ts
22576
22781
  function createApp() {