micro-models-agent 0.49.1 → 0.50.0
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/main.js +784 -76
- package/package.json +2 -2
package/dist/main.js
CHANGED
|
@@ -2676,6 +2676,8 @@ Fix the error and re-edit the file (a clean write clears the failure), or mark t
|
|
|
2676
2676
|
"cli.cert_fixture_missing": "Fixture missing for {id}: {path}",
|
|
2677
2677
|
"cli.cert_marks_hint": "Markers: ✔ certified on this provider, ○ certified on an older MMA version, · not certified",
|
|
2678
2678
|
"cli.manage_providers": "Manage providers",
|
|
2679
|
+
"cli.provider_check": "Probe configured providers (free listModels call)",
|
|
2680
|
+
"repl.provider_down": '[WARN] Provider "{name}" is unreachable: {error}',
|
|
2679
2681
|
"cli.list_providers": "List providers",
|
|
2680
2682
|
"cli.current_provider": "Current provider:",
|
|
2681
2683
|
"cli.base_url": "Base URL:",
|
|
@@ -2829,14 +2831,14 @@ Available commands:`,
|
|
|
2829
2831
|
"repl.wizard": "Run setup wizard",
|
|
2830
2832
|
"repl.wizard_usage": "Usage: /wizard",
|
|
2831
2833
|
"repl.wizard_running": "Running setup wizard... (current config will be overwritten)",
|
|
2832
|
-
"repl.provider_list": "
|
|
2834
|
+
"repl.provider_list": "List configured providers or hot-swap the active one",
|
|
2833
2835
|
"repl.provider_current": "Current provider",
|
|
2834
2836
|
"repl.provider_set": "Provider set to: {name}",
|
|
2835
|
-
"repl.provider_usage": "
|
|
2836
|
-
"repl.model_list": "
|
|
2837
|
+
"repl.provider_usage": "/provider list - show entries (* = active, prio = fallback order) | /provider use <name> - switch without restart",
|
|
2838
|
+
"repl.model_list": "List models of the active provider or switch the model",
|
|
2837
2839
|
"repl.model_current": "Current model",
|
|
2838
2840
|
"repl.model_set": "Model set to: {name}",
|
|
2839
|
-
"repl.model_usage": "
|
|
2841
|
+
"repl.model_usage": "/model list - fetch models from the active provider | /model use <name> - switch model",
|
|
2840
2842
|
"repl.group.general": "General",
|
|
2841
2843
|
"repl.group.agent": "Agent",
|
|
2842
2844
|
"repl.group.session": "Sessions",
|
|
@@ -3047,7 +3049,12 @@ Apply a matching solution from these results. If none is relevant — do NOT rep
|
|
|
3047
3049
|
"tools.hidden_header": "Additional tools (enable on demand via enable_tools or route to subagent tool_tags):",
|
|
3048
3050
|
"tool.friendly.enable_tools": "Enable tools",
|
|
3049
3051
|
"cli.provider_base_hint": "Base URL set to: {baseUrl}",
|
|
3052
|
+
"cli.add_provider": "Add a provider to the config",
|
|
3053
|
+
"cli.provider_no_url": 'No base URL for provider "{name}". Pass --url.',
|
|
3054
|
+
"cli.provider_added": "Provider added: {name}",
|
|
3055
|
+
"cli.provider_switch_hint": "Switch to it with: mma provider use <name>",
|
|
3050
3056
|
"repl.cost": "Total cost: {cost}",
|
|
3057
|
+
"repl.cost_breakdown": "By provider: {breakdown}",
|
|
3051
3058
|
"repl.tokens": "Tokens used: {tokens}",
|
|
3052
3059
|
"repl.ctrl_c_interrupt": `
|
|
3053
3060
|
[Ctrl+C] Stopping agent... (press again to force)`,
|
|
@@ -3374,6 +3381,8 @@ var init_ru = __esm(() => {
|
|
|
3374
3381
|
"cli.cert_fixture_missing": "Фикстура отсутствует для {id}: {path}",
|
|
3375
3382
|
"cli.cert_marks_hint": "Маркеры: ✔ сертифицировано на этом провайдере, ○ сертифицировано на старой версии MMA, · не сертифицировано",
|
|
3376
3383
|
"cli.manage_providers": "Управление провайдерами",
|
|
3384
|
+
"cli.provider_check": "Проверить доступность провайдеров (бесплатный вызов listModels)",
|
|
3385
|
+
"repl.provider_down": "[ВНИМАНИЕ] Провайдер «{name}» недоступен: {error}",
|
|
3377
3386
|
"cli.list_providers": "Список провайдеров",
|
|
3378
3387
|
"cli.current_provider": "Текущий провайдер:",
|
|
3379
3388
|
"cli.base_url": "Базовый URL:",
|
|
@@ -3527,14 +3536,14 @@ var init_ru = __esm(() => {
|
|
|
3527
3536
|
"repl.wizard": "Запустить мастер настройки",
|
|
3528
3537
|
"repl.wizard_usage": "Использование: /wizard",
|
|
3529
3538
|
"repl.wizard_running": "Запуск мастера настройки... (текущая конфигурация будет перезаписана)",
|
|
3530
|
-
"repl.provider_list": "
|
|
3539
|
+
"repl.provider_list": "Список настроенных провайдеров или горячая смена активного",
|
|
3531
3540
|
"repl.provider_current": "Текущий провайдер",
|
|
3532
3541
|
"repl.provider_set": "Провайдер установлен: {name}",
|
|
3533
|
-
"repl.provider_usage": "
|
|
3534
|
-
"repl.model_list": "
|
|
3542
|
+
"repl.provider_usage": "/provider list — записи (* = активный, prio = порядок fallback) | /provider use <имя> — смена без перезапуска",
|
|
3543
|
+
"repl.model_list": "Список моделей активного провайдера или смена модели",
|
|
3535
3544
|
"repl.model_current": "Текущая модель",
|
|
3536
3545
|
"repl.model_set": "Модель установлена: {name}",
|
|
3537
|
-
"repl.model_usage": "
|
|
3546
|
+
"repl.model_usage": "/model list — получить модели активного провайдера | /model use <имя> — сменить модель",
|
|
3538
3547
|
"repl.reload": "Перезагрузить агента с текущим конфигом",
|
|
3539
3548
|
"repl.reload_usage": "Использование: /reload",
|
|
3540
3549
|
"repl.reloading": "Перезагрузка агента...",
|
|
@@ -3752,7 +3761,12 @@ var init_ru = __esm(() => {
|
|
|
3752
3761
|
"tools.hidden_header": "Дополнительные тулы (включите по требованию через enable_tools или маршрутизируйте через subagent tool_tags):",
|
|
3753
3762
|
"tool.friendly.enable_tools": "Включить тулы",
|
|
3754
3763
|
"cli.provider_base_hint": "Базовый URL установлен: {baseUrl}",
|
|
3764
|
+
"cli.add_provider": "Добавить провайдера в конфиг",
|
|
3765
|
+
"cli.provider_no_url": 'Нет базового URL для провайдера "{name}". Укажите --url.',
|
|
3766
|
+
"cli.provider_added": "Провайдер добавлен: {name}",
|
|
3767
|
+
"cli.provider_switch_hint": "Переключитесь на него: mma provider use <имя>",
|
|
3755
3768
|
"repl.cost": "Итого потрачено: {cost}",
|
|
3769
|
+
"repl.cost_breakdown": "По провайдерам: {breakdown}",
|
|
3756
3770
|
"repl.tokens": "Потрачено токенов: {tokens}",
|
|
3757
3771
|
"repl.ctrl_c_interrupt": `
|
|
3758
3772
|
[Ctrl+C] Остановка агента... (ещё раз — принудительно)`,
|
|
@@ -4757,6 +4771,26 @@ var init_app_logger = __esm(() => {
|
|
|
4757
4771
|
};
|
|
4758
4772
|
});
|
|
4759
4773
|
|
|
4774
|
+
// src/modules/providers/registry.ts
|
|
4775
|
+
class ProviderRegistry {
|
|
4776
|
+
specs = new Map;
|
|
4777
|
+
register(spec) {
|
|
4778
|
+
this.specs.set(spec.type, spec);
|
|
4779
|
+
}
|
|
4780
|
+
get(type) {
|
|
4781
|
+
return this.specs.get(type);
|
|
4782
|
+
}
|
|
4783
|
+
has(type) {
|
|
4784
|
+
return this.specs.has(type);
|
|
4785
|
+
}
|
|
4786
|
+
list() {
|
|
4787
|
+
return [...this.specs.values()];
|
|
4788
|
+
}
|
|
4789
|
+
types() {
|
|
4790
|
+
return [...this.specs.keys()];
|
|
4791
|
+
}
|
|
4792
|
+
}
|
|
4793
|
+
|
|
4760
4794
|
// node_modules/base64-js/index.js
|
|
4761
4795
|
var require_base64_js = __commonJS((exports) => {
|
|
4762
4796
|
exports.byteLength = byteLength;
|
|
@@ -5646,13 +5680,13 @@ class OpenAICompatProvider {
|
|
|
5646
5680
|
headers
|
|
5647
5681
|
});
|
|
5648
5682
|
if (!response.ok) {
|
|
5649
|
-
|
|
5683
|
+
throw new Error(`HTTP ${response.status} from ${url}`);
|
|
5650
5684
|
}
|
|
5651
5685
|
const data = await response.json();
|
|
5652
5686
|
const models = (data.data || data || []).map((m) => m.id || m.name || m.model || "").filter(Boolean);
|
|
5653
5687
|
return models;
|
|
5654
|
-
} catch {
|
|
5655
|
-
|
|
5688
|
+
} catch (err) {
|
|
5689
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
5656
5690
|
}
|
|
5657
5691
|
}
|
|
5658
5692
|
async fetchWithRetry(url, init) {
|
|
@@ -5681,12 +5715,16 @@ class OpenAICompatProvider {
|
|
|
5681
5715
|
}
|
|
5682
5716
|
}
|
|
5683
5717
|
if (lastStatus === 429) {
|
|
5684
|
-
|
|
5718
|
+
const e429 = new Error(t("error.llm_429", {
|
|
5685
5719
|
model: this.model,
|
|
5686
5720
|
baseUrl: this.config.baseUrl
|
|
5687
5721
|
}));
|
|
5722
|
+
e429.llmStatus = lastStatus;
|
|
5723
|
+
throw e429;
|
|
5688
5724
|
}
|
|
5689
|
-
|
|
5725
|
+
const eRetry = new Error(t("error.llm_retries"));
|
|
5726
|
+
eRetry.llmStatus = lastStatus;
|
|
5727
|
+
throw eRetry;
|
|
5690
5728
|
}
|
|
5691
5729
|
isRetryable(status) {
|
|
5692
5730
|
return status === 429 || status >= 500;
|
|
@@ -5717,6 +5755,399 @@ var init_openai_compat = __esm(() => {
|
|
|
5717
5755
|
init_rate_limiter();
|
|
5718
5756
|
});
|
|
5719
5757
|
|
|
5758
|
+
// src/modules/providers/presets.ts
|
|
5759
|
+
function openaiCompat(opts) {
|
|
5760
|
+
const retry = opts.retry ?? {
|
|
5761
|
+
maxRetries: 3,
|
|
5762
|
+
baseDelay: 1000,
|
|
5763
|
+
maxDelay: 30000
|
|
5764
|
+
};
|
|
5765
|
+
const rateLimits = opts.rateLimits;
|
|
5766
|
+
return new OpenAICompatProvider({
|
|
5767
|
+
model: opts.model,
|
|
5768
|
+
baseUrl: opts.baseUrl,
|
|
5769
|
+
apiKey: opts.apiKey,
|
|
5770
|
+
contextWindow: opts.contextWindow,
|
|
5771
|
+
retry,
|
|
5772
|
+
rateLimits
|
|
5773
|
+
});
|
|
5774
|
+
}
|
|
5775
|
+
var OPENAI_COMPAT, OPENROUTER, OPENAI, ANTHROPIC, OPENCODE_ZEN, OPENCODE_GO, BUILTIN_PROVIDERS, HOSTED_BASE_URLS;
|
|
5776
|
+
var init_presets = __esm(() => {
|
|
5777
|
+
init_openai_compat();
|
|
5778
|
+
OPENAI_COMPAT = {
|
|
5779
|
+
type: "openai-compat",
|
|
5780
|
+
label: "OpenAI-compatible (LM Studio, Ollama, vLLM, etc.)",
|
|
5781
|
+
baseUrl: "",
|
|
5782
|
+
capabilities: {
|
|
5783
|
+
streaming: true,
|
|
5784
|
+
tools: true,
|
|
5785
|
+
vision: false,
|
|
5786
|
+
reasoningEffort: false,
|
|
5787
|
+
listModels: true,
|
|
5788
|
+
requiresKey: false,
|
|
5789
|
+
auth: "bearer"
|
|
5790
|
+
},
|
|
5791
|
+
create: openaiCompat
|
|
5792
|
+
};
|
|
5793
|
+
OPENROUTER = {
|
|
5794
|
+
type: "openrouter",
|
|
5795
|
+
label: "OpenRouter (openrouter.ai)",
|
|
5796
|
+
baseUrl: "https://openrouter.ai/api/v1",
|
|
5797
|
+
capabilities: {
|
|
5798
|
+
streaming: true,
|
|
5799
|
+
tools: true,
|
|
5800
|
+
vision: true,
|
|
5801
|
+
reasoningEffort: false,
|
|
5802
|
+
listModels: true,
|
|
5803
|
+
requiresKey: true,
|
|
5804
|
+
auth: "bearer"
|
|
5805
|
+
},
|
|
5806
|
+
create: openaiCompat
|
|
5807
|
+
};
|
|
5808
|
+
OPENAI = {
|
|
5809
|
+
type: "openai",
|
|
5810
|
+
label: "OpenAI API",
|
|
5811
|
+
baseUrl: "https://api.openai.com/v1",
|
|
5812
|
+
capabilities: {
|
|
5813
|
+
streaming: true,
|
|
5814
|
+
tools: true,
|
|
5815
|
+
vision: true,
|
|
5816
|
+
reasoningEffort: true,
|
|
5817
|
+
listModels: true,
|
|
5818
|
+
requiresKey: true,
|
|
5819
|
+
auth: "bearer"
|
|
5820
|
+
},
|
|
5821
|
+
create: openaiCompat
|
|
5822
|
+
};
|
|
5823
|
+
ANTHROPIC = {
|
|
5824
|
+
type: "anthropic",
|
|
5825
|
+
label: "Anthropic Claude",
|
|
5826
|
+
baseUrl: "https://api.anthropic.com",
|
|
5827
|
+
capabilities: {
|
|
5828
|
+
streaming: false,
|
|
5829
|
+
tools: false,
|
|
5830
|
+
vision: true,
|
|
5831
|
+
reasoningEffort: false,
|
|
5832
|
+
listModels: false,
|
|
5833
|
+
requiresKey: true,
|
|
5834
|
+
auth: "header"
|
|
5835
|
+
},
|
|
5836
|
+
create: openaiCompat
|
|
5837
|
+
};
|
|
5838
|
+
OPENCODE_ZEN = {
|
|
5839
|
+
type: "opencode-zen",
|
|
5840
|
+
label: "OpenCode Zen (opencode.ai/zen)",
|
|
5841
|
+
baseUrl: "https://opencode.ai/zen/v1",
|
|
5842
|
+
capabilities: {
|
|
5843
|
+
streaming: true,
|
|
5844
|
+
tools: true,
|
|
5845
|
+
vision: false,
|
|
5846
|
+
reasoningEffort: false,
|
|
5847
|
+
listModels: true,
|
|
5848
|
+
requiresKey: false,
|
|
5849
|
+
auth: "bearer"
|
|
5850
|
+
},
|
|
5851
|
+
create: openaiCompat
|
|
5852
|
+
};
|
|
5853
|
+
OPENCODE_GO = {
|
|
5854
|
+
type: "opencode-go",
|
|
5855
|
+
label: "OpenCode Go (opencode.ai/go)",
|
|
5856
|
+
baseUrl: "https://opencode.ai/zen/go/v1",
|
|
5857
|
+
capabilities: {
|
|
5858
|
+
streaming: true,
|
|
5859
|
+
tools: true,
|
|
5860
|
+
vision: false,
|
|
5861
|
+
reasoningEffort: false,
|
|
5862
|
+
listModels: true,
|
|
5863
|
+
requiresKey: false,
|
|
5864
|
+
auth: "bearer"
|
|
5865
|
+
},
|
|
5866
|
+
create: openaiCompat
|
|
5867
|
+
};
|
|
5868
|
+
BUILTIN_PROVIDERS = [
|
|
5869
|
+
OPENAI_COMPAT,
|
|
5870
|
+
OPENROUTER,
|
|
5871
|
+
OPENAI,
|
|
5872
|
+
ANTHROPIC,
|
|
5873
|
+
OPENCODE_ZEN,
|
|
5874
|
+
OPENCODE_GO
|
|
5875
|
+
];
|
|
5876
|
+
HOSTED_BASE_URLS = {};
|
|
5877
|
+
for (const spec of BUILTIN_PROVIDERS) {
|
|
5878
|
+
if (spec.baseUrl)
|
|
5879
|
+
HOSTED_BASE_URLS[spec.type] = spec.baseUrl;
|
|
5880
|
+
}
|
|
5881
|
+
});
|
|
5882
|
+
|
|
5883
|
+
// src/modules/providers/create.ts
|
|
5884
|
+
var exports_create = {};
|
|
5885
|
+
__export(exports_create, {
|
|
5886
|
+
defaultRegistry: () => defaultRegistry,
|
|
5887
|
+
createProvider: () => createProvider
|
|
5888
|
+
});
|
|
5889
|
+
function createProvider(type, opts, registry = defaultRegistry()) {
|
|
5890
|
+
const spec = registry.get(type);
|
|
5891
|
+
if (!spec) {
|
|
5892
|
+
return BUILTIN_PROVIDERS[0].create(opts);
|
|
5893
|
+
}
|
|
5894
|
+
return spec.create(opts);
|
|
5895
|
+
}
|
|
5896
|
+
function defaultRegistry() {
|
|
5897
|
+
const registry = new ProviderRegistry;
|
|
5898
|
+
for (const spec of BUILTIN_PROVIDERS) {
|
|
5899
|
+
registry.register(spec);
|
|
5900
|
+
}
|
|
5901
|
+
return registry;
|
|
5902
|
+
}
|
|
5903
|
+
var init_create = __esm(() => {
|
|
5904
|
+
init_presets();
|
|
5905
|
+
});
|
|
5906
|
+
|
|
5907
|
+
// src/modules/providers/manager.ts
|
|
5908
|
+
class ProviderManager {
|
|
5909
|
+
entries;
|
|
5910
|
+
activeProviderName;
|
|
5911
|
+
model;
|
|
5912
|
+
opts;
|
|
5913
|
+
registry;
|
|
5914
|
+
cache = new Map;
|
|
5915
|
+
constructor(config, opts) {
|
|
5916
|
+
this.opts = opts;
|
|
5917
|
+
this.registry = opts.registry ?? defaultRegistry();
|
|
5918
|
+
this.entries = this.normalizeEntries(config);
|
|
5919
|
+
this.activeProviderName = this.resolveActive(config);
|
|
5920
|
+
this.model = "";
|
|
5921
|
+
}
|
|
5922
|
+
get activeModel() {
|
|
5923
|
+
return this.model;
|
|
5924
|
+
}
|
|
5925
|
+
get activeName() {
|
|
5926
|
+
return this.activeProviderName;
|
|
5927
|
+
}
|
|
5928
|
+
get active() {
|
|
5929
|
+
return this.build(this.activeProviderName);
|
|
5930
|
+
}
|
|
5931
|
+
get activeEntry() {
|
|
5932
|
+
const entry = this.entryByName(this.activeProviderName);
|
|
5933
|
+
if (!entry)
|
|
5934
|
+
throw new Error(`Unknown active provider "${this.activeProviderName}"`);
|
|
5935
|
+
return entry;
|
|
5936
|
+
}
|
|
5937
|
+
list() {
|
|
5938
|
+
return this.entries.map((e) => {
|
|
5939
|
+
const preset = this.registry.get(e.type);
|
|
5940
|
+
return { ...e, label: e.label || preset?.label || e.type };
|
|
5941
|
+
}).map((e, i) => ({ e, i, p: e.priority ?? Number.MAX_SAFE_INTEGER })).sort((a, b) => a.p - b.p || a.i - b.i).map(({ e }) => e);
|
|
5942
|
+
}
|
|
5943
|
+
listNames() {
|
|
5944
|
+
return this.list().map((e) => e.label);
|
|
5945
|
+
}
|
|
5946
|
+
pickFor(needs) {
|
|
5947
|
+
const globalWindow = this.opts.contextWindow;
|
|
5948
|
+
if (!needs.vision && !needs.tools && needs.minContext === undefined) {
|
|
5949
|
+
return this.activeProviderName;
|
|
5950
|
+
}
|
|
5951
|
+
for (const e of this.list()) {
|
|
5952
|
+
const caps = this.registry.get(e.type)?.capabilities;
|
|
5953
|
+
if (needs.vision && !caps?.vision)
|
|
5954
|
+
continue;
|
|
5955
|
+
if (needs.tools && !caps?.tools)
|
|
5956
|
+
continue;
|
|
5957
|
+
if (needs.minContext !== undefined && (e.contextWindow ?? globalWindow) < needs.minContext) {
|
|
5958
|
+
continue;
|
|
5959
|
+
}
|
|
5960
|
+
return e.label;
|
|
5961
|
+
}
|
|
5962
|
+
return;
|
|
5963
|
+
}
|
|
5964
|
+
switch(name, model) {
|
|
5965
|
+
if (!this.entryByName(name)) {
|
|
5966
|
+
throw new Error(`Unknown provider "${name}"`);
|
|
5967
|
+
}
|
|
5968
|
+
this.activeProviderName = name;
|
|
5969
|
+
if (model !== undefined)
|
|
5970
|
+
this.model = model;
|
|
5971
|
+
return this.active;
|
|
5972
|
+
}
|
|
5973
|
+
setModel(model) {
|
|
5974
|
+
this.model = model;
|
|
5975
|
+
return this.active;
|
|
5976
|
+
}
|
|
5977
|
+
toConfig() {
|
|
5978
|
+
const active = this.activeEntry;
|
|
5979
|
+
return {
|
|
5980
|
+
active: this.activeProviderName,
|
|
5981
|
+
entries: this.entries,
|
|
5982
|
+
fallback: false,
|
|
5983
|
+
type: active.type,
|
|
5984
|
+
baseUrl: active.baseUrl,
|
|
5985
|
+
apiKey: active.apiKey
|
|
5986
|
+
};
|
|
5987
|
+
}
|
|
5988
|
+
normalizeEntries(config) {
|
|
5989
|
+
if (Array.isArray(config.entries) && config.entries.length > 0) {
|
|
5990
|
+
return config.entries.map((e) => ({ ...e, label: e.label || e.type }));
|
|
5991
|
+
}
|
|
5992
|
+
return [
|
|
5993
|
+
{
|
|
5994
|
+
type: config.type ?? "openai-compat",
|
|
5995
|
+
label: config.type ?? "openai-compat",
|
|
5996
|
+
baseUrl: config.baseUrl ?? "",
|
|
5997
|
+
apiKey: config.apiKey
|
|
5998
|
+
}
|
|
5999
|
+
];
|
|
6000
|
+
}
|
|
6001
|
+
resolveActive(config) {
|
|
6002
|
+
if (config.active && this.entryByName(config.active))
|
|
6003
|
+
return config.active;
|
|
6004
|
+
const first = this.entries[0];
|
|
6005
|
+
return first ? first.label ?? "openai-compat" : "openai-compat";
|
|
6006
|
+
}
|
|
6007
|
+
entryByName(name) {
|
|
6008
|
+
return this.entries.find((e) => e.label === name || e.type === name);
|
|
6009
|
+
}
|
|
6010
|
+
build(name) {
|
|
6011
|
+
const entry = this.entryByName(name);
|
|
6012
|
+
if (!entry)
|
|
6013
|
+
throw new Error(`Unknown provider "${name}"`);
|
|
6014
|
+
const key = `${name}|${this.model}`;
|
|
6015
|
+
const cached = this.cache.get(key);
|
|
6016
|
+
if (cached)
|
|
6017
|
+
return cached;
|
|
6018
|
+
const provider = createProvider(entry.type, {
|
|
6019
|
+
model: this.model,
|
|
6020
|
+
baseUrl: entry.baseUrl,
|
|
6021
|
+
apiKey: entry.apiKey,
|
|
6022
|
+
contextWindow: entry.contextWindow ?? this.opts.contextWindow,
|
|
6023
|
+
retry: entry.retry ?? this.opts.retry,
|
|
6024
|
+
rateLimits: entry.rateLimits ?? this.opts.rateLimits
|
|
6025
|
+
}, this.registry);
|
|
6026
|
+
this.cache.set(key, provider);
|
|
6027
|
+
return provider;
|
|
6028
|
+
}
|
|
6029
|
+
resetCache() {
|
|
6030
|
+
this.cache.clear();
|
|
6031
|
+
}
|
|
6032
|
+
}
|
|
6033
|
+
var init_manager = __esm(() => {
|
|
6034
|
+
init_create();
|
|
6035
|
+
init_presets();
|
|
6036
|
+
});
|
|
6037
|
+
|
|
6038
|
+
// src/modules/providers/health.ts
|
|
6039
|
+
async function probeProviders(targets, opts) {
|
|
6040
|
+
const timeoutMs = opts?.timeoutMs ?? 5000;
|
|
6041
|
+
const results = await Promise.all(targets.map((t2) => probeOne(t2, timeoutMs)));
|
|
6042
|
+
return results;
|
|
6043
|
+
}
|
|
6044
|
+
async function probeOne(target, timeoutMs) {
|
|
6045
|
+
const start = Date.now();
|
|
6046
|
+
try {
|
|
6047
|
+
const models = await withTimeout(target.listModels(), timeoutMs);
|
|
6048
|
+
return { name: target.name, ok: true, ms: Date.now() - start, models: models.length };
|
|
6049
|
+
} catch (err) {
|
|
6050
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6051
|
+
return {
|
|
6052
|
+
name: target.name,
|
|
6053
|
+
ok: false,
|
|
6054
|
+
ms: Date.now() - start,
|
|
6055
|
+
models: 0,
|
|
6056
|
+
error: message
|
|
6057
|
+
};
|
|
6058
|
+
}
|
|
6059
|
+
}
|
|
6060
|
+
function withTimeout(promise, ms) {
|
|
6061
|
+
return new Promise((resolve, reject) => {
|
|
6062
|
+
const timer = setTimeout(() => reject(new Error(`timeout after ${ms}ms`)), ms);
|
|
6063
|
+
promise.then((v) => {
|
|
6064
|
+
clearTimeout(timer);
|
|
6065
|
+
resolve(v);
|
|
6066
|
+
}, (e) => {
|
|
6067
|
+
clearTimeout(timer);
|
|
6068
|
+
reject(e);
|
|
6069
|
+
});
|
|
6070
|
+
});
|
|
6071
|
+
}
|
|
6072
|
+
function targetsFromProviders(entries) {
|
|
6073
|
+
return entries.map(({ name, provider }) => ({
|
|
6074
|
+
name,
|
|
6075
|
+
listModels: () => provider.listModels()
|
|
6076
|
+
}));
|
|
6077
|
+
}
|
|
6078
|
+
|
|
6079
|
+
// src/modules/providers/index.ts
|
|
6080
|
+
var init_providers = __esm(() => {
|
|
6081
|
+
init_create();
|
|
6082
|
+
init_manager();
|
|
6083
|
+
init_presets();
|
|
6084
|
+
});
|
|
6085
|
+
|
|
6086
|
+
// src/modules/providers/fallback.ts
|
|
6087
|
+
function isFailoverError(err) {
|
|
6088
|
+
const e = err;
|
|
6089
|
+
if (e?.name === "AbortError")
|
|
6090
|
+
return false;
|
|
6091
|
+
if (typeof e?.llmStatus === "number")
|
|
6092
|
+
return e.llmStatus === 429 || e.llmStatus >= 500;
|
|
6093
|
+
const normCode = (e?.code ?? "").replace(/[-_]/g, "").toLowerCase();
|
|
6094
|
+
if (/^abort/.test(normCode))
|
|
6095
|
+
return false;
|
|
6096
|
+
if (/conn(refused|reset|timedout)|enotfound|eai_again|namenotresolved/.test(normCode)) {
|
|
6097
|
+
return true;
|
|
6098
|
+
}
|
|
6099
|
+
return /fetch failed|unable to connect|network|econnrefused|enotfound|etimedout|socket hang up|connection refused|connection reset/i.test(e?.message ?? "");
|
|
6100
|
+
}
|
|
6101
|
+
|
|
6102
|
+
class FallbackProvider {
|
|
6103
|
+
source;
|
|
6104
|
+
onFailover;
|
|
6105
|
+
constructor(source, onFailover) {
|
|
6106
|
+
this.source = source;
|
|
6107
|
+
this.onFailover = onFailover;
|
|
6108
|
+
}
|
|
6109
|
+
get model() {
|
|
6110
|
+
return this.source.active.model;
|
|
6111
|
+
}
|
|
6112
|
+
get contextWindow() {
|
|
6113
|
+
return this.source.active.contextWindow;
|
|
6114
|
+
}
|
|
6115
|
+
async* chat(messages, tools, signal, options) {
|
|
6116
|
+
const names = this.source.listNames();
|
|
6117
|
+
if (names.length <= 1) {
|
|
6118
|
+
yield* this.source.active.chat(messages, tools, signal, options);
|
|
6119
|
+
return;
|
|
6120
|
+
}
|
|
6121
|
+
let emitted = false;
|
|
6122
|
+
let idx = Math.max(0, names.indexOf(this.source.activeName));
|
|
6123
|
+
let lastError;
|
|
6124
|
+
for (let attempt = 0;attempt < names.length; attempt++) {
|
|
6125
|
+
const name = names[idx];
|
|
6126
|
+
const provider = name === this.source.activeName ? this.source.active : this.source.switch(name);
|
|
6127
|
+
try {
|
|
6128
|
+
for await (const chunk of provider.chat(messages, tools, signal, options)) {
|
|
6129
|
+
emitted = true;
|
|
6130
|
+
yield chunk;
|
|
6131
|
+
}
|
|
6132
|
+
return;
|
|
6133
|
+
} catch (err) {
|
|
6134
|
+
if (emitted || !isFailoverError(err))
|
|
6135
|
+
throw err;
|
|
6136
|
+
lastError = err;
|
|
6137
|
+
idx = (idx + 1) % names.length;
|
|
6138
|
+
this.onFailover?.(name, names[idx], err);
|
|
6139
|
+
}
|
|
6140
|
+
}
|
|
6141
|
+
throw lastError;
|
|
6142
|
+
}
|
|
6143
|
+
countTokens(text) {
|
|
6144
|
+
return this.source.active.countTokens(text);
|
|
6145
|
+
}
|
|
6146
|
+
async listModels() {
|
|
6147
|
+
return this.source.active.listModels();
|
|
6148
|
+
}
|
|
6149
|
+
}
|
|
6150
|
+
|
|
5720
6151
|
// src/llm/model-loader.ts
|
|
5721
6152
|
class ModelLoader {
|
|
5722
6153
|
baseUrl;
|
|
@@ -6018,6 +6449,16 @@ function safeResolvePath(baseDir, userPath) {
|
|
|
6018
6449
|
return asIs;
|
|
6019
6450
|
}
|
|
6020
6451
|
const norm = normalize(userPath);
|
|
6452
|
+
if (isAbsolute(norm)) {
|
|
6453
|
+
if (existsSync7(norm) || existsSync7(dirname2(norm)))
|
|
6454
|
+
return norm;
|
|
6455
|
+
const stripped2 = norm.replace(/^[/\\]/, "");
|
|
6456
|
+
const relativeCandidate = resolve(baseDir, stripped2);
|
|
6457
|
+
if (existsSync7(relativeCandidate) || existsSync7(dirname2(relativeCandidate))) {
|
|
6458
|
+
return relativeCandidate;
|
|
6459
|
+
}
|
|
6460
|
+
return norm;
|
|
6461
|
+
}
|
|
6021
6462
|
const stripped = norm.replace(/^[/\\]/, "");
|
|
6022
6463
|
const resolved = resolve(baseDir, stripped);
|
|
6023
6464
|
if (existsSync7(resolved) || existsSync7(dirname2(resolved)))
|
|
@@ -8636,9 +9077,11 @@ import { readFileSync as readFileSync9, existsSync as existsSync18 } from "fs";
|
|
|
8636
9077
|
|
|
8637
9078
|
class SessionLogger {
|
|
8638
9079
|
session;
|
|
9080
|
+
getCaller;
|
|
8639
9081
|
logger;
|
|
8640
|
-
constructor(session, logger) {
|
|
9082
|
+
constructor(session, logger, getCaller) {
|
|
8641
9083
|
this.session = session;
|
|
9084
|
+
this.getCaller = getCaller;
|
|
8642
9085
|
this.logger = logger;
|
|
8643
9086
|
this.bindSessionDir();
|
|
8644
9087
|
}
|
|
@@ -8677,12 +9120,15 @@ class SessionLogger {
|
|
|
8677
9120
|
});
|
|
8678
9121
|
}
|
|
8679
9122
|
logAssistant(content, reasoning, toolCalls, iteration) {
|
|
9123
|
+
const caller = this.getCaller?.();
|
|
8680
9124
|
if (reasoning) {
|
|
8681
9125
|
this.session?.appendLog({
|
|
8682
9126
|
ts: new Date().toISOString(),
|
|
8683
9127
|
type: "reasoning",
|
|
8684
9128
|
content: reasoning,
|
|
8685
|
-
iteration
|
|
9129
|
+
iteration,
|
|
9130
|
+
provider: caller?.provider,
|
|
9131
|
+
model: caller?.model
|
|
8686
9132
|
});
|
|
8687
9133
|
}
|
|
8688
9134
|
this.session?.appendLog({
|
|
@@ -8696,14 +9142,19 @@ class SessionLogger {
|
|
|
8696
9142
|
arguments: tc.arguments
|
|
8697
9143
|
}))
|
|
8698
9144
|
} : {},
|
|
8699
|
-
iteration
|
|
9145
|
+
iteration,
|
|
9146
|
+
provider: caller?.provider,
|
|
9147
|
+
model: caller?.model
|
|
8700
9148
|
});
|
|
8701
9149
|
}
|
|
8702
9150
|
saveAssistantMessage(content) {
|
|
9151
|
+
const caller = this.getCaller?.();
|
|
8703
9152
|
this.session?.appendMessage({
|
|
8704
9153
|
role: "assistant",
|
|
8705
9154
|
content: content.slice(0, 500),
|
|
8706
|
-
timestamp: new Date().toISOString()
|
|
9155
|
+
timestamp: new Date().toISOString(),
|
|
9156
|
+
provider: caller?.provider,
|
|
9157
|
+
model: caller?.model
|
|
8707
9158
|
});
|
|
8708
9159
|
}
|
|
8709
9160
|
logToolDefs(toolCount, toolNames, iteration) {
|
|
@@ -8726,11 +9177,14 @@ class SessionLogger {
|
|
|
8726
9177
|
});
|
|
8727
9178
|
}
|
|
8728
9179
|
logToolResult(call, result, duration, iteration) {
|
|
9180
|
+
const caller = this.getCaller?.();
|
|
8729
9181
|
this.session?.appendMessage({
|
|
8730
9182
|
role: "tool",
|
|
8731
9183
|
content: sanitizeLogMessage(result.output.slice(0, 500)),
|
|
8732
9184
|
name: call.name,
|
|
8733
|
-
timestamp: new Date().toISOString()
|
|
9185
|
+
timestamp: new Date().toISOString(),
|
|
9186
|
+
provider: caller?.provider,
|
|
9187
|
+
model: caller?.model
|
|
8734
9188
|
});
|
|
8735
9189
|
this.session?.appendLog({
|
|
8736
9190
|
ts: new Date().toISOString(),
|
|
@@ -8741,7 +9195,9 @@ class SessionLogger {
|
|
|
8741
9195
|
content: sanitizeLogMessage(result.output.slice(0, 1000)),
|
|
8742
9196
|
diff: result.diff,
|
|
8743
9197
|
duration,
|
|
8744
|
-
iteration
|
|
9198
|
+
iteration,
|
|
9199
|
+
provider: caller?.provider,
|
|
9200
|
+
model: caller?.model
|
|
8745
9201
|
});
|
|
8746
9202
|
}
|
|
8747
9203
|
logCompaction(info) {
|
|
@@ -8768,6 +9224,8 @@ class SessionLogger {
|
|
|
8768
9224
|
type: "context",
|
|
8769
9225
|
content: info.kind === "start" ? "context snapshot at session start" : `context at iteration ${info.iteration}`,
|
|
8770
9226
|
iteration: info.iteration,
|
|
9227
|
+
provider: info.provider,
|
|
9228
|
+
model: info.model,
|
|
8771
9229
|
window: info.window,
|
|
8772
9230
|
systemBudget: info.systemBudget,
|
|
8773
9231
|
reserveBudget: info.reserveBudget,
|
|
@@ -8787,6 +9245,8 @@ class SessionLogger {
|
|
|
8787
9245
|
ts: new Date().toISOString(),
|
|
8788
9246
|
type: "llm_usage",
|
|
8789
9247
|
iteration,
|
|
9248
|
+
provider: usage.provider,
|
|
9249
|
+
model: usage.model,
|
|
8790
9250
|
promptTokens: usage.promptTokens,
|
|
8791
9251
|
completionTokens: usage.completionTokens,
|
|
8792
9252
|
totalTokens: usage.totalTokens,
|
|
@@ -9622,18 +10082,20 @@ class OrchestratorClient {
|
|
|
9622
10082
|
this.config = config;
|
|
9623
10083
|
if (config.model) {
|
|
9624
10084
|
if (config.provider) {
|
|
9625
|
-
this.provider =
|
|
10085
|
+
this.provider = createProvider(config.provider.type ?? "openai-compat", {
|
|
9626
10086
|
model: config.model,
|
|
9627
10087
|
baseUrl: config.provider.baseUrl || "http://localhost:1234/v1",
|
|
9628
10088
|
apiKey: config.provider.apiKey,
|
|
10089
|
+
contextWindow: 32768,
|
|
9629
10090
|
retry: config.retry
|
|
9630
10091
|
});
|
|
9631
10092
|
} else if (defaultProvider) {
|
|
9632
10093
|
this.provider = defaultProvider;
|
|
9633
10094
|
} else {
|
|
9634
|
-
this.provider =
|
|
10095
|
+
this.provider = createProvider("openai-compat", {
|
|
9635
10096
|
model: config.model,
|
|
9636
10097
|
baseUrl: "http://localhost:1234/v1",
|
|
10098
|
+
contextWindow: 32768,
|
|
9637
10099
|
retry: config.retry
|
|
9638
10100
|
});
|
|
9639
10101
|
}
|
|
@@ -9800,7 +10262,7 @@ Respond with JSON only:
|
|
|
9800
10262
|
|
|
9801
10263
|
Max 3 re-plan cycles. After 3 cycles, return partial result.`;
|
|
9802
10264
|
var init_orchestrator = __esm(() => {
|
|
9803
|
-
|
|
10265
|
+
init_create();
|
|
9804
10266
|
init_esm();
|
|
9805
10267
|
});
|
|
9806
10268
|
|
|
@@ -11849,6 +12311,7 @@ class CostTracker {
|
|
|
11849
12311
|
model;
|
|
11850
12312
|
_total = 0;
|
|
11851
12313
|
_known = false;
|
|
12314
|
+
byProvider = new Map;
|
|
11852
12315
|
constructor(model, config) {
|
|
11853
12316
|
this.model = model;
|
|
11854
12317
|
this.config = config;
|
|
@@ -11856,16 +12319,36 @@ class CostTracker {
|
|
|
11856
12319
|
setModel(model) {
|
|
11857
12320
|
this.model = model;
|
|
11858
12321
|
}
|
|
11859
|
-
record(promptTokens, completionTokens) {
|
|
12322
|
+
record(promptTokens, completionTokens, provider) {
|
|
12323
|
+
const key = provider || "unknown";
|
|
11860
12324
|
const cost = calculateCost(this.model, promptTokens, completionTokens, this.config);
|
|
11861
12325
|
if (cost === undefined)
|
|
11862
12326
|
return;
|
|
11863
12327
|
this._known = true;
|
|
11864
12328
|
this._total += cost;
|
|
12329
|
+
let entry = this.byProvider.get(key);
|
|
12330
|
+
if (!entry) {
|
|
12331
|
+
entry = { total: 0, known: false };
|
|
12332
|
+
this.byProvider.set(key, entry);
|
|
12333
|
+
}
|
|
12334
|
+
entry.known = true;
|
|
12335
|
+
entry.total += cost;
|
|
11865
12336
|
}
|
|
11866
12337
|
get total() {
|
|
11867
12338
|
return this._known ? this._total : undefined;
|
|
11868
12339
|
}
|
|
12340
|
+
totalFor(provider) {
|
|
12341
|
+
const entry = this.byProvider.get(provider);
|
|
12342
|
+
return entry && entry.known ? entry.total : undefined;
|
|
12343
|
+
}
|
|
12344
|
+
breakdown() {
|
|
12345
|
+
const entries = [];
|
|
12346
|
+
for (const [provider, entry] of this.byProvider) {
|
|
12347
|
+
if (entry.known)
|
|
12348
|
+
entries.push({ provider, cost: entry.total });
|
|
12349
|
+
}
|
|
12350
|
+
return entries.sort((a, b) => b.cost - a.cost);
|
|
12351
|
+
}
|
|
11869
12352
|
get formatted() {
|
|
11870
12353
|
return this._known ? formatCost(this._total) : undefined;
|
|
11871
12354
|
}
|
|
@@ -11908,6 +12391,12 @@ class Agent {
|
|
|
11908
12391
|
get contextManager() {
|
|
11909
12392
|
return this.deps.contextManager;
|
|
11910
12393
|
}
|
|
12394
|
+
callerProvenance() {
|
|
12395
|
+
return {
|
|
12396
|
+
provider: this.deps.config.provider?.type || "unknown",
|
|
12397
|
+
model: this.deps.config.model
|
|
12398
|
+
};
|
|
12399
|
+
}
|
|
11911
12400
|
getModule(name) {
|
|
11912
12401
|
return this.deps.moduleRegistry?.get(name);
|
|
11913
12402
|
}
|
|
@@ -12022,7 +12511,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
12022
12511
|
sessionManager,
|
|
12023
12512
|
baseDir
|
|
12024
12513
|
} = this.deps;
|
|
12025
|
-
const slog = new SessionLogger(sessionManager, logger);
|
|
12514
|
+
const slog = new SessionLogger(sessionManager, logger, () => this.callerProvenance());
|
|
12026
12515
|
if (sessionManager && !sessionManager.getActive()) {
|
|
12027
12516
|
sessionManager.create();
|
|
12028
12517
|
logger.debug(`Session started: ${sessionManager.getActive()}`);
|
|
@@ -12076,7 +12565,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
12076
12565
|
sessionManager,
|
|
12077
12566
|
baseDir
|
|
12078
12567
|
} = this.deps;
|
|
12079
|
-
const slog = new SessionLogger(sessionManager, logger);
|
|
12568
|
+
const slog = new SessionLogger(sessionManager, logger, () => this.callerProvenance());
|
|
12080
12569
|
this.abortController = new AbortController;
|
|
12081
12570
|
let iteration = 0;
|
|
12082
12571
|
let lastText = "";
|
|
@@ -12255,7 +12744,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
12255
12744
|
source,
|
|
12256
12745
|
durationMs: Date.now() - llmStart
|
|
12257
12746
|
});
|
|
12258
|
-
this.costTracker.record(prompt, completion);
|
|
12747
|
+
this.costTracker.record(prompt, completion, this.callerProvenance().provider);
|
|
12259
12748
|
}
|
|
12260
12749
|
if (this.shutdownRequested) {
|
|
12261
12750
|
break;
|
|
@@ -12608,6 +13097,7 @@ ${warnLine}
|
|
|
12608
13097
|
completionTokens: usageTokens.completion,
|
|
12609
13098
|
totalTokens: usageTokens.total,
|
|
12610
13099
|
totalCost: this.costTracker.total,
|
|
13100
|
+
costBreakdown: this.costTracker.breakdown(),
|
|
12611
13101
|
compactionCount: contextManager.getCompactionCount(),
|
|
12612
13102
|
contextQuality: contextManager.getQuality()
|
|
12613
13103
|
};
|
|
@@ -12623,6 +13113,7 @@ ${warnLine}
|
|
|
12623
13113
|
completionTokens: usageTokens.completion,
|
|
12624
13114
|
totalTokens: usageTokens.total,
|
|
12625
13115
|
totalCost: this.costTracker.total,
|
|
13116
|
+
costBreakdown: this.costTracker.breakdown(),
|
|
12626
13117
|
compactionCount: contextManager.getCompactionCount(),
|
|
12627
13118
|
contextQuality: contextManager.getQuality()
|
|
12628
13119
|
};
|
|
@@ -12649,6 +13140,45 @@ ${warnLine}
|
|
|
12649
13140
|
this.deps.config = config;
|
|
12650
13141
|
this.costTracker.setModel(config.model);
|
|
12651
13142
|
}
|
|
13143
|
+
setProvider(name, model) {
|
|
13144
|
+
const manager = new ProviderManager(this.deps.config.provider, {
|
|
13145
|
+
contextWindow: this.deps.config.contextWindow,
|
|
13146
|
+
retry: this.deps.config.retry,
|
|
13147
|
+
rateLimits: this.deps.config.security?.rateLimits
|
|
13148
|
+
});
|
|
13149
|
+
manager.switch(name, model);
|
|
13150
|
+
const providerCfg = manager.toConfig();
|
|
13151
|
+
this.deps.config.provider = providerCfg;
|
|
13152
|
+
this.deps.llmProvider = manager.active;
|
|
13153
|
+
this.deps.toolExecutor.updateProvider(manager.active);
|
|
13154
|
+
this.deps.toolExecutor.ctx.llmProvider = manager.active;
|
|
13155
|
+
}
|
|
13156
|
+
listProviders() {
|
|
13157
|
+
const manager = new ProviderManager(this.deps.config.provider, {
|
|
13158
|
+
contextWindow: this.deps.config.contextWindow,
|
|
13159
|
+
retry: this.deps.config.retry,
|
|
13160
|
+
rateLimits: this.deps.config.security?.rateLimits
|
|
13161
|
+
});
|
|
13162
|
+
const activeName = this.deps.config.provider.active;
|
|
13163
|
+
return manager.list().map((e) => ({
|
|
13164
|
+
...e,
|
|
13165
|
+
active: e.label === activeName || e.type === activeName
|
|
13166
|
+
}));
|
|
13167
|
+
}
|
|
13168
|
+
getProviderFor(name) {
|
|
13169
|
+
try {
|
|
13170
|
+
const manager = new ProviderManager(this.deps.config.provider, {
|
|
13171
|
+
contextWindow: this.deps.config.contextWindow,
|
|
13172
|
+
retry: this.deps.config.retry,
|
|
13173
|
+
rateLimits: this.deps.config.security?.rateLimits
|
|
13174
|
+
});
|
|
13175
|
+
manager.setModel(this.deps.config.model);
|
|
13176
|
+
manager.switch(name);
|
|
13177
|
+
return manager.active;
|
|
13178
|
+
} catch {
|
|
13179
|
+
return;
|
|
13180
|
+
}
|
|
13181
|
+
}
|
|
12652
13182
|
setContext(messages) {
|
|
12653
13183
|
const { contextManager } = this.deps;
|
|
12654
13184
|
contextManager.clear();
|
|
@@ -12683,6 +13213,7 @@ ${warnLine}
|
|
|
12683
13213
|
}
|
|
12684
13214
|
var TOOL_RESULT_MAX_TOKENS_RATIO = 0.3, TOOL_RESULT_ABSOLUTE_MAX_CHARS = 15000, QUALITY_TRIGGER_THRESHOLD = 40, FORCED_COMPACTION_COOLDOWN = 3;
|
|
12685
13215
|
var init_agent = __esm(() => {
|
|
13216
|
+
init_manager();
|
|
12686
13217
|
init_i18n();
|
|
12687
13218
|
init_colors();
|
|
12688
13219
|
init_prompt_builder();
|
|
@@ -13158,7 +13689,7 @@ ${lines.join(`
|
|
|
13158
13689
|
}
|
|
13159
13690
|
}
|
|
13160
13691
|
var COMPACTION_INTERVAL = 15, KEEP_LAST_N = 6;
|
|
13161
|
-
var
|
|
13692
|
+
var init_manager2 = __esm(() => {
|
|
13162
13693
|
init_fact_extractor();
|
|
13163
13694
|
});
|
|
13164
13695
|
|
|
@@ -13828,7 +14359,7 @@ class PluginManager {
|
|
|
13828
14359
|
return chunk;
|
|
13829
14360
|
}
|
|
13830
14361
|
}
|
|
13831
|
-
var
|
|
14362
|
+
var init_manager3 = __esm(() => {
|
|
13832
14363
|
init_checker();
|
|
13833
14364
|
});
|
|
13834
14365
|
|
|
@@ -13848,9 +14379,9 @@ Workflow rules:
|
|
|
13848
14379
|
var init_subagent = __esm(() => {
|
|
13849
14380
|
init_i18n();
|
|
13850
14381
|
init_agent();
|
|
13851
|
-
init_manager();
|
|
13852
|
-
init_detector();
|
|
13853
14382
|
init_manager2();
|
|
14383
|
+
init_detector();
|
|
14384
|
+
init_manager3();
|
|
13854
14385
|
init_audit_log();
|
|
13855
14386
|
init_session_isolation();
|
|
13856
14387
|
init_store();
|
|
@@ -14958,8 +15489,8 @@ var init_pipeline_run = __esm(() => {
|
|
|
14958
15489
|
init_engine();
|
|
14959
15490
|
init_parser();
|
|
14960
15491
|
init_agent();
|
|
14961
|
-
init_manager();
|
|
14962
15492
|
init_manager2();
|
|
15493
|
+
init_manager3();
|
|
14963
15494
|
init_detector();
|
|
14964
15495
|
init_audit_log();
|
|
14965
15496
|
init_session_isolation();
|
|
@@ -20016,7 +20547,7 @@ class SessionManager {
|
|
|
20016
20547
|
}
|
|
20017
20548
|
}
|
|
20018
20549
|
}
|
|
20019
|
-
var
|
|
20550
|
+
var init_manager4 = __esm(() => {
|
|
20020
20551
|
init_i18n();
|
|
20021
20552
|
init_session_isolation();
|
|
20022
20553
|
});
|
|
@@ -20052,7 +20583,7 @@ var init_module2 = __esm(() => {
|
|
|
20052
20583
|
// src/modules/session/index.ts
|
|
20053
20584
|
var init_session2 = __esm(() => {
|
|
20054
20585
|
init_store3();
|
|
20055
|
-
|
|
20586
|
+
init_manager4();
|
|
20056
20587
|
init_module2();
|
|
20057
20588
|
});
|
|
20058
20589
|
|
|
@@ -22274,14 +22805,18 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
22274
22805
|
const profile = new UserProfile(join39(dir));
|
|
22275
22806
|
profile.load() || profile.collect();
|
|
22276
22807
|
profile.save();
|
|
22277
|
-
const
|
|
22278
|
-
model: config.model,
|
|
22279
|
-
baseUrl: config.provider.baseUrl,
|
|
22280
|
-
apiKey: config.provider.apiKey,
|
|
22808
|
+
const providerManager = new ProviderManager(config.provider, {
|
|
22281
22809
|
contextWindow: config.contextWindow,
|
|
22282
22810
|
retry: config.retry,
|
|
22283
22811
|
rateLimits: config.security?.rateLimits
|
|
22284
22812
|
});
|
|
22813
|
+
providerManager.setModel(config.model);
|
|
22814
|
+
let llmProvider = providerManager.active;
|
|
22815
|
+
if (config.provider.fallback && providerManager.listNames().length > 1) {
|
|
22816
|
+
llmProvider = new FallbackProvider(providerManager, (from, to, error) => {
|
|
22817
|
+
logger.warn(`provider failover: ${from} -> ${to} (${error.message.slice(0, 120)})`);
|
|
22818
|
+
});
|
|
22819
|
+
}
|
|
22285
22820
|
const baseDir = projectDir ? resolve23(projectDir) : process.cwd();
|
|
22286
22821
|
const envReport = collectEnvironment({
|
|
22287
22822
|
configDir: dir,
|
|
@@ -22572,15 +23107,15 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
22572
23107
|
var init_bootstrap = __esm(() => {
|
|
22573
23108
|
init_config2();
|
|
22574
23109
|
init_app_logger();
|
|
22575
|
-
|
|
23110
|
+
init_providers();
|
|
22576
23111
|
init_tools();
|
|
22577
23112
|
init_hidden_tools_block();
|
|
22578
23113
|
init_executor();
|
|
22579
|
-
|
|
23114
|
+
init_manager3();
|
|
22580
23115
|
init_loader();
|
|
22581
23116
|
init_lint_on_write();
|
|
22582
23117
|
init_notify();
|
|
22583
|
-
|
|
23118
|
+
init_manager2();
|
|
22584
23119
|
init_token_counter();
|
|
22585
23120
|
init_detector();
|
|
22586
23121
|
init_module();
|
|
@@ -23324,35 +23859,24 @@ async function runSetup(externalRl) {
|
|
|
23324
23859
|
console.log(l);
|
|
23325
23860
|
return answers;
|
|
23326
23861
|
}
|
|
23327
|
-
var PROVIDER_TYPES, KNOWN_PORTS
|
|
23862
|
+
var PROVIDER_TYPES, KNOWN_PORTS;
|
|
23328
23863
|
var init_setup = __esm(() => {
|
|
23329
23864
|
init_colors();
|
|
23330
23865
|
init_i18n();
|
|
23331
23866
|
init_spinner();
|
|
23867
|
+
init_presets();
|
|
23332
23868
|
init_box();
|
|
23333
23869
|
init_table();
|
|
23334
|
-
PROVIDER_TYPES =
|
|
23335
|
-
|
|
23336
|
-
|
|
23337
|
-
|
|
23338
|
-
},
|
|
23339
|
-
{ value: "openai", label: "OpenAI API" },
|
|
23340
|
-
{ value: "anthropic", label: "Anthropic Claude" },
|
|
23341
|
-
{ value: "opencode-zen", label: "OpenCode Zen (opencode.ai/zen)" },
|
|
23342
|
-
{ value: "opencode-go", label: "OpenCode Go (opencode.ai/go)" }
|
|
23343
|
-
];
|
|
23870
|
+
PROVIDER_TYPES = BUILTIN_PROVIDERS.map((spec) => ({
|
|
23871
|
+
value: spec.type,
|
|
23872
|
+
label: spec.label
|
|
23873
|
+
}));
|
|
23344
23874
|
KNOWN_PORTS = [
|
|
23345
23875
|
{ port: 1234, label: "LM Studio" },
|
|
23346
23876
|
{ port: 11434, label: "Ollama" },
|
|
23347
23877
|
{ port: 8080, label: "vLLM / custom" },
|
|
23348
23878
|
{ port: 4891, label: "GPT4All" }
|
|
23349
23879
|
];
|
|
23350
|
-
HOSTED_BASE_URLS = {
|
|
23351
|
-
openai: "https://api.openai.com/v1",
|
|
23352
|
-
anthropic: "https://api.anthropic.com",
|
|
23353
|
-
"opencode-zen": "https://opencode.ai/zen/v1",
|
|
23354
|
-
"opencode-go": "https://opencode.ai/zen/go/v1"
|
|
23355
|
-
};
|
|
23356
23880
|
});
|
|
23357
23881
|
|
|
23358
23882
|
// src/modules/certification/manifest.ts
|
|
@@ -31176,7 +31700,13 @@ function registerMmaCommands(ctx) {
|
|
|
31176
31700
|
const sys = Math.floor(ctxW * cfg.contextBudget.systemPrompt);
|
|
31177
31701
|
const res = Math.floor(ctxW * cfg.contextBudget.responseReserve);
|
|
31178
31702
|
console.log(`${t("repl.model")} ${cfg.model}`);
|
|
31179
|
-
|
|
31703
|
+
const providers = ctx.agent.listProviders();
|
|
31704
|
+
const active = providers.find((p) => p.active);
|
|
31705
|
+
if (active) {
|
|
31706
|
+
console.log(`${t("repl.provider")} ${active.label} (${pc2.dim(active.type)}) → ${pc2.dim(active.baseUrl)}`);
|
|
31707
|
+
} else {
|
|
31708
|
+
console.log(`${t("repl.provider")} ${cfg.provider.type} → ${cfg.provider.baseUrl}`);
|
|
31709
|
+
}
|
|
31180
31710
|
console.log(`${t("repl.context")} ${ctxW} (sys:${sys} res:${res} hist:${ctxW - sys - res})`);
|
|
31181
31711
|
console.log(`${t("repl.max_iters")} ${cfg.maxToolIterations}`);
|
|
31182
31712
|
console.log(`${t("repl.stuck_thresh")} ${cfg.stuckThreshold}`);
|
|
@@ -31204,8 +31734,14 @@ function registerMmaCommands(ctx) {
|
|
|
31204
31734
|
description: t("repl.status"),
|
|
31205
31735
|
usage: t("repl.status_usage"),
|
|
31206
31736
|
action: () => {
|
|
31737
|
+
const providers = ctx.agent.listProviders();
|
|
31738
|
+
const active = providers.find((p) => p.active);
|
|
31207
31739
|
console.log(`${t("repl.model")} ${ctx.config.model}`);
|
|
31208
|
-
|
|
31740
|
+
if (active) {
|
|
31741
|
+
console.log(`${t("repl.provider")} ${active.label} (${pc2.dim(active.type)}) @ ${pc2.dim(active.baseUrl)}`);
|
|
31742
|
+
} else {
|
|
31743
|
+
console.log(`${t("repl.provider")} ${ctx.config.provider.type} @ ${ctx.config.provider.baseUrl}`);
|
|
31744
|
+
}
|
|
31209
31745
|
const meta = ctx.sessionManager?.getActiveMeta();
|
|
31210
31746
|
if (meta) {
|
|
31211
31747
|
console.log(`${t("repl.session_label")} ${meta.name} (${meta.id}) — ${meta.messageCount} messages`);
|
|
@@ -31264,8 +31800,17 @@ Excluded blocks: ${info.excluded.length}`));
|
|
|
31264
31800
|
action: async (args) => {
|
|
31265
31801
|
const subcmd = args[0];
|
|
31266
31802
|
if (!subcmd || subcmd === "list") {
|
|
31267
|
-
|
|
31268
|
-
|
|
31803
|
+
const providers = ctx.agent.listProviders();
|
|
31804
|
+
if (providers.length === 0) {
|
|
31805
|
+
console.log(`${t("repl.provider_current")} ${ctx.config.provider.type}`);
|
|
31806
|
+
console.log(` ${ctx.config.provider.baseUrl}`);
|
|
31807
|
+
} else {
|
|
31808
|
+
console.log(t("repl.provider_current"));
|
|
31809
|
+
for (const p of providers) {
|
|
31810
|
+
const marker = p.active ? pc2.green("* ") : " ";
|
|
31811
|
+
console.log(` ${marker}${p.label} (${pc2.dim(p.type)}) ${pc2.dim(p.baseUrl)}`);
|
|
31812
|
+
}
|
|
31813
|
+
}
|
|
31269
31814
|
return;
|
|
31270
31815
|
}
|
|
31271
31816
|
if (subcmd === "use") {
|
|
@@ -31274,11 +31819,13 @@ Excluded blocks: ${info.excluded.length}`));
|
|
|
31274
31819
|
console.log(t("repl.provider_usage"));
|
|
31275
31820
|
return;
|
|
31276
31821
|
}
|
|
31277
|
-
|
|
31278
|
-
|
|
31279
|
-
|
|
31280
|
-
|
|
31281
|
-
|
|
31822
|
+
try {
|
|
31823
|
+
await ctx.agent.setProvider(name);
|
|
31824
|
+
ctx.refreshModelCache();
|
|
31825
|
+
console.log(pc2.green(t("repl.provider_set", { name })));
|
|
31826
|
+
} catch (e) {
|
|
31827
|
+
console.log(pc2.red(e.message));
|
|
31828
|
+
}
|
|
31282
31829
|
return;
|
|
31283
31830
|
}
|
|
31284
31831
|
console.log(t("repl.provider_usage"));
|
|
@@ -31306,6 +31853,7 @@ Excluded blocks: ${info.excluded.length}`));
|
|
|
31306
31853
|
const models = await provider.listModels();
|
|
31307
31854
|
s.stop();
|
|
31308
31855
|
if (models.length > 0) {
|
|
31856
|
+
ctx.refreshModelCache();
|
|
31309
31857
|
console.log(t("cli.available_models"));
|
|
31310
31858
|
const { getCertMark: getCertMark2 } = await Promise.resolve().then(() => (init_manifest(), exports_manifest));
|
|
31311
31859
|
for (const m of models) {
|
|
@@ -31777,6 +32325,7 @@ init_bootstrap();
|
|
|
31777
32325
|
init_config2();
|
|
31778
32326
|
init_setup();
|
|
31779
32327
|
init_i18n();
|
|
32328
|
+
init_colors();
|
|
31780
32329
|
import { join as join46 } from "path";
|
|
31781
32330
|
import { homedir as homedir16 } from "os";
|
|
31782
32331
|
|
|
@@ -32413,7 +32962,7 @@ function createPluginCommand(program2) {
|
|
|
32413
32962
|
}
|
|
32414
32963
|
|
|
32415
32964
|
// src/cli/commands.ts
|
|
32416
|
-
|
|
32965
|
+
init_presets();
|
|
32417
32966
|
init_version();
|
|
32418
32967
|
var version = readMmaVersion();
|
|
32419
32968
|
function createProgram() {
|
|
@@ -32496,8 +33045,8 @@ function createProgram() {
|
|
|
32496
33045
|
model.command("list").description(t("cli.list_models")).action(async () => {
|
|
32497
33046
|
const { config } = await bootstrap();
|
|
32498
33047
|
console.log(t("cli.current_model"), config.model);
|
|
32499
|
-
const {
|
|
32500
|
-
const provider2 =
|
|
33048
|
+
const { createProvider: createProvider2 } = await Promise.resolve().then(() => (init_create(), exports_create));
|
|
33049
|
+
const provider2 = createProvider2(config.provider.type, {
|
|
32501
33050
|
model: config.model,
|
|
32502
33051
|
baseUrl: config.provider.baseUrl,
|
|
32503
33052
|
apiKey: config.provider.apiKey,
|
|
@@ -32576,14 +33125,51 @@ function createProgram() {
|
|
|
32576
33125
|
console.log(t("cli.context_set", { size: contextWindow }));
|
|
32577
33126
|
});
|
|
32578
33127
|
const provider = program2.command("provider").description(t("cli.manage_providers"));
|
|
33128
|
+
provider.command("check").description(t("cli.provider_check")).option("--timeout <ms>", "Per-provider probe timeout in ms").action(async (opts) => {
|
|
33129
|
+
const { config, agent } = await bootstrap();
|
|
33130
|
+
const providers = agent.listProviders();
|
|
33131
|
+
const targets = targetsFromProviders(providers.length > 0 ? providers.map((p) => ({ name: p.label, provider: agent.getProviderFor(p.label) })).filter((x) => Boolean(x.provider)) : [
|
|
33132
|
+
{
|
|
33133
|
+
name: config.provider.type,
|
|
33134
|
+
provider: agent.getProviderFor(config.provider.active || config.provider.type)
|
|
33135
|
+
}
|
|
33136
|
+
].filter((x) => Boolean(x.provider)));
|
|
33137
|
+
const results = await probeProviders(targets, {
|
|
33138
|
+
timeoutMs: opts.timeout ? Number(opts.timeout) : 5000
|
|
33139
|
+
});
|
|
33140
|
+
for (const r of results) {
|
|
33141
|
+
const mark = r.ok ? pc2.green("✓") : pc2.red("✗");
|
|
33142
|
+
const detail = r.ok ? pc2.dim(`${r.ms}ms, ${r.models} models`) : pc2.red(r.error ?? "failed");
|
|
33143
|
+
console.log(` ${mark} ${r.name.padEnd(16)} ${detail}`);
|
|
33144
|
+
}
|
|
33145
|
+
});
|
|
32579
33146
|
provider.command("list").description(t("cli.list_providers")).action(async () => {
|
|
32580
|
-
const { config } = await bootstrap();
|
|
33147
|
+
const { config, agent } = await bootstrap();
|
|
33148
|
+
const providers = agent.listProviders();
|
|
33149
|
+
if (providers.length > 0) {
|
|
33150
|
+
console.log(t("cli.current_provider"));
|
|
33151
|
+
for (const p of providers) {
|
|
33152
|
+
const marker = p.active ? pc2.green("* ") : " ";
|
|
33153
|
+
const prio = p.priority !== undefined ? ` ${pc2.dim(`prio=${p.priority}`)}` : "";
|
|
33154
|
+
console.log(` ${marker}${p.label} (${pc2.dim(p.type)}) ${pc2.dim(p.baseUrl)}${prio}`);
|
|
33155
|
+
}
|
|
33156
|
+
return;
|
|
33157
|
+
}
|
|
32581
33158
|
console.log(t("cli.current_provider"), config.provider.type);
|
|
32582
33159
|
console.log(t("cli.base_url"), config.provider.baseUrl);
|
|
32583
33160
|
});
|
|
32584
33161
|
provider.command("use").argument("<name>", "Provider name").description(t("cli.set_provider")).action(async (name) => {
|
|
32585
33162
|
const configPath = join46(homedir16(), ".mma", "config.json");
|
|
32586
|
-
const { config } = await bootstrap();
|
|
33163
|
+
const { config, agent } = await bootstrap();
|
|
33164
|
+
if (config.provider.entries && config.provider.entries.length > 0) {
|
|
33165
|
+
try {
|
|
33166
|
+
await agent.setProvider(name);
|
|
33167
|
+
console.log(t("cli.provider_set", { name }));
|
|
33168
|
+
} catch (e) {
|
|
33169
|
+
console.log(pc2.red(e.message));
|
|
33170
|
+
}
|
|
33171
|
+
return;
|
|
33172
|
+
}
|
|
32587
33173
|
config.provider.type = name;
|
|
32588
33174
|
const baseUrl = HOSTED_BASE_URLS[name];
|
|
32589
33175
|
if (baseUrl) {
|
|
@@ -32595,6 +33181,43 @@ function createProgram() {
|
|
|
32595
33181
|
console.log(t("cli.provider_base_hint", { baseUrl }));
|
|
32596
33182
|
}
|
|
32597
33183
|
});
|
|
33184
|
+
provider.command("add").argument("<name>", "Provider type or label").option("--url <url>", "Base URL").option("--key <key>", "API key").option("--priority <n>", "Fallback priority (lower = tried first)").option("--context-window <n>", "Context window override for this entry").option("--rpm <n>", "Max requests per minute for this entry").option("--parallel <n>", "Max parallel tasks for this entry").description(t("cli.add_provider")).action(async (name, opts) => {
|
|
33185
|
+
const configPath = join46(homedir16(), ".mma", "config.json");
|
|
33186
|
+
const { config } = await bootstrap();
|
|
33187
|
+
const entries = Array.isArray(config.provider.entries) ? config.provider.entries : [];
|
|
33188
|
+
if (entries.length === 0) {
|
|
33189
|
+
entries.push({
|
|
33190
|
+
type: config.provider.type,
|
|
33191
|
+
label: config.provider.type,
|
|
33192
|
+
baseUrl: config.provider.baseUrl,
|
|
33193
|
+
apiKey: config.provider.apiKey
|
|
33194
|
+
});
|
|
33195
|
+
}
|
|
33196
|
+
const baseUrl = opts.url || HOSTED_BASE_URLS[name] || HOSTED_BASE_URLS[`opencode-${name}`] || "";
|
|
33197
|
+
if (!baseUrl) {
|
|
33198
|
+
console.log(pc2.red(t("cli.provider_no_url", { name })));
|
|
33199
|
+
return;
|
|
33200
|
+
}
|
|
33201
|
+
entries.push({
|
|
33202
|
+
type: name,
|
|
33203
|
+
label: name,
|
|
33204
|
+
baseUrl,
|
|
33205
|
+
apiKey: opts.key,
|
|
33206
|
+
...opts.priority !== undefined ? { priority: Number(opts.priority) } : {},
|
|
33207
|
+
...opts.contextWindow !== undefined ? { contextWindow: Number(opts.contextWindow) } : {},
|
|
33208
|
+
...opts.rpm !== undefined || opts.parallel !== undefined ? {
|
|
33209
|
+
rateLimits: {
|
|
33210
|
+
maxRequestsPerMinute: Number(opts.rpm ?? 0) || 60,
|
|
33211
|
+
maxParallelTasks: Number(opts.parallel ?? 0) || 1
|
|
33212
|
+
}
|
|
33213
|
+
} : {}
|
|
33214
|
+
});
|
|
33215
|
+
config.provider.entries = entries;
|
|
33216
|
+
config.provider.active = config.provider.active || config.provider.type;
|
|
33217
|
+
saveConfig(config, configPath);
|
|
33218
|
+
console.log(pc2.green(t("cli.provider_added", { name })));
|
|
33219
|
+
console.log(t("cli.provider_switch_hint"));
|
|
33220
|
+
});
|
|
32598
33221
|
const session2 = program2.command("session").description(t("cli.manage_sessions"));
|
|
32599
33222
|
session2.command("list").description(t("cli.list_sessions")).action(async () => {
|
|
32600
33223
|
const { sessionManager } = await bootstrap();
|
|
@@ -33431,6 +34054,34 @@ class SubcommandProvider {
|
|
|
33431
34054
|
return this.subcommands.filter((s) => s.startsWith(ctx.partial));
|
|
33432
34055
|
}
|
|
33433
34056
|
}
|
|
34057
|
+
|
|
34058
|
+
class ModelArgProvider {
|
|
34059
|
+
name = "model-args";
|
|
34060
|
+
getModels;
|
|
34061
|
+
constructor(getModels) {
|
|
34062
|
+
this.getModels = getModels;
|
|
34063
|
+
}
|
|
34064
|
+
match(ctx) {
|
|
34065
|
+
return ctx.tokenIndex === 2 && ctx.tokens[0] === "/model" && ctx.tokens[1] === "use";
|
|
34066
|
+
}
|
|
34067
|
+
complete(ctx) {
|
|
34068
|
+
return this.getModels().filter((m) => m.startsWith(ctx.partial));
|
|
34069
|
+
}
|
|
34070
|
+
}
|
|
34071
|
+
class ProviderArgProvider {
|
|
34072
|
+
name = "provider-args";
|
|
34073
|
+
providers;
|
|
34074
|
+
constructor(providers) {
|
|
34075
|
+
this.providers = providers;
|
|
34076
|
+
}
|
|
34077
|
+
match(ctx) {
|
|
34078
|
+
return ctx.tokenIndex === 2 && ctx.tokens[0] === "/provider" && ctx.tokens[1] === "use";
|
|
34079
|
+
}
|
|
34080
|
+
complete(ctx) {
|
|
34081
|
+
return this.providers.filter((p) => p.startsWith(ctx.partial));
|
|
34082
|
+
}
|
|
34083
|
+
}
|
|
34084
|
+
|
|
33434
34085
|
class SessionNameProvider {
|
|
33435
34086
|
name = "session-names";
|
|
33436
34087
|
getNames;
|
|
@@ -33697,6 +34348,9 @@ class FormattingStream {
|
|
|
33697
34348
|
return result;
|
|
33698
34349
|
}
|
|
33699
34350
|
}
|
|
34351
|
+
function formatWarning(text) {
|
|
34352
|
+
return pc2.yellow(pc2.bold(t("ui.warning_prefix")) + text);
|
|
34353
|
+
}
|
|
33700
34354
|
|
|
33701
34355
|
// src/ui/renderer.ts
|
|
33702
34356
|
init_spinner();
|
|
@@ -33843,8 +34497,9 @@ ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}
|
|
|
33843
34497
|
`);
|
|
33844
34498
|
}
|
|
33845
34499
|
}
|
|
33846
|
-
toolEnd(_tool, duration, error, ctxDelta, costUsd) {
|
|
34500
|
+
toolEnd(_tool, duration, error, ctxDelta, costUsd, provider, model) {
|
|
33847
34501
|
this.spinner.stop();
|
|
34502
|
+
const prov = provider && model ? `${pc2.dim(`${provider}·${model}`)}` : undefined;
|
|
33848
34503
|
if (!this.rich) {
|
|
33849
34504
|
const parts = [];
|
|
33850
34505
|
if (ctxDelta !== undefined && ctxDelta !== 0) {
|
|
@@ -33854,6 +34509,8 @@ ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}
|
|
|
33854
34509
|
if (costUsd !== undefined && costUsd > 0) {
|
|
33855
34510
|
parts.push(`${pc2.dim("cost")} ${pc2.yellow(formatUsd(costUsd))}`);
|
|
33856
34511
|
}
|
|
34512
|
+
if (prov)
|
|
34513
|
+
parts.push(prov);
|
|
33857
34514
|
if (parts.length > 0)
|
|
33858
34515
|
this.out.write(`${parts.join(" ")}
|
|
33859
34516
|
`);
|
|
@@ -33871,6 +34528,9 @@ ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}
|
|
|
33871
34528
|
if (costUsd !== undefined && costUsd > 0) {
|
|
33872
34529
|
footer += ` ${pc2.dim("cost")} ${pc2.yellow(formatUsd(costUsd))}`;
|
|
33873
34530
|
}
|
|
34531
|
+
if (prov) {
|
|
34532
|
+
footer += ` ${pc2.dim("via")} ${prov}`;
|
|
34533
|
+
}
|
|
33874
34534
|
if (this.toolStyle === "inline") {
|
|
33875
34535
|
this.out.write(`${GUTTER}${footer}
|
|
33876
34536
|
`);
|
|
@@ -33961,7 +34621,7 @@ async function probeLspServers(config, baseDir, deps = {}) {
|
|
|
33961
34621
|
const ok = [];
|
|
33962
34622
|
const failed = [];
|
|
33963
34623
|
const deadline = Date.now() + deadlineMs;
|
|
33964
|
-
const
|
|
34624
|
+
const probeOne2 = async (language, server) => {
|
|
33965
34625
|
const projectRoot = findProjectRoot(baseDir, baseDir, server.workspaceMarkers ?? []);
|
|
33966
34626
|
const serverTimeout = Math.min(server.timeout ?? timeoutCap, timeoutCap);
|
|
33967
34627
|
const started = Date.now();
|
|
@@ -33996,7 +34656,7 @@ async function probeLspServers(config, baseDir, deps = {}) {
|
|
|
33996
34656
|
break;
|
|
33997
34657
|
}
|
|
33998
34658
|
const wave = servers.slice(i, i + maxParallel);
|
|
33999
|
-
await Promise.all(wave.map(({ language, config: server }) =>
|
|
34659
|
+
await Promise.all(wave.map(({ language, config: server }) => probeOne2(language, server)));
|
|
34000
34660
|
}
|
|
34001
34661
|
return { ok, failed, noneApplicable: false };
|
|
34002
34662
|
}
|
|
@@ -34086,6 +34746,14 @@ function stepContextForTool(plan, tool, args) {
|
|
|
34086
34746
|
|
|
34087
34747
|
// src/cli/repl.ts
|
|
34088
34748
|
init_prices();
|
|
34749
|
+
|
|
34750
|
+
// src/ui/output.ts
|
|
34751
|
+
function writeWarning(text) {
|
|
34752
|
+
process.stdout.write(formatWarning(text) + `
|
|
34753
|
+
`);
|
|
34754
|
+
}
|
|
34755
|
+
|
|
34756
|
+
// src/cli/repl.ts
|
|
34089
34757
|
function formatContextBar(used, limit, compactions, quality) {
|
|
34090
34758
|
const pct = Math.min(100, Math.round(used / limit * 100));
|
|
34091
34759
|
const barLen = 20;
|
|
@@ -34105,6 +34773,7 @@ function formatContextBar(used, limit, compactions, quality) {
|
|
|
34105
34773
|
|
|
34106
34774
|
class Repl {
|
|
34107
34775
|
completer = new Completer;
|
|
34776
|
+
modelCache = [];
|
|
34108
34777
|
running = false;
|
|
34109
34778
|
agentRunning = false;
|
|
34110
34779
|
inputLocked = false;
|
|
@@ -34200,6 +34869,28 @@ class Repl {
|
|
|
34200
34869
|
this.completer.registerProvider(new SubcommandProvider("skill", ["list", "loaded", "load", "unload", "search"]));
|
|
34201
34870
|
this.completer.registerProvider(new SkillNameProvider(this.skillsModule));
|
|
34202
34871
|
}
|
|
34872
|
+
this.completer.registerProvider(new SubcommandProvider("provider", ["list", "use"]));
|
|
34873
|
+
this.completer.registerProvider(new SubcommandProvider("model", ["list", "use"]));
|
|
34874
|
+
const entryLabels = this.config.provider?.entries?.map((e) => e.label || e.type) ?? [];
|
|
34875
|
+
if (entryLabels.length > 0) {
|
|
34876
|
+
this.completer.registerProvider(new ProviderArgProvider(entryLabels));
|
|
34877
|
+
}
|
|
34878
|
+
this.completer.registerProvider(new ModelArgProvider(() => this.modelCache));
|
|
34879
|
+
this.refreshModelCache();
|
|
34880
|
+
}
|
|
34881
|
+
refreshModelCache() {
|
|
34882
|
+
(async () => {
|
|
34883
|
+
try {
|
|
34884
|
+
const name = this.config.provider.active || this.config.provider.entries?.[0]?.label || this.config.provider.type;
|
|
34885
|
+
const provider = this.agent.getProviderFor(name);
|
|
34886
|
+
if (!provider)
|
|
34887
|
+
return;
|
|
34888
|
+
const models = await provider.listModels();
|
|
34889
|
+
if (models.length > 0) {
|
|
34890
|
+
this.modelCache = models.sort();
|
|
34891
|
+
}
|
|
34892
|
+
} catch {}
|
|
34893
|
+
})();
|
|
34203
34894
|
}
|
|
34204
34895
|
setupListeners() {
|
|
34205
34896
|
let multiLineBuffer = "";
|
|
@@ -34383,7 +35074,7 @@ ${t("image.clipboard_empty")}`));
|
|
|
34383
35074
|
if (ev.type === "start") {
|
|
34384
35075
|
renderer.toolStart(ev.tool, ev.args, stepContextForTool(this.execModule?.getActivePlan() ?? null, ev.tool, ev.args), ev.icon);
|
|
34385
35076
|
} else {
|
|
34386
|
-
renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta, ev.costUsd);
|
|
35077
|
+
renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta, ev.costUsd, ev.provider, ev.model);
|
|
34387
35078
|
if (ev.tool === "plan" || ev.tool === "todo") {
|
|
34388
35079
|
this.renderPlan(renderer);
|
|
34389
35080
|
}
|
|
@@ -34582,6 +35273,18 @@ ${t("image.clipboard_empty")}`));
|
|
|
34582
35273
|
this.updateLspRow(headerWidth, lspSummary);
|
|
34583
35274
|
}).catch(() => {});
|
|
34584
35275
|
}
|
|
35276
|
+
if ((this.config.provider.entries?.length ?? 0) > 1) {
|
|
35277
|
+
probeProviders(targetsFromProviders((this.config.provider.entries ?? []).map((e) => ({
|
|
35278
|
+
name: e.label || e.type,
|
|
35279
|
+
provider: this.agent.getProviderFor(e.label || e.type)
|
|
35280
|
+
})).filter((x) => Boolean(x.provider))), { timeoutMs: 5000 }).then((results) => {
|
|
35281
|
+
for (const r of results) {
|
|
35282
|
+
if (!r.ok && r.name !== this.config.provider.active) {
|
|
35283
|
+
writeWarning(t("repl.provider_down", { name: r.name, error: r.error ?? "" }));
|
|
35284
|
+
}
|
|
35285
|
+
}
|
|
35286
|
+
}).catch(() => {});
|
|
35287
|
+
}
|
|
34585
35288
|
}
|
|
34586
35289
|
updateLspRow(headerWidth, summary) {
|
|
34587
35290
|
if (this.agentRunning || !process.stdout.isTTY)
|
|
@@ -34644,6 +35347,10 @@ function printRunResult(result, flush) {
|
|
|
34644
35347
|
if (result.success) {
|
|
34645
35348
|
if (result.totalCost !== undefined && result.totalCost > 0) {
|
|
34646
35349
|
console.log(pc2.dim(`${t("repl.cost", { cost: formatCost(result.totalCost) })}`));
|
|
35350
|
+
if (result.costBreakdown && result.costBreakdown.length > 1) {
|
|
35351
|
+
const parts = result.costBreakdown.map((e) => `${e.provider} ${formatCost(e.cost)}`);
|
|
35352
|
+
console.log(pc2.dim(` ${t("repl.cost_breakdown", { breakdown: parts.join(", ") })}`));
|
|
35353
|
+
}
|
|
34647
35354
|
} else if (result.totalTokens !== undefined && result.totalTokens > 0) {
|
|
34648
35355
|
console.log(pc2.dim(`${t("repl.tokens", { tokens: result.totalTokens })}`));
|
|
34649
35356
|
}
|
|
@@ -34858,7 +35565,8 @@ async function main() {
|
|
|
34858
35565
|
promptTokens: result2.promptTokens ?? null,
|
|
34859
35566
|
completionTokens: result2.completionTokens ?? null,
|
|
34860
35567
|
totalTokens: result2.totalTokens ?? null,
|
|
34861
|
-
totalCost: result2.totalCost ?? null
|
|
35568
|
+
totalCost: result2.totalCost ?? null,
|
|
35569
|
+
costBreakdown: result2.costBreakdown ?? []
|
|
34862
35570
|
}, null, 2));
|
|
34863
35571
|
process.stdout.write(`
|
|
34864
35572
|
`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "micro-models-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.50.0",
|
|
4
4
|
"description": "Micro Models Agent (MMA) — LLM agent harness for small models (Qwen3.5-9B, 32K-64K context)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -43,6 +43,6 @@
|
|
|
43
43
|
"@types/node": "^22.20.1",
|
|
44
44
|
"prettier": "^3.9.6",
|
|
45
45
|
"typescript": "^5.9.3",
|
|
46
|
-
"vitest": "^4.1.
|
|
46
|
+
"vitest": "^4.1.11"
|
|
47
47
|
}
|
|
48
48
|
}
|