@cosmicstack/mercury-agent 1.1.0 → 1.1.2

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/index.js CHANGED
@@ -90,6 +90,20 @@ function getDefaultConfig() {
90
90
  baseUrl: getEnv("OLLAMA_LOCAL_BASE_URL", "http://127.0.0.1:11434/api"),
91
91
  model: getEnv("OLLAMA_LOCAL_MODEL", "gpt-oss:20b"),
92
92
  enabled: getEnvBool("OLLAMA_LOCAL_ENABLED", false)
93
+ },
94
+ mimo: {
95
+ name: "mimo",
96
+ apiKey: getEnv("MIMO_API_KEY", ""),
97
+ baseUrl: getEnv("MIMO_BASE_URL", "https://api.xiaomimimo.com/v1"),
98
+ model: getEnv("MIMO_MODEL", "mimo-v2.5-pro"),
99
+ enabled: getEnvBool("MIMO_ENABLED", true)
100
+ },
101
+ mimoTokenPlan: {
102
+ name: "mimoTokenPlan",
103
+ apiKey: getEnv("MIMO_TOKEN_PLAN_API_KEY", ""),
104
+ baseUrl: getEnv("MIMO_TOKEN_PLAN_BASE_URL", "https://token-plan-cn.xiaomimimo.com/v1"),
105
+ model: getEnv("MIMO_TOKEN_PLAN_MODEL", "mimo-v2.5-pro"),
106
+ enabled: getEnvBool("MIMO_TOKEN_PLAN_ENABLED", false)
93
107
  }
94
108
  },
95
109
  channels: {
@@ -1679,6 +1693,36 @@ var OllamaProvider = class extends BaseProvider {
1679
1693
  }
1680
1694
  };
1681
1695
 
1696
+ // src/providers/mimo.ts
1697
+ import { createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
1698
+ var MiMoProvider = class extends BaseProvider {
1699
+ name;
1700
+ model;
1701
+ modelInstance;
1702
+ constructor(config) {
1703
+ super(config);
1704
+ this.name = config.name;
1705
+ this.model = config.model;
1706
+ const client = createOpenAI2({
1707
+ apiKey: config.apiKey,
1708
+ baseURL: config.baseUrl
1709
+ });
1710
+ this.modelInstance = client.chat(config.model);
1711
+ }
1712
+ async generateText(_prompt, _systemPrompt) {
1713
+ throw new Error("Use getModelInstance() with the AI SDK agent loop");
1714
+ }
1715
+ async *streamText(_prompt, _systemPrompt) {
1716
+ throw new Error("Use getModelInstance() with the AI SDK agent loop");
1717
+ }
1718
+ isAvailable() {
1719
+ return this.config.apiKey.length > 0;
1720
+ }
1721
+ getModelInstance() {
1722
+ return this.modelInstance;
1723
+ }
1724
+ };
1725
+
1682
1726
  // src/providers/registry.ts
1683
1727
  var ProviderRegistry = class {
1684
1728
  providers = /* @__PURE__ */ new Map();
@@ -1692,7 +1736,9 @@ var ProviderRegistry = class {
1692
1736
  config.providers.anthropic,
1693
1737
  config.providers.grok,
1694
1738
  config.providers.ollamaCloud,
1695
- config.providers.ollamaLocal
1739
+ config.providers.ollamaLocal,
1740
+ config.providers.mimo,
1741
+ config.providers.mimoTokenPlan
1696
1742
  ];
1697
1743
  for (const pc of entries) {
1698
1744
  if (!isProviderConfigured(pc)) continue;
@@ -1704,6 +1750,8 @@ var ProviderRegistry = class {
1704
1750
  provider = new DeepSeekProvider(pc);
1705
1751
  } else if (pc.name === "ollamaCloud" || pc.name === "ollamaLocal") {
1706
1752
  provider = new OllamaProvider(pc);
1753
+ } else if (pc.name === "mimo" || pc.name === "mimoTokenPlan") {
1754
+ provider = new MiMoProvider(pc);
1707
1755
  } else {
1708
1756
  provider = new OpenAICompatProvider(pc);
1709
1757
  }
@@ -4581,6 +4629,22 @@ Or use /budget override, /budget reset, /budget set <number> anytime.`,
4581
4629
  this.shutdown();
4582
4630
  return true;
4583
4631
  }
4632
+ if (cmd === "/permissions") {
4633
+ if (channelType === "cli" && channel instanceof CLIChannel) {
4634
+ const mode = await channel.askPermissionMode?.();
4635
+ if (mode === "allow-all") {
4636
+ this.capabilities.permissions.setAutoApproveAll(true);
4637
+ this.capabilities.permissions.addTempScope("/", true, true);
4638
+ await channel.send("Allow All mode active for this session. All scopes, commands, and loops auto-approved. Resets on restart.", channelId);
4639
+ } else {
4640
+ this.capabilities.permissions.setAutoApproveAll(false);
4641
+ await channel.send("Ask Me mode active. Risky actions will prompt for confirmation.", channelId);
4642
+ }
4643
+ return true;
4644
+ }
4645
+ await channel.send("Use /permissions in CLI to switch permission mode. On Telegram, use the /permissions button or command.", channelId);
4646
+ return true;
4647
+ }
4584
4648
  if (cmd === "/status") {
4585
4649
  const config = ctx.config();
4586
4650
  const budget = ctx.tokenBudget();
@@ -4833,9 +4897,11 @@ Or use /budget override, /budget reset, /budget set <number> anytime.`,
4833
4897
  await channel.withMenu(async (select) => {
4834
4898
  while (true) {
4835
4899
  const streamLabel = this.telegramStreaming ? "Disable Telegram Streaming" : "Enable Telegram Streaming";
4900
+ const permLabel = this.capabilities.permissions.isAutoApproveAll() ? "Switch to Ask Me" : "Switch to Allow All";
4836
4901
  const action = await select("Mercury Commands", [
4837
4902
  { value: "status", label: "Status" },
4838
4903
  { value: "memory", label: "Memory" },
4904
+ { value: "permissions", label: permLabel },
4839
4905
  { value: "telegram", label: "Telegram" },
4840
4906
  { value: "tools", label: "Tools" },
4841
4907
  { value: "skills", label: "Skills" },
@@ -4858,6 +4924,10 @@ Or use /budget override, /budget reset, /budget set <number> anytime.`,
4858
4924
  }
4859
4925
  continue;
4860
4926
  }
4927
+ if (action === "permissions") {
4928
+ await this.handleChatCommand("/permissions", "cli", channelId);
4929
+ continue;
4930
+ }
4861
4931
  if (action === "telegram") {
4862
4932
  await this.openCliTelegramMenu(channel, channelId, select);
4863
4933
  continue;
@@ -5365,6 +5435,11 @@ var ChannelRegistry = class {
5365
5435
  import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
5366
5436
  import { join as join7 } from "path";
5367
5437
  var TOKEN_FILE = "token-usage.json";
5438
+ function safeNumber(value) {
5439
+ if (value === null || value === void 0) return 0;
5440
+ const n = Number(value);
5441
+ return isNaN(n) ? 0 : n;
5442
+ }
5368
5443
  var TokenBudget = class {
5369
5444
  constructor(config) {
5370
5445
  this.config = config;
@@ -5380,7 +5455,7 @@ var TokenBudget = class {
5380
5455
  forceNext = false;
5381
5456
  canAfford(estimatedTokens) {
5382
5457
  this.resetIfNewDay();
5383
- return this.dailyUsed + estimatedTokens <= this.dailyBudget;
5458
+ return safeNumber(this.dailyUsed) + estimatedTokens <= safeNumber(this.dailyBudget);
5384
5459
  }
5385
5460
  isOverBudget() {
5386
5461
  this.resetIfNewDay();
@@ -5388,7 +5463,7 @@ var TokenBudget = class {
5388
5463
  this.forceNext = false;
5389
5464
  return false;
5390
5465
  }
5391
- return this.dailyUsed >= this.dailyBudget;
5466
+ return safeNumber(this.dailyUsed) >= safeNumber(this.dailyBudget);
5392
5467
  }
5393
5468
  forceAllowNext() {
5394
5469
  this.forceNext = true;
@@ -5416,18 +5491,24 @@ var TokenBudget = class {
5416
5491
  }
5417
5492
  recordUsage(entry) {
5418
5493
  this.resetIfNewDay();
5419
- const logEntry = { ...entry, timestamp: Date.now() };
5420
- this.dailyUsed += entry.totalTokens;
5494
+ const inputTokens = safeNumber(entry.inputTokens);
5495
+ const outputTokens = safeNumber(entry.outputTokens);
5496
+ const totalTokens = safeNumber(entry.totalTokens) || inputTokens + outputTokens;
5497
+ const safeEntry = { ...entry, inputTokens, outputTokens, totalTokens };
5498
+ const logEntry = { ...safeEntry, timestamp: Date.now() };
5499
+ this.dailyUsed += totalTokens;
5421
5500
  this.requestLog.push(logEntry);
5422
5501
  this.persist();
5423
5502
  }
5424
5503
  getRemaining() {
5425
5504
  this.resetIfNewDay();
5426
- return Math.max(0, this.dailyBudget - this.dailyUsed);
5505
+ return Math.max(0, safeNumber(this.dailyBudget) - safeNumber(this.dailyUsed));
5427
5506
  }
5428
5507
  getUsagePercentage() {
5429
5508
  this.resetIfNewDay();
5430
- return this.dailyBudget > 0 ? this.dailyUsed / this.dailyBudget * 100 : 0;
5509
+ const budget = safeNumber(this.dailyBudget);
5510
+ const used = safeNumber(this.dailyUsed);
5511
+ return budget > 0 ? used / budget * 100 : 0;
5431
5512
  }
5432
5513
  getStatusText() {
5433
5514
  const pct = Math.round(this.getUsagePercentage());
@@ -5466,8 +5547,16 @@ var TokenBudget = class {
5466
5547
  const data = JSON.parse(raw);
5467
5548
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
5468
5549
  if (data.lastResetDate === today) {
5469
- this.dailyUsed = data.dailyUsed ?? 0;
5470
- this.requestLog = data.requestLog ?? [];
5550
+ const restored = safeNumber(data.dailyUsed);
5551
+ if (!isNaN(restored)) {
5552
+ this.dailyUsed = restored;
5553
+ }
5554
+ this.requestLog = (data.requestLog ?? []).map((entry) => ({
5555
+ ...entry,
5556
+ inputTokens: safeNumber(entry.inputTokens),
5557
+ outputTokens: safeNumber(entry.outputTokens),
5558
+ totalTokens: safeNumber(entry.totalTokens)
5559
+ }));
5471
5560
  }
5472
5561
  this.lastResetDate = data.lastResetDate ?? today;
5473
5562
  } catch (err) {
@@ -5592,7 +5681,6 @@ var PermissionManager = class {
5592
5681
  askHandler;
5593
5682
  autoApproveAll = false;
5594
5683
  elevatedCommands = /* @__PURE__ */ new Set();
5595
- pendingApprovals = /* @__PURE__ */ new Set();
5596
5684
  currentChannelType = "cli";
5597
5685
  tempScopes = [];
5598
5686
  constructor() {
@@ -5635,12 +5723,6 @@ var PermissionManager = class {
5635
5723
  isShellElevated() {
5636
5724
  return this.elevatedCommands.has("run_command");
5637
5725
  }
5638
- addPendingApproval(baseCommand) {
5639
- this.pendingApprovals.add(baseCommand);
5640
- }
5641
- clearPendingApprovals() {
5642
- this.pendingApprovals.clear();
5643
- }
5644
5726
  load() {
5645
5727
  if (existsSync7(PERMISSIONS_FILE)) {
5646
5728
  try {
@@ -5699,6 +5781,9 @@ var PermissionManager = class {
5699
5781
  if (mode === "write" && tempScope.write) return { allowed: true };
5700
5782
  return { allowed: false, reason: `Permission denied: ${mode} access to ${path3}` };
5701
5783
  }
5784
+ if (!this.autoApproveAll && this.askHandler && this.currentChannelType !== "internal") {
5785
+ return this.requestScopeExternal(path3, mode);
5786
+ }
5702
5787
  return { allowed: false, reason: `Permission denied for ${mode} access to ${path3}` };
5703
5788
  }
5704
5789
  async checkShellCommand(command) {
@@ -5716,10 +5801,6 @@ var PermissionManager = class {
5716
5801
  }
5717
5802
  const trimmed = command.trim();
5718
5803
  const baseCmd = trimmed.split(/\s+/)[0];
5719
- if (this.pendingApprovals.has(baseCmd)) {
5720
- logger.info({ cmd: trimmed }, "Shell command auto-approved (pending approval)");
5721
- return { allowed: true, needsApproval: false };
5722
- }
5723
5804
  for (const pattern of shell.blocked) {
5724
5805
  if (this.matchPattern(trimmed, pattern)) {
5725
5806
  return { allowed: false, reason: `Blocked command: matches "${pattern}"`, needsApproval: false };
@@ -5742,7 +5823,7 @@ var PermissionManager = class {
5742
5823
  }
5743
5824
  for (const pattern of shell.needsApproval) {
5744
5825
  if (this.matchPattern(trimmed, pattern)) {
5745
- if (this.currentChannelType === "telegram" && this.askHandler) {
5826
+ if (this.askHandler && this.currentChannelType !== "internal") {
5746
5827
  const result = await this.askHandler(`Run command: ${trimmed}`);
5747
5828
  if (result === "yes") {
5748
5829
  return { allowed: true, needsApproval: false };
@@ -5756,7 +5837,7 @@ var PermissionManager = class {
5756
5837
  return { allowed: false, reason: `Command requires approval: matches "${pattern}"`, needsApproval: true };
5757
5838
  }
5758
5839
  }
5759
- if (this.currentChannelType === "telegram" && this.askHandler) {
5840
+ if (this.askHandler && this.currentChannelType !== "internal") {
5760
5841
  const result = await this.askHandler(`Run command: ${trimmed}`);
5761
5842
  if (result === "yes") {
5762
5843
  return { allowed: true, needsApproval: false };
@@ -5862,6 +5943,11 @@ Allow access?`;
5862
5943
  return null;
5863
5944
  }
5864
5945
  mergeDefaults(parsed) {
5946
+ const mergeArray = (existing, defaults) => {
5947
+ if (!existing) return [...defaults];
5948
+ const combined = /* @__PURE__ */ new Set([...defaults, ...existing]);
5949
+ return [...combined];
5950
+ };
5865
5951
  return {
5866
5952
  capabilities: {
5867
5953
  filesystem: {
@@ -5870,9 +5956,9 @@ Allow access?`;
5870
5956
  },
5871
5957
  shell: {
5872
5958
  enabled: parsed.capabilities?.shell?.enabled ?? DEFAULT_MANIFEST.capabilities.shell.enabled,
5873
- blocked: parsed.capabilities?.shell?.blocked ?? DEFAULT_MANIFEST.capabilities.shell.blocked,
5874
- autoApproved: parsed.capabilities?.shell?.autoApproved ?? DEFAULT_MANIFEST.capabilities.shell.autoApproved,
5875
- needsApproval: parsed.capabilities?.shell?.needsApproval ?? DEFAULT_MANIFEST.capabilities.shell.needsApproval,
5959
+ blocked: mergeArray(parsed.capabilities?.shell?.blocked, DEFAULT_MANIFEST.capabilities.shell.blocked),
5960
+ autoApproved: mergeArray(parsed.capabilities?.shell?.autoApproved, DEFAULT_MANIFEST.capabilities.shell.autoApproved),
5961
+ needsApproval: mergeArray(parsed.capabilities?.shell?.needsApproval, DEFAULT_MANIFEST.capabilities.shell.needsApproval),
5876
5962
  cwdOnly: parsed.capabilities?.shell?.cwdOnly ?? DEFAULT_MANIFEST.capabilities.shell.cwdOnly
5877
5963
  },
5878
5964
  git: {
@@ -6222,20 +6308,13 @@ function createRunCommandTool(permissions, getCwd, setCwd) {
6222
6308
  description: `Run a shell command in the current working directory. Use the cd tool to change directories first \u2014 cd commands within this tool only affect chained commands (e.g., "cd /path && ls"), not subsequent calls.
6223
6309
  Blocked commands (sudo, rm -rf /, etc.) are never executed.
6224
6310
  Auto-approved commands (ls, cat, git status, curl, etc.) run without asking.
6225
- Other commands require user approval.`,
6311
+ Other commands prompt the user for approval before execution.`,
6226
6312
  inputSchema: zodSchema10(z10.object({
6227
6313
  command: z10.string().describe("The shell command to execute")
6228
6314
  })),
6229
6315
  execute: async ({ command }) => {
6230
6316
  const check = await permissions.checkShellCommand(command);
6231
6317
  if (!check.allowed) {
6232
- if (check.needsApproval) {
6233
- const baseCmd = command.trim().split(/\s+/)[0];
6234
- permissions.addPendingApproval(baseCmd);
6235
- return `\u26A0 Command requires approval: ${command}
6236
-
6237
- Tell the user what this command does and ask for permission. If they approve, try running it again. If they say "always", use the approve_command tool to permanently approve this command type.`;
6238
- }
6239
6318
  return `Error: ${check.reason}`;
6240
6319
  }
6241
6320
  const cwd = getCwd();
@@ -6711,12 +6790,6 @@ function createGitPushTool(permissions, getCwd) {
6711
6790
  execute: async ({ remote, branch }) => {
6712
6791
  const cmd = `git push ${remote || "origin"} ${branch || ""}`.trim();
6713
6792
  const check = await permissions.checkShellCommand(cmd);
6714
- if (!check.allowed && check.needsApproval) {
6715
- const baseCmd = "git";
6716
- permissions.addPendingApproval(baseCmd);
6717
- return `\u26A0 This command pushes to a remote: ${cmd}
6718
- Ask the user for permission. If they approve, try again. If they say "always", use the approve_command tool.`;
6719
- }
6720
6793
  if (!check.allowed) {
6721
6794
  return `Error: ${check.reason}`;
6722
6795
  }
@@ -8075,6 +8148,14 @@ var OLLAMA_LOCAL_PREFERRED_MODELS = [
8075
8148
  "gpt-oss:20b",
8076
8149
  "gpt-oss:120b"
8077
8150
  ];
8151
+ var MIMO_PREFERRED_MODELS = [
8152
+ "mimo-v2.5-pro",
8153
+ "mimo-v2.5",
8154
+ "mimo-v2-pro",
8155
+ "mimo-v2-omni",
8156
+ "mimo-v2-flash"
8157
+ ];
8158
+ var MIMO_TOKEN_PLAN_PREFERRED_MODELS = MIMO_PREFERRED_MODELS;
8078
8159
  var ProviderModelFetchError = class extends Error {
8079
8160
  constructor(message) {
8080
8161
  super(message);
@@ -8129,7 +8210,9 @@ function chooseRecommendedModel(provider, models, currentModel) {
8129
8210
  anthropic: ANTHROPIC_PREFERRED_MODELS,
8130
8211
  grok: GROK_PREFERRED_MODELS,
8131
8212
  ollamaCloud: OLLAMA_CLOUD_PREFERRED_MODELS,
8132
- ollamaLocal: OLLAMA_LOCAL_PREFERRED_MODELS
8213
+ ollamaLocal: OLLAMA_LOCAL_PREFERRED_MODELS,
8214
+ mimo: MIMO_PREFERRED_MODELS,
8215
+ mimoTokenPlan: MIMO_TOKEN_PLAN_PREFERRED_MODELS
8133
8216
  };
8134
8217
  for (const candidate of preferredByProvider[provider]) {
8135
8218
  if (models.includes(candidate)) {
@@ -8153,7 +8236,9 @@ function buildModelCatalog(provider, models, currentModel) {
8153
8236
  anthropic: ANTHROPIC_PREFERRED_MODELS,
8154
8237
  grok: GROK_PREFERRED_MODELS,
8155
8238
  ollamaCloud: OLLAMA_CLOUD_PREFERRED_MODELS,
8156
- ollamaLocal: OLLAMA_LOCAL_PREFERRED_MODELS
8239
+ ollamaLocal: OLLAMA_LOCAL_PREFERRED_MODELS,
8240
+ mimo: MIMO_PREFERRED_MODELS,
8241
+ mimoTokenPlan: MIMO_TOKEN_PLAN_PREFERRED_MODELS
8157
8242
  };
8158
8243
  const withoutRecommended = filtered.filter((model) => model !== recommendedModel);
8159
8244
  const prioritized = prioritizeModels(withoutRecommended, preferredByProvider[provider]);
@@ -8219,6 +8304,38 @@ async function fetchOllamaModels(provider, config) {
8219
8304
  const ids = (data.models ?? []).map((model) => model.model?.trim() || model.name?.trim() || "").filter(Boolean);
8220
8305
  return buildModelCatalog(provider, ids, config.model);
8221
8306
  }
8307
+ async function fetchMiMoModels(config) {
8308
+ const data = await fetchJson(
8309
+ `${trimTrailingSlash(config.baseUrl)}/models`,
8310
+ {
8311
+ headers: {
8312
+ Authorization: `Bearer ${config.apiKey}`
8313
+ }
8314
+ },
8315
+ "Mercury could not fetch models for this MiMo key. Please re-enter it."
8316
+ );
8317
+ const ids = (data.data ?? []).map((model) => model.id?.trim() ?? "").filter((id) => {
8318
+ const lower = id.toLowerCase();
8319
+ return lower.startsWith("mimo-") && !lower.includes("tts");
8320
+ });
8321
+ return buildModelCatalog("mimo", ids, config.model);
8322
+ }
8323
+ async function fetchMiMoTokenPlanModels(config) {
8324
+ const data = await fetchJson(
8325
+ `${trimTrailingSlash(config.baseUrl)}/models`,
8326
+ {
8327
+ headers: {
8328
+ Authorization: `Bearer ${config.apiKey}`
8329
+ }
8330
+ },
8331
+ "Mercury could not fetch models for this MiMo Token Plan key. Please re-enter it."
8332
+ );
8333
+ const ids = (data.data ?? []).map((model) => model.id?.trim() ?? "").filter((id) => {
8334
+ const lower = id.toLowerCase();
8335
+ return lower.startsWith("mimo-") && !lower.includes("tts");
8336
+ });
8337
+ return buildModelCatalog("mimoTokenPlan", ids, config.model);
8338
+ }
8222
8339
  async function fetchProviderModelCatalog(provider, config) {
8223
8340
  if (provider === "anthropic") {
8224
8341
  return fetchAnthropicModels(config);
@@ -8229,6 +8346,12 @@ async function fetchProviderModelCatalog(provider, config) {
8229
8346
  if (provider === "ollamaCloud" || provider === "ollamaLocal") {
8230
8347
  return fetchOllamaModels(provider, config);
8231
8348
  }
8349
+ if (provider === "mimo") {
8350
+ return fetchMiMoModels(config);
8351
+ }
8352
+ if (provider === "mimoTokenPlan") {
8353
+ return fetchMiMoTokenPlanModels(config);
8354
+ }
8232
8355
  return fetchOpenAICompatModels(provider, config);
8233
8356
  }
8234
8357
 
@@ -8286,7 +8409,9 @@ var PROVIDER_OPTIONS = [
8286
8409
  { key: "anthropic", label: "Anthropic" },
8287
8410
  { key: "grok", label: "Grok (xAI)" },
8288
8411
  { key: "ollamaCloud", label: "Ollama Cloud" },
8289
- { key: "ollamaLocal", label: "Ollama Local" }
8412
+ { key: "ollamaLocal", label: "Ollama Local" },
8413
+ { key: "mimo", label: "MiMo (Xiaomi)" },
8414
+ { key: "mimoTokenPlan", label: "MiMo Token Plan (Xiaomi)" }
8290
8415
  ];
8291
8416
  function getConfiguredProviderNames(config) {
8292
8417
  return PROVIDER_OPTIONS.map((option) => option.key).filter((key) => isProviderConfigured(config.providers[key]));
@@ -8387,6 +8512,12 @@ function validateApiKey(provider, value) {
8387
8512
  if (provider === "ollamaCloud") {
8388
8513
  return looksLikeToken(value) ? null : "Ollama Cloud keys must look like a real API token: long, no spaces, and not plain text.";
8389
8514
  }
8515
+ if (provider === "mimo") {
8516
+ return /^sk-[A-Za-z0-9_-]{16,}$/i.test(value) ? null : "MiMo keys must start with `sk-`.";
8517
+ }
8518
+ if (provider === "mimoTokenPlan") {
8519
+ return /^tp-[A-Za-z0-9_-]{16,}$/i.test(value) ? null : "MiMo Token Plan keys must start with `tp-`.";
8520
+ }
8390
8521
  return null;
8391
8522
  }
8392
8523
  function validateBaseUrl(value) {
@@ -8735,6 +8866,38 @@ async function configure(existingConfig) {
8735
8866
  config.providers.ollamaLocal.model = result.model;
8736
8867
  config.providers.ollamaLocal.enabled = true;
8737
8868
  }
8869
+ continue;
8870
+ }
8871
+ if (provider === "mimo") {
8872
+ const mask = isReconfig && config.providers.mimo.apiKey ? ` [${maskKey(config.providers.mimo.apiKey)}]` : "";
8873
+ const result = await promptApiKeyWithModelSelection(
8874
+ config,
8875
+ "mimo",
8876
+ "MiMo",
8877
+ chalk7.white(` MiMo API key${mask}${isReconfig ? "" : " (Enter to skip)"}: `),
8878
+ isReconfig
8879
+ );
8880
+ if (!result.skipped && result.apiKey && result.model) {
8881
+ config.providers.mimo.apiKey = result.apiKey;
8882
+ config.providers.mimo.model = result.model;
8883
+ config.providers.mimo.enabled = true;
8884
+ }
8885
+ continue;
8886
+ }
8887
+ if (provider === "mimoTokenPlan") {
8888
+ const mask = isReconfig && config.providers.mimoTokenPlan.apiKey ? ` [${maskKey(config.providers.mimoTokenPlan.apiKey)}]` : "";
8889
+ const result = await promptApiKeyWithModelSelection(
8890
+ config,
8891
+ "mimoTokenPlan",
8892
+ "MiMo Token Plan",
8893
+ chalk7.white(` MiMo Token Plan API key${mask}${isReconfig ? "" : " (Enter to skip)"}: `),
8894
+ isReconfig
8895
+ );
8896
+ if (!result.skipped && result.apiKey && result.model) {
8897
+ config.providers.mimoTokenPlan.apiKey = result.apiKey;
8898
+ config.providers.mimoTokenPlan.model = result.model;
8899
+ config.providers.mimoTokenPlan.enabled = true;
8900
+ }
8738
8901
  }
8739
8902
  }
8740
8903
  const configuredProviders = getConfiguredProviderNames(config);