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/boot.js CHANGED
@@ -951,6 +951,7 @@ var init_persona_jobs = __esm({
951
951
  init_db();
952
952
  var VALID_CARRIER_PREFS = /* @__PURE__ */ new Set([
953
953
  "openrouter",
954
+ "bai",
954
955
  "anthropic",
955
956
  "openai",
956
957
  "google",
@@ -2264,6 +2265,7 @@ var MODELS = {
2264
2265
  provider: "anthropic",
2265
2266
  directApiId: "claude-sonnet-4-6",
2266
2267
  openrouterId: "anthropic/claude-sonnet-4.6",
2268
+ baiId: "claude-sonnet-4-6",
2267
2269
  displayName: "Sonnet 4.6",
2268
2270
  contextBudget: 2e5,
2269
2271
  deck: "balanced \xB7 default"
@@ -2273,6 +2275,7 @@ var MODELS = {
2273
2275
  provider: "anthropic",
2274
2276
  directApiId: "claude-opus-4-6",
2275
2277
  openrouterId: "anthropic/claude-opus-4.6",
2278
+ baiId: "claude-opus-4-6",
2276
2279
  displayName: "Opus 4.6",
2277
2280
  contextBudget: 1e6,
2278
2281
  deck: "deep reasoning \xB7 1M ctx"
@@ -2282,6 +2285,7 @@ var MODELS = {
2282
2285
  provider: "anthropic",
2283
2286
  directApiId: "claude-opus-4-7",
2284
2287
  openrouterId: "anthropic/claude-opus-4.7",
2288
+ baiId: "claude-opus-4-7",
2285
2289
  displayName: "Opus 4.7",
2286
2290
  contextBudget: 2e5,
2287
2291
  deck: "deep reasoning"
@@ -2291,6 +2295,9 @@ var MODELS = {
2291
2295
  provider: "anthropic",
2292
2296
  directApiId: "claude-opus-4-6-fast",
2293
2297
  openrouterId: "anthropic/claude-opus-4.6-fast",
2298
+ // No baiId · B.AI's catalog doesn't carry Anthropic's "fast"
2299
+ // variants (only the base models). Routing this through B.AI 503s
2300
+ // with "no available channel". Direct Anthropic key or OR carries it.
2294
2301
  displayName: "Opus 4.6 Fast",
2295
2302
  contextBudget: 2e5,
2296
2303
  deck: "faster 4.6 \xB7 same intelligence"
@@ -2302,6 +2309,7 @@ var MODELS = {
2302
2309
  // the unsuffixed `claude-haiku-4-5` 404s on the direct API.
2303
2310
  directApiId: "claude-haiku-4-5-20251001",
2304
2311
  openrouterId: "anthropic/claude-haiku-4.5",
2312
+ baiId: "claude-haiku-4-5",
2305
2313
  displayName: "Haiku 4.5",
2306
2314
  contextBudget: 2e5,
2307
2315
  deck: "fast \xB7 low-cost"
@@ -2316,6 +2324,7 @@ var MODELS = {
2316
2324
  provider: "openai",
2317
2325
  directApiId: "gpt-5.5",
2318
2326
  openrouterId: "openai/gpt-5.5",
2327
+ baiId: "gpt-5-5",
2319
2328
  displayName: "GPT-5.5",
2320
2329
  contextBudget: 1e6,
2321
2330
  deck: "flagship \xB7 1M ctx"
@@ -2325,6 +2334,7 @@ var MODELS = {
2325
2334
  provider: "openai",
2326
2335
  directApiId: "gpt-5.4",
2327
2336
  openrouterId: "openai/gpt-5.4",
2337
+ baiId: "gpt-5-4",
2328
2338
  displayName: "GPT-5.4",
2329
2339
  contextBudget: 1e6,
2330
2340
  deck: "general \xB7 1M ctx"
@@ -2334,6 +2344,10 @@ var MODELS = {
2334
2344
  provider: "openai",
2335
2345
  directApiId: "gpt-5.4-mini",
2336
2346
  openrouterId: "openai/gpt-5.4-mini",
2347
+ // No baiId · B.AI's catalog reports "No available channel for
2348
+ // model gpt-5-4-mini" on the OneAPI distributor — the mini
2349
+ // variant isn't routed there. The full `gpt-5-4` IS available on
2350
+ // B.AI; pick that or use direct / OR for the mini tier.
2337
2351
  displayName: "GPT-5.4 Mini",
2338
2352
  contextBudget: 4e5,
2339
2353
  deck: "fast \xB7 400k ctx"
@@ -2344,20 +2358,23 @@ var MODELS = {
2344
2358
  provider: "openai",
2345
2359
  directApiId: "gpt-5.5-pro",
2346
2360
  openrouterId: "openai/gpt-5.5-pro",
2361
+ baiId: "gpt-5-5-pro",
2347
2362
  displayName: "GPT-5.5 Pro",
2348
2363
  contextBudget: 1e6,
2349
2364
  deck: "deep reasoning \xB7 1M ctx",
2350
- openrouterOnly: true
2365
+ viaUniversalOnly: true
2351
2366
  },
2352
2367
  "codex-5-4": {
2353
2368
  v: "codex-5-4",
2354
2369
  provider: "openai",
2355
2370
  directApiId: "gpt-5.3-codex",
2356
2371
  openrouterId: "openai/gpt-5.3-codex",
2372
+ // No baiId · B.AI's catalog doesn't list a codex variant; the
2373
+ // preview only ships via OR. Routing it through B.AI 503s.
2357
2374
  displayName: "ChatGPT Codex 5.4",
2358
2375
  contextBudget: 4e5,
2359
2376
  deck: "code \xB7 agents",
2360
- openrouterOnly: true
2377
+ viaUniversalOnly: true
2361
2378
  },
2362
2379
  // ── Google · current frontier (3.1 Pro / 3 Flash / 3.1 Flash Lite) ──
2363
2380
  // Replaced the legacy gemini-2.5-pro / gemini-2.5-flash entries — all
@@ -2369,6 +2386,7 @@ var MODELS = {
2369
2386
  provider: "google",
2370
2387
  directApiId: "gemini-3.1-pro-preview",
2371
2388
  openrouterId: "google/gemini-3.1-pro-preview",
2389
+ baiId: "gemini-3-1-pro",
2372
2390
  displayName: "Gemini 3.1 Pro",
2373
2391
  contextBudget: 1e6,
2374
2392
  deck: "flagship \xB7 1M ctx"
@@ -2378,6 +2396,7 @@ var MODELS = {
2378
2396
  provider: "google",
2379
2397
  directApiId: "gemini-3-flash-preview",
2380
2398
  openrouterId: "google/gemini-3-flash-preview",
2399
+ baiId: "gemini-3-flash",
2381
2400
  displayName: "Gemini 3 Flash",
2382
2401
  contextBudget: 1e6,
2383
2402
  deck: "frontier flash \xB7 1M ctx"
@@ -2387,6 +2406,11 @@ var MODELS = {
2387
2406
  provider: "google",
2388
2407
  directApiId: "gemini-3.1-flash-lite-preview",
2389
2408
  openrouterId: "google/gemini-3.1-flash-lite-preview",
2409
+ // No baiId · B.AI's catalog only has `gemini-3-1-pro` and
2410
+ // `gemini-3-flash` for the Gemini family — no 3.1 Flash Lite
2411
+ // channel. Earlier mapping to `gemini-3-1-flash` 503'd with
2412
+ // "no available channel for model gemini-3-1-flash". Direct
2413
+ // Google key or OR carries this preview model.
2390
2414
  displayName: "Gemini 3.1 Flash Lite",
2391
2415
  contextBudget: 1e6,
2392
2416
  deck: "fast \xB7 1M ctx"
@@ -2401,6 +2425,7 @@ var MODELS = {
2401
2425
  provider: "xai",
2402
2426
  directApiId: "grok-4.3",
2403
2427
  openrouterId: "x-ai/grok-4.3",
2428
+ baiId: "grok-4-3",
2404
2429
  displayName: "Grok 4.3",
2405
2430
  contextBudget: 1e6,
2406
2431
  deck: "flagship \xB7 1M ctx"
@@ -2410,6 +2435,7 @@ var MODELS = {
2410
2435
  provider: "xai",
2411
2436
  directApiId: "grok-4.1-fast",
2412
2437
  openrouterId: "x-ai/grok-4.1-fast",
2438
+ baiId: "grok-4-1-fast",
2413
2439
  displayName: "Grok 4.1 Fast",
2414
2440
  contextBudget: 256e3,
2415
2441
  deck: "fast \xB7 256k ctx"
@@ -2419,10 +2445,11 @@ var MODELS = {
2419
2445
  provider: "xai",
2420
2446
  directApiId: "grok-4.20",
2421
2447
  openrouterId: "x-ai/grok-4.20",
2448
+ baiId: "grok-4-20",
2422
2449
  displayName: "Grok 4.20",
2423
2450
  contextBudget: 2e6,
2424
2451
  deck: "2M ctx \xB7 big context",
2425
- openrouterOnly: true
2452
+ viaUniversalOnly: true
2426
2453
  },
2427
2454
  // ── DeepSeek (OR-only · no @ai-sdk/deepseek shipped) ──
2428
2455
  "deepseek-v4-pro": {
@@ -2430,10 +2457,11 @@ var MODELS = {
2430
2457
  provider: "deepseek",
2431
2458
  directApiId: "deepseek-v4-pro",
2432
2459
  openrouterId: "deepseek/deepseek-v4-pro",
2460
+ baiId: "deepseek-v4-pro",
2433
2461
  displayName: "DeepSeek V4 Pro",
2434
2462
  contextBudget: 128e3,
2435
2463
  deck: "reasoning \xB7 open weights",
2436
- openrouterOnly: true
2464
+ viaUniversalOnly: true
2437
2465
  },
2438
2466
  // OpenRouter catalog id · deepseek/deepseek-v4-flash ("V4 Flash" — lite tier).
2439
2467
  "deepseek-v4-flash": {
@@ -2441,10 +2469,41 @@ var MODELS = {
2441
2469
  provider: "deepseek",
2442
2470
  directApiId: "deepseek-v4-flash",
2443
2471
  openrouterId: "deepseek/deepseek-v4-flash",
2472
+ baiId: "deepseek-v4-flash",
2444
2473
  displayName: "DeepSeek Lite",
2445
2474
  contextBudget: 1e6,
2446
2475
  deck: "V4 Flash \xB7 fast \xB7 1M ctx",
2447
- openrouterOnly: true
2476
+ viaUniversalOnly: true
2477
+ },
2478
+ // ── Zhipu (Z.AI) · GLM family · OR + B.AI only ──
2479
+ // OpenRouter catalog convention: `z-ai/glm-X.Y`. B.AI uses
2480
+ // hyphenated lowercase: `glm-5-1`. No direct @ai-sdk client ·
2481
+ // viaUniversalOnly skips the direct path.
2482
+ "glm-5-1": {
2483
+ v: "glm-5-1",
2484
+ provider: "zhipu",
2485
+ directApiId: "glm-5.1",
2486
+ openrouterId: "z-ai/glm-5.1",
2487
+ baiId: "glm-5-1",
2488
+ displayName: "GLM 5.1",
2489
+ contextBudget: 2e5,
2490
+ deck: "Zhipu flagship \xB7 200k ctx",
2491
+ viaUniversalOnly: true
2492
+ },
2493
+ // ── Moonshot · Kimi family · OR + B.AI only ──
2494
+ // OpenRouter catalog convention: `moonshotai/kimi-…`. B.AI uses
2495
+ // hyphenated lowercase: `kimi-2-6`. No direct @ai-sdk client ·
2496
+ // viaUniversalOnly skips the direct path.
2497
+ "kimi-2-6": {
2498
+ v: "kimi-2-6",
2499
+ provider: "moonshot",
2500
+ directApiId: "kimi-2.6",
2501
+ openrouterId: "moonshotai/kimi-2.6",
2502
+ baiId: "kimi-2-6",
2503
+ displayName: "Kimi 2.6",
2504
+ contextBudget: 256e3,
2505
+ deck: "Moonshot \xB7 long-context",
2506
+ viaUniversalOnly: true
2448
2507
  }
2449
2508
  };
2450
2509
  function getModel(v) {
@@ -3482,6 +3541,7 @@ var NoKeyError = class extends Error {
3482
3541
  provider;
3483
3542
  };
3484
3543
  var OPENROUTER_BASE = "https://openrouter.ai/api/v1";
3544
+ var BAI_BASE = "https://api.b.ai/v1";
3485
3545
  var SENSITIVE_HEADER_NAMES = /* @__PURE__ */ new Set([
3486
3546
  "authorization",
3487
3547
  "x-api-key",
@@ -3609,70 +3669,107 @@ function formatStreamError(e) {
3609
3669
  }
3610
3670
  return String(e);
3611
3671
  }
3612
- function resolveModel(modelV, carrier) {
3672
+ function resolveModel(modelV, carrier, excludeCarriers) {
3613
3673
  const meta = getModel(modelV);
3614
- const orKey = getKey("openrouter");
3615
- const directKey = getKey(meta.provider);
3674
+ const skip = (c) => excludeCarriers?.has(c) === true;
3675
+ const orKey = !skip("openrouter") ? getKey("openrouter") : void 0;
3676
+ const baiKey = !skip("bai") ? getKey("bai") : void 0;
3677
+ const directKey = !skip(meta.provider) ? getKey(meta.provider) : void 0;
3616
3678
  if (carrier === "openrouter" && orKey) {
3617
3679
  process.stderr.write(`[adapter] modelV=${modelV} \u2192 openrouter:${meta.openrouterId} (pinned)
3618
3680
  `);
3619
3681
  return openRouterResolved(meta, orKey);
3620
3682
  }
3621
- if (carrier && carrier !== "openrouter" && carrier === meta.provider) {
3622
- const pinnedKey = getKey(carrier);
3683
+ if (carrier === "bai" && baiKey && meta.baiId) {
3684
+ process.stderr.write(`[adapter] modelV=${modelV} \u2192 bai:${meta.baiId} (pinned)
3685
+ `);
3686
+ return baiResolved(meta, baiKey);
3687
+ }
3688
+ if (carrier && carrier !== "openrouter" && carrier !== "bai" && carrier === meta.provider) {
3689
+ const pinnedKey = !skip(carrier) ? getKey(carrier) : void 0;
3623
3690
  if (pinnedKey) {
3624
3691
  process.stderr.write(`[adapter] modelV=${modelV} \u2192 direct:${meta.provider}/${meta.directApiId} (pinned)
3625
3692
  `);
3626
3693
  return directResolved(meta, pinnedKey);
3627
3694
  }
3628
3695
  }
3629
- if (carrier) {
3696
+ if (carrier && !excludeCarriers?.size) {
3630
3697
  process.stderr.write(
3631
3698
  `[adapter] modelV=${modelV} pinned carrier=${carrier} unreachable; falling back to default routing
3632
3699
  `
3633
3700
  );
3634
3701
  }
3635
- if (meta.openrouterOnly && orKey) {
3702
+ if (meta.viaUniversalOnly && orKey) {
3636
3703
  process.stderr.write(`[adapter] modelV=${modelV} \u2192 openrouter:${meta.openrouterId} (preferred)
3637
3704
  `);
3638
3705
  return openRouterResolved(meta, orKey);
3639
3706
  }
3640
- if (directKey && !meta.openrouterOnly) {
3707
+ if (directKey && !meta.viaUniversalOnly) {
3641
3708
  process.stderr.write(`[adapter] modelV=${modelV} \u2192 direct:${meta.provider}/${meta.directApiId}
3642
3709
  `);
3643
3710
  return directResolved(meta, directKey);
3644
3711
  }
3712
+ if (baiKey && meta.baiId) {
3713
+ process.stderr.write(`[adapter] modelV=${modelV} \u2192 bai:${meta.baiId}
3714
+ `);
3715
+ return baiResolved(meta, baiKey);
3716
+ }
3645
3717
  if (orKey) {
3646
3718
  process.stderr.write(`[adapter] modelV=${modelV} \u2192 openrouter:${meta.openrouterId}
3647
3719
  `);
3648
3720
  return openRouterResolved(meta, orKey);
3649
3721
  }
3650
- if (meta.openrouterOnly && directKey) {
3651
- process.stderr.write(`[adapter] modelV=${modelV} \u2192 direct:${meta.provider}/${meta.directApiId} (openrouterOnly fallback \xB7 no OR key)
3722
+ if (meta.viaUniversalOnly && directKey) {
3723
+ process.stderr.write(`[adapter] modelV=${modelV} \u2192 direct:${meta.provider}/${meta.directApiId} (viaUniversalOnly fallback \xB7 no OR / B.AI key)
3652
3724
  `);
3653
3725
  return directResolved(meta, directKey);
3654
3726
  }
3655
3727
  throw new NoKeyError(meta.provider);
3656
3728
  }
3729
+ function isCarrierAccessDenied(message) {
3730
+ if (!message) return false;
3731
+ const m = message.toLowerCase();
3732
+ if (/\b40[23]\b/.test(m) && /(access|deposit|payment|paid|premium|subscri|quota|balance|fund)/.test(m)) return true;
3733
+ if (/access[_\s-]?denied/.test(m)) return true;
3734
+ if (/deposit\s+required/.test(m)) return true;
3735
+ if (/paid[_\s-]?plan[_\s-]?required/.test(m)) return true;
3736
+ if (/premium[_\s-]?model/.test(m)) return true;
3737
+ if (/insufficient[_\s-]?(?:quota|balance|fund)/.test(m)) return true;
3738
+ if (/quota[_\s-]?exceeded/.test(m)) return true;
3739
+ if (/payment[_\s-]?required/.test(m)) return true;
3740
+ if (/billing/.test(m) && /(disabled|inactive|required|invalid|missing)/.test(m)) return true;
3741
+ if (/model[_\s-]?not[_\s-]?found/.test(m)) return true;
3742
+ if (/no\s+available\s+channel/.test(m)) return true;
3743
+ if (/no\s+endpoints?\s+found/.test(m)) return true;
3744
+ if (/invalid\s+model/.test(m)) return true;
3745
+ if (/model.*(unavailable|not\s+(?:supported|available))/.test(m)) return true;
3746
+ if (/prohibited.*(?:terms?\s+of\s+service|provider)/.test(m)) return true;
3747
+ if (/provider.*terms?\s+of\s+service/.test(m)) return true;
3748
+ if (/violates?\s+(?:our|the)?\s*(?:terms|policy|content)/.test(m)) return true;
3749
+ return false;
3750
+ }
3657
3751
  function directResolved(meta, apiKey) {
3658
3752
  switch (meta.provider) {
3659
3753
  case "anthropic":
3660
3754
  return {
3661
- model: createAnthropic({ apiKey, fetch: makeLoggedFetch("anthropic") })(meta.directApiId)
3755
+ model: createAnthropic({ apiKey, fetch: makeLoggedFetch("anthropic") })(meta.directApiId),
3756
+ carrier: "anthropic"
3662
3757
  };
3663
3758
  case "openai":
3664
3759
  return {
3665
3760
  model: createOpenAI({ apiKey, fetch: makeLoggedFetch("openai") }).responses(meta.directApiId),
3666
3761
  providerOptions: {
3667
3762
  openai: { reasoningEffort: "none" }
3668
- }
3763
+ },
3764
+ carrier: "openai"
3669
3765
  };
3670
3766
  case "google":
3671
3767
  return {
3672
3768
  model: createGoogleGenerativeAI({ apiKey, fetch: makeLoggedFetch("google") })(meta.directApiId),
3673
3769
  providerOptions: {
3674
3770
  google: { thinkingConfig: { thinkingBudget: 0 } }
3675
- }
3771
+ },
3772
+ carrier: "google"
3676
3773
  };
3677
3774
  case "xai": {
3678
3775
  return {
@@ -3680,13 +3777,31 @@ function directResolved(meta, apiKey) {
3680
3777
  apiKey,
3681
3778
  baseURL: "https://api.x.ai/v1",
3682
3779
  fetch: makeLoggedFetch("xai")
3683
- }).responses(meta.directApiId)
3780
+ }).responses(meta.directApiId),
3781
+ carrier: "xai"
3684
3782
  };
3685
3783
  }
3686
3784
  default:
3687
3785
  throw new NoKeyError(meta.provider);
3688
3786
  }
3689
3787
  }
3788
+ function baiResolved(meta, apiKey) {
3789
+ const baiId = meta.baiId;
3790
+ if (!baiId) {
3791
+ throw new NoKeyError(meta.provider);
3792
+ }
3793
+ const compat = createOpenAICompatible({
3794
+ name: "bai",
3795
+ apiKey,
3796
+ baseURL: BAI_BASE,
3797
+ fetch: baiLoggedFetch
3798
+ });
3799
+ return {
3800
+ model: compat.chatModel(baiId),
3801
+ carrier: "bai"
3802
+ };
3803
+ }
3804
+ var baiLoggedFetch = makeLoggedFetch("bai");
3690
3805
  function openRouterResolved(meta, apiKey) {
3691
3806
  const compat = createOpenAICompatible({
3692
3807
  name: "openrouter",
@@ -3711,7 +3826,8 @@ function openRouterResolved(meta, apiKey) {
3711
3826
  openrouter: {
3712
3827
  provider: { allow_fallbacks: false }
3713
3828
  }
3714
- }
3829
+ },
3830
+ carrier: "openrouter"
3715
3831
  };
3716
3832
  }
3717
3833
  var RETRY_MAX_ATTEMPTS = 3;
@@ -3762,6 +3878,8 @@ async function* callLLMStream(req) {
3762
3878
  let attempt = 0;
3763
3879
  let lastTransientMessage = "";
3764
3880
  let yieldedText = false;
3881
+ const triedCarriers = /* @__PURE__ */ new Set();
3882
+ triedCarriers.add(resolved.carrier);
3765
3883
  while (attempt < RETRY_MAX_ATTEMPTS) {
3766
3884
  attempt++;
3767
3885
  if (req.signal?.aborted) {
@@ -3803,6 +3921,21 @@ async function* callLLMStream(req) {
3803
3921
  retriableErrorMessage = msg;
3804
3922
  break;
3805
3923
  }
3924
+ if (!yieldedText && isCarrierAccessDenied(msg)) {
3925
+ try {
3926
+ const next = resolveModel(req.modelV, null, triedCarriers);
3927
+ triedCarriers.add(next.carrier);
3928
+ process.stderr.write(
3929
+ `[adapter] modelV=${req.modelV} carrier=${resolved.carrier} rejected (access denied); retrying via ${next.carrier}
3930
+ `
3931
+ );
3932
+ resolved = next;
3933
+ attempt = 0;
3934
+ retriableErrorMessage = msg;
3935
+ break;
3936
+ } catch {
3937
+ }
3938
+ }
3806
3939
  sawError = true;
3807
3940
  yield { type: "error", message: msg };
3808
3941
  }
@@ -3845,6 +3978,20 @@ async function* callLLMStream(req) {
3845
3978
  lastTransientMessage = msg;
3846
3979
  continue;
3847
3980
  }
3981
+ if (!yieldedText && isCarrierAccessDenied(msg)) {
3982
+ try {
3983
+ const next = resolveModel(req.modelV, null, triedCarriers);
3984
+ triedCarriers.add(next.carrier);
3985
+ process.stderr.write(
3986
+ `[adapter] modelV=${req.modelV} carrier=${resolved.carrier} rejected (access denied / threw); retrying via ${next.carrier}
3987
+ `
3988
+ );
3989
+ resolved = next;
3990
+ attempt = 0;
3991
+ continue;
3992
+ } catch {
3993
+ }
3994
+ }
3848
3995
  yield { type: "error", message: msg };
3849
3996
  return;
3850
3997
  }
@@ -3884,25 +4031,31 @@ async function callLLMWithUsage(req) {
3884
4031
  // src/storage/reconcile-models.ts
3885
4032
  var PRIMARY_BY_CARRIER = {
3886
4033
  openrouter: "opus-4-6-fast",
4034
+ bai: "haiku-4-5",
3887
4035
  anthropic: "haiku-4-5",
3888
4036
  openai: "gpt-5-4-mini",
3889
4037
  google: "gemini-3-1-flash",
3890
4038
  xai: "grok-4-1-fast"
3891
4039
  };
3892
- var CARRIER_PRIORITY = ["openrouter", "anthropic", "openai", "google", "xai"];
4040
+ var CARRIER_PRIORITY = ["openrouter", "bai", "anthropic", "openai", "google", "xai"];
3893
4041
  function reachableModelVs() {
3894
4042
  const out = /* @__PURE__ */ new Set();
3895
4043
  const orKey = !!getKey("openrouter");
4044
+ const baiKey = !!getKey("bai");
3896
4045
  for (const [v, meta] of Object.entries(MODELS)) {
3897
4046
  if (orKey) {
3898
4047
  out.add(v);
3899
4048
  continue;
3900
4049
  }
3901
- if (!meta.openrouterOnly && hasDirectKey(meta.provider)) {
4050
+ if (baiKey && meta.baiId) {
3902
4051
  out.add(v);
3903
4052
  continue;
3904
4053
  }
3905
- if (meta.openrouterOnly && hasDirectKey(meta.provider)) {
4054
+ if (!meta.viaUniversalOnly && hasDirectKey(meta.provider)) {
4055
+ out.add(v);
4056
+ continue;
4057
+ }
4058
+ if (meta.viaUniversalOnly && hasDirectKey(meta.provider)) {
3906
4059
  out.add(v);
3907
4060
  }
3908
4061
  }
@@ -3924,14 +4077,16 @@ function activeCarrier() {
3924
4077
  if (prefs.defaultModelV) {
3925
4078
  const meta = MODELS[prefs.defaultModelV];
3926
4079
  if (meta) {
3927
- if (meta.openrouterOnly && getKey("openrouter")) return "openrouter";
4080
+ if (meta.viaUniversalOnly && getKey("openrouter")) return "openrouter";
3928
4081
  if (hasDirectKey(meta.provider)) return meta.provider;
4082
+ if (getKey("bai") && meta.baiId) return "bai";
3929
4083
  if (getKey("openrouter")) return "openrouter";
3930
4084
  }
3931
4085
  }
3932
4086
  for (const c of CARRIER_PRIORITY) {
3933
4087
  if (c === "openrouter" && getKey("openrouter")) return "openrouter";
3934
- if (c !== "openrouter" && hasDirectKey(c)) return c;
4088
+ if (c === "bai" && getKey("bai")) return "bai";
4089
+ if (c !== "openrouter" && c !== "bai" && hasDirectKey(c)) return c;
3935
4090
  }
3936
4091
  return null;
3937
4092
  }
@@ -3942,8 +4097,18 @@ function reconcileAgentModels(opts = {}) {
3942
4097
  const forcePrimary = opts.forcePrimary === true;
3943
4098
  const switched = [];
3944
4099
  const cleared = [];
4100
+ const orReachable = !!getKey("openrouter");
4101
+ const baiReachable = !!getKey("bai");
4102
+ function carrierKeyReachable(c) {
4103
+ if (c === "openrouter") return orReachable;
4104
+ if (c === "bai") return baiReachable;
4105
+ return hasDirectKey(c);
4106
+ }
3945
4107
  for (const agent of listAllAgents()) {
3946
4108
  const v = (agent.modelV || "").trim();
4109
+ if (agent.carrierPref && !carrierKeyReachable(agent.carrierPref)) {
4110
+ updateAgent(agent.id, { carrierPref: null });
4111
+ }
3947
4112
  if (!forcePrimary && v && reachable.has(v)) continue;
3948
4113
  if (primary && carrier) {
3949
4114
  const isChair = agent.roleKind === "moderator";
@@ -3972,26 +4137,29 @@ function reconcileAgentModels(opts = {}) {
3972
4137
  function getProviderKeyState() {
3973
4138
  const directProviders = /* @__PURE__ */ new Set();
3974
4139
  let hasOpenRouter = false;
4140
+ let hasBai = false;
3975
4141
  for (const meta of listKeyMeta()) {
3976
4142
  if (!meta.configured) continue;
3977
4143
  if (meta.provider === "openrouter") hasOpenRouter = true;
4144
+ else if (meta.provider === "bai") hasBai = true;
3978
4145
  else if (meta.provider === "brave" || meta.provider === "tavily" || meta.provider === "minimax" || meta.provider === "elevenlabs") continue;
3979
4146
  else directProviders.add(meta.provider);
3980
4147
  }
3981
- return { hasOpenRouter, directProviders };
4148
+ return { hasOpenRouter, hasBai, directProviders };
3982
4149
  }
3983
4150
  function availabilityFor(meta, keys) {
3984
- const directReachable = !meta.openrouterOnly && keys.directProviders.has(meta.provider);
4151
+ const directReachable = !meta.viaUniversalOnly && keys.directProviders.has(meta.provider);
3985
4152
  const orReachable = keys.hasOpenRouter && !!meta.openrouterId;
3986
- const reachable = directReachable || orReachable;
4153
+ const baiReachable = keys.hasBai && !!meta.baiId;
4154
+ const reachable = directReachable || orReachable || baiReachable;
3987
4155
  return {
3988
4156
  modelV: meta.v,
3989
4157
  displayName: meta.displayName,
3990
4158
  provider: meta.provider,
3991
4159
  deck: meta.deck,
3992
- routes: { direct: directReachable, openrouter: orReachable },
4160
+ routes: { direct: directReachable, openrouter: orReachable, bai: baiReachable },
3993
4161
  reachable,
3994
- preferredRoute: directReachable ? "direct" : orReachable ? "openrouter" : null
4162
+ preferredRoute: directReachable ? "direct" : baiReachable ? "bai" : orReachable ? "openrouter" : null
3995
4163
  };
3996
4164
  }
3997
4165
  function modelAvailability() {
@@ -4003,7 +4171,7 @@ function reachableModels() {
4003
4171
  }
4004
4172
  function hasAnyModelKey() {
4005
4173
  const keys = getProviderKeyState();
4006
- return keys.hasOpenRouter || keys.directProviders.size > 0;
4174
+ return keys.hasOpenRouter || keys.hasBai || keys.directProviders.size > 0;
4007
4175
  }
4008
4176
  var PROVIDER_FLAGSHIP = {
4009
4177
  anthropic: "opus-4-7",
@@ -4011,7 +4179,10 @@ var PROVIDER_FLAGSHIP = {
4011
4179
  google: "gemini-3-flash",
4012
4180
  xai: "grok-4-3",
4013
4181
  deepseek: "deepseek-v4-pro",
4182
+ zhipu: "glm-5-1",
4183
+ moonshot: "kimi-2-6",
4014
4184
  openrouter: "opus-4-7",
4185
+ bai: "opus-4-7",
4015
4186
  brave: null,
4016
4187
  tavily: null,
4017
4188
  minimax: null,
@@ -4023,7 +4194,13 @@ var PROVIDER_FAST = {
4023
4194
  google: "gemini-3-1-flash",
4024
4195
  xai: "grok-4-1-fast",
4025
4196
  deepseek: "deepseek-v4-flash",
4197
+ // GLM / Kimi · no separate fast/flash tier in our registry yet, so
4198
+ // both providers' "fast pick" falls back to the same flagship that
4199
+ // PROVIDER_FLAGSHIP names. Reachability-via-OR/B.AI carries it.
4200
+ zhipu: "glm-5-1",
4201
+ moonshot: "kimi-2-6",
4026
4202
  openrouter: "opus-4-6-fast",
4203
+ bai: "haiku-4-5",
4027
4204
  brave: null,
4028
4205
  tavily: null,
4029
4206
  minimax: null,
@@ -4039,6 +4216,21 @@ var FAST_POOL_BY_CARRIER = {
4039
4216
  "grok-4-1-fast",
4040
4217
  "deepseek-v4-flash"
4041
4218
  ],
4219
+ // B.AI carries the same brand-spanning fast catalog as OpenRouter ·
4220
+ // identical pool gives a B.AI-only user the same visibly-mixed
4221
+ // director cast (different brand badges per seat) that the OpenRouter
4222
+ // path produces. Members are filtered against reachability inside
4223
+ // `pickRandomFastModel`, so models without a baiId fall out naturally
4224
+ // if B.AI ends up not carrying one of them in practice.
4225
+ bai: [
4226
+ "opus-4-6-fast",
4227
+ "haiku-4-5",
4228
+ "gpt-5-4-mini",
4229
+ "gemini-3-flash",
4230
+ "gemini-3-1-flash",
4231
+ "grok-4-1-fast",
4232
+ "deepseek-v4-flash"
4233
+ ],
4042
4234
  anthropic: ["opus-4-6-fast", "haiku-4-5"],
4043
4235
  openai: ["gpt-5-4-mini"],
4044
4236
  google: ["gemini-3-flash", "gemini-3-1-flash"],
@@ -4066,7 +4258,10 @@ var FLAGSHIP_TIER = /* @__PURE__ */ new Set([
4066
4258
  // xAI
4067
4259
  "grok-4-3",
4068
4260
  // DeepSeek
4069
- "deepseek-v4-pro"
4261
+ "deepseek-v4-pro",
4262
+ // Zhipu · Moonshot · single flagship each (both OR + B.AI routed).
4263
+ "glm-5-1",
4264
+ "kimi-2-6"
4070
4265
  ]);
4071
4266
  function effectiveDefaultModel() {
4072
4267
  const prefs = getPrefs();
@@ -4096,6 +4291,12 @@ function defaultModelFor(keys = getProviderKeyState()) {
4096
4291
  const opus = reachable.find((m) => m.modelV === "opus-4-7");
4097
4292
  if (opus) return opus.modelV;
4098
4293
  }
4294
+ if (keys.hasBai) {
4295
+ const fast = reachable.find((m) => m.modelV === "haiku-4-5");
4296
+ if (fast) return fast.modelV;
4297
+ const opus = reachable.find((m) => m.modelV === "opus-4-7");
4298
+ if (opus) return opus.modelV;
4299
+ }
4099
4300
  for (const provider of keys.directProviders) {
4100
4301
  const fast = PROVIDER_FAST[provider];
4101
4302
  if (fast && reachable.find((m) => m.modelV === fast)) return fast;
@@ -4108,6 +4309,8 @@ function defaultModelFor(keys = getProviderKeyState()) {
4108
4309
  }
4109
4310
  var CHEAP_BY_CARRIER = {
4110
4311
  openrouter: "haiku-4-5",
4312
+ bai: "haiku-4-5",
4313
+ // B.AI carries Haiku 4.5 (claude-haiku-4-5)
4111
4314
  anthropic: "sonnet-4-6",
4112
4315
  // only direct-routable Claude
4113
4316
  openai: "gpt-5-4-mini",
@@ -7628,7 +7831,7 @@ function agentsRouter() {
7628
7831
  patch.carrierPref = null;
7629
7832
  } else if (typeof b.carrierPref === "string") {
7630
7833
  const v = b.carrierPref.trim();
7631
- const allowed = /* @__PURE__ */ new Set(["openrouter", "anthropic", "openai", "google", "xai"]);
7834
+ const allowed = /* @__PURE__ */ new Set(["openrouter", "bai", "anthropic", "openai", "google", "xai"]);
7632
7835
  if (!allowed.has(v)) {
7633
7836
  return c.json({ error: `unknown carrier: ${v}` }, 400);
7634
7837
  }
@@ -15196,6 +15399,7 @@ function defaultVoiceForProvider(provider) {
15196
15399
  // src/routes/keys.ts
15197
15400
  var PROVIDERS = /* @__PURE__ */ new Set([
15198
15401
  "openrouter",
15402
+ "bai",
15199
15403
  "anthropic",
15200
15404
  "openai",
15201
15405
  "google",
@@ -15208,6 +15412,7 @@ var PROVIDERS = /* @__PURE__ */ new Set([
15208
15412
  ]);
15209
15413
  var LLM_PROVIDERS = /* @__PURE__ */ new Set([
15210
15414
  "openrouter",
15415
+ "bai",
15211
15416
  "anthropic",
15212
15417
  "openai",
15213
15418
  "google",
@@ -23339,7 +23544,7 @@ function voicesRouter() {
23339
23544
  init_paths();
23340
23545
 
23341
23546
  // src/version.ts
23342
- var VERSION = "0.1.19";
23547
+ var VERSION = "0.1.21";
23343
23548
 
23344
23549
  // src/server.ts
23345
23550
  function createApp() {