micro-models-agent 0.49.0 → 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 +809 -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
|
|
|
@@ -22205,6 +22736,31 @@ function buildSystemInfo(config, baseDir, profileCompressed) {
|
|
|
22205
22736
|
lines.push(`Plan rule (MANDATORY): any task creating files, installing packages, or requiring multiple actions MUST create a plan with the "plan" tool BEFORE starting. Each step = a concrete deliverable.`);
|
|
22206
22737
|
}
|
|
22207
22738
|
lines.push(`Runtime verification: when a bug is described in terms of runtime behavior (what happens when the app runs), reproduce it — run the app / build / browser tool — and confirm the fix against actual runtime output before calling "plan update step=N status=done". Check existing servers first with process_list/process_log: a stale dev server started BEFORE your change still runs the old pipeline and keeps failing even after the files on disk are fixed. Restart or kill it (process_kill) and verify the exact endpoint/port the user reported — a fresh server on a new port does NOT prove the user's error is gone.`);
|
|
22739
|
+
const sec = config.security;
|
|
22740
|
+
if (sec?.enabled) {
|
|
22741
|
+
const secLines = [`SECURITY RESTRICTIONS (enforced — blocked calls return "[SECURITY BLOCKED]", do not retry them):`];
|
|
22742
|
+
if (sec.bash?.enabled && sec.bash.blacklist.length > 0) {
|
|
22743
|
+
const cmds = sec.bash.blacklist.slice(0, 15).join(", ");
|
|
22744
|
+
secLines.push(`Forbidden bash commands: ${cmds}${sec.bash.blacklist.length > 15 ? ", …" : ""}.`);
|
|
22745
|
+
if (sec.bash.whitelist.length > 0) {
|
|
22746
|
+
secLines.push(`Only whitelisted bash commands are allowed: ${sec.bash.whitelist.slice(0, 15).join(", ")}.`);
|
|
22747
|
+
}
|
|
22748
|
+
if (sec.bash.blockDangerousFlags) {
|
|
22749
|
+
secLines.push(`Pipes/redirects/chaining (| > >> ; &&) in bash are blocked — one plain command per call.`);
|
|
22750
|
+
}
|
|
22751
|
+
}
|
|
22752
|
+
if (sec.paths?.enabled && sec.paths.denied.length > 0) {
|
|
22753
|
+
const paths = sec.paths.denied.slice(0, 10).join(", ");
|
|
22754
|
+
secLines.push(`Denied paths (read/write): ${paths}${sec.paths.denied.length > 10 ? ", …" : ""}.`);
|
|
22755
|
+
}
|
|
22756
|
+
if (sec.network?.enabled && sec.network.deniedDomains.length > 0) {
|
|
22757
|
+
const domains = sec.network.deniedDomains.slice(0, 10).join(", ");
|
|
22758
|
+
secLines.push(`Denied network domains: ${domains}${sec.network.deniedDomains.length > 10 ? ", …" : ""}.`);
|
|
22759
|
+
}
|
|
22760
|
+
if (secLines.length > 1) {
|
|
22761
|
+
lines.push(...secLines);
|
|
22762
|
+
}
|
|
22763
|
+
}
|
|
22208
22764
|
const hasMCP = config.mcpServers && Object.values(config.mcpServers).some((s) => s.enabled !== false);
|
|
22209
22765
|
if (hasMCP) {
|
|
22210
22766
|
lines.push(`MCP servers are available — use the named MCP tools (prefixed mcp__) to query external services.`);
|
|
@@ -22249,14 +22805,18 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
22249
22805
|
const profile = new UserProfile(join39(dir));
|
|
22250
22806
|
profile.load() || profile.collect();
|
|
22251
22807
|
profile.save();
|
|
22252
|
-
const
|
|
22253
|
-
model: config.model,
|
|
22254
|
-
baseUrl: config.provider.baseUrl,
|
|
22255
|
-
apiKey: config.provider.apiKey,
|
|
22808
|
+
const providerManager = new ProviderManager(config.provider, {
|
|
22256
22809
|
contextWindow: config.contextWindow,
|
|
22257
22810
|
retry: config.retry,
|
|
22258
22811
|
rateLimits: config.security?.rateLimits
|
|
22259
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
|
+
}
|
|
22260
22820
|
const baseDir = projectDir ? resolve23(projectDir) : process.cwd();
|
|
22261
22821
|
const envReport = collectEnvironment({
|
|
22262
22822
|
configDir: dir,
|
|
@@ -22547,15 +23107,15 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
22547
23107
|
var init_bootstrap = __esm(() => {
|
|
22548
23108
|
init_config2();
|
|
22549
23109
|
init_app_logger();
|
|
22550
|
-
|
|
23110
|
+
init_providers();
|
|
22551
23111
|
init_tools();
|
|
22552
23112
|
init_hidden_tools_block();
|
|
22553
23113
|
init_executor();
|
|
22554
|
-
|
|
23114
|
+
init_manager3();
|
|
22555
23115
|
init_loader();
|
|
22556
23116
|
init_lint_on_write();
|
|
22557
23117
|
init_notify();
|
|
22558
|
-
|
|
23118
|
+
init_manager2();
|
|
22559
23119
|
init_token_counter();
|
|
22560
23120
|
init_detector();
|
|
22561
23121
|
init_module();
|
|
@@ -23299,35 +23859,24 @@ async function runSetup(externalRl) {
|
|
|
23299
23859
|
console.log(l);
|
|
23300
23860
|
return answers;
|
|
23301
23861
|
}
|
|
23302
|
-
var PROVIDER_TYPES, KNOWN_PORTS
|
|
23862
|
+
var PROVIDER_TYPES, KNOWN_PORTS;
|
|
23303
23863
|
var init_setup = __esm(() => {
|
|
23304
23864
|
init_colors();
|
|
23305
23865
|
init_i18n();
|
|
23306
23866
|
init_spinner();
|
|
23867
|
+
init_presets();
|
|
23307
23868
|
init_box();
|
|
23308
23869
|
init_table();
|
|
23309
|
-
PROVIDER_TYPES =
|
|
23310
|
-
|
|
23311
|
-
|
|
23312
|
-
|
|
23313
|
-
},
|
|
23314
|
-
{ value: "openai", label: "OpenAI API" },
|
|
23315
|
-
{ value: "anthropic", label: "Anthropic Claude" },
|
|
23316
|
-
{ value: "opencode-zen", label: "OpenCode Zen (opencode.ai/zen)" },
|
|
23317
|
-
{ value: "opencode-go", label: "OpenCode Go (opencode.ai/go)" }
|
|
23318
|
-
];
|
|
23870
|
+
PROVIDER_TYPES = BUILTIN_PROVIDERS.map((spec) => ({
|
|
23871
|
+
value: spec.type,
|
|
23872
|
+
label: spec.label
|
|
23873
|
+
}));
|
|
23319
23874
|
KNOWN_PORTS = [
|
|
23320
23875
|
{ port: 1234, label: "LM Studio" },
|
|
23321
23876
|
{ port: 11434, label: "Ollama" },
|
|
23322
23877
|
{ port: 8080, label: "vLLM / custom" },
|
|
23323
23878
|
{ port: 4891, label: "GPT4All" }
|
|
23324
23879
|
];
|
|
23325
|
-
HOSTED_BASE_URLS = {
|
|
23326
|
-
openai: "https://api.openai.com/v1",
|
|
23327
|
-
anthropic: "https://api.anthropic.com",
|
|
23328
|
-
"opencode-zen": "https://opencode.ai/zen/v1",
|
|
23329
|
-
"opencode-go": "https://opencode.ai/zen/go/v1"
|
|
23330
|
-
};
|
|
23331
23880
|
});
|
|
23332
23881
|
|
|
23333
23882
|
// src/modules/certification/manifest.ts
|
|
@@ -31151,7 +31700,13 @@ function registerMmaCommands(ctx) {
|
|
|
31151
31700
|
const sys = Math.floor(ctxW * cfg.contextBudget.systemPrompt);
|
|
31152
31701
|
const res = Math.floor(ctxW * cfg.contextBudget.responseReserve);
|
|
31153
31702
|
console.log(`${t("repl.model")} ${cfg.model}`);
|
|
31154
|
-
|
|
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
|
+
}
|
|
31155
31710
|
console.log(`${t("repl.context")} ${ctxW} (sys:${sys} res:${res} hist:${ctxW - sys - res})`);
|
|
31156
31711
|
console.log(`${t("repl.max_iters")} ${cfg.maxToolIterations}`);
|
|
31157
31712
|
console.log(`${t("repl.stuck_thresh")} ${cfg.stuckThreshold}`);
|
|
@@ -31179,8 +31734,14 @@ function registerMmaCommands(ctx) {
|
|
|
31179
31734
|
description: t("repl.status"),
|
|
31180
31735
|
usage: t("repl.status_usage"),
|
|
31181
31736
|
action: () => {
|
|
31737
|
+
const providers = ctx.agent.listProviders();
|
|
31738
|
+
const active = providers.find((p) => p.active);
|
|
31182
31739
|
console.log(`${t("repl.model")} ${ctx.config.model}`);
|
|
31183
|
-
|
|
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
|
+
}
|
|
31184
31745
|
const meta = ctx.sessionManager?.getActiveMeta();
|
|
31185
31746
|
if (meta) {
|
|
31186
31747
|
console.log(`${t("repl.session_label")} ${meta.name} (${meta.id}) — ${meta.messageCount} messages`);
|
|
@@ -31239,8 +31800,17 @@ Excluded blocks: ${info.excluded.length}`));
|
|
|
31239
31800
|
action: async (args) => {
|
|
31240
31801
|
const subcmd = args[0];
|
|
31241
31802
|
if (!subcmd || subcmd === "list") {
|
|
31242
|
-
|
|
31243
|
-
|
|
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
|
+
}
|
|
31244
31814
|
return;
|
|
31245
31815
|
}
|
|
31246
31816
|
if (subcmd === "use") {
|
|
@@ -31249,11 +31819,13 @@ Excluded blocks: ${info.excluded.length}`));
|
|
|
31249
31819
|
console.log(t("repl.provider_usage"));
|
|
31250
31820
|
return;
|
|
31251
31821
|
}
|
|
31252
|
-
|
|
31253
|
-
|
|
31254
|
-
|
|
31255
|
-
|
|
31256
|
-
|
|
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
|
+
}
|
|
31257
31829
|
return;
|
|
31258
31830
|
}
|
|
31259
31831
|
console.log(t("repl.provider_usage"));
|
|
@@ -31281,6 +31853,7 @@ Excluded blocks: ${info.excluded.length}`));
|
|
|
31281
31853
|
const models = await provider.listModels();
|
|
31282
31854
|
s.stop();
|
|
31283
31855
|
if (models.length > 0) {
|
|
31856
|
+
ctx.refreshModelCache();
|
|
31284
31857
|
console.log(t("cli.available_models"));
|
|
31285
31858
|
const { getCertMark: getCertMark2 } = await Promise.resolve().then(() => (init_manifest(), exports_manifest));
|
|
31286
31859
|
for (const m of models) {
|
|
@@ -31752,6 +32325,7 @@ init_bootstrap();
|
|
|
31752
32325
|
init_config2();
|
|
31753
32326
|
init_setup();
|
|
31754
32327
|
init_i18n();
|
|
32328
|
+
init_colors();
|
|
31755
32329
|
import { join as join46 } from "path";
|
|
31756
32330
|
import { homedir as homedir16 } from "os";
|
|
31757
32331
|
|
|
@@ -32388,7 +32962,7 @@ function createPluginCommand(program2) {
|
|
|
32388
32962
|
}
|
|
32389
32963
|
|
|
32390
32964
|
// src/cli/commands.ts
|
|
32391
|
-
|
|
32965
|
+
init_presets();
|
|
32392
32966
|
init_version();
|
|
32393
32967
|
var version = readMmaVersion();
|
|
32394
32968
|
function createProgram() {
|
|
@@ -32471,8 +33045,8 @@ function createProgram() {
|
|
|
32471
33045
|
model.command("list").description(t("cli.list_models")).action(async () => {
|
|
32472
33046
|
const { config } = await bootstrap();
|
|
32473
33047
|
console.log(t("cli.current_model"), config.model);
|
|
32474
|
-
const {
|
|
32475
|
-
const provider2 =
|
|
33048
|
+
const { createProvider: createProvider2 } = await Promise.resolve().then(() => (init_create(), exports_create));
|
|
33049
|
+
const provider2 = createProvider2(config.provider.type, {
|
|
32476
33050
|
model: config.model,
|
|
32477
33051
|
baseUrl: config.provider.baseUrl,
|
|
32478
33052
|
apiKey: config.provider.apiKey,
|
|
@@ -32551,14 +33125,51 @@ function createProgram() {
|
|
|
32551
33125
|
console.log(t("cli.context_set", { size: contextWindow }));
|
|
32552
33126
|
});
|
|
32553
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
|
+
});
|
|
32554
33146
|
provider.command("list").description(t("cli.list_providers")).action(async () => {
|
|
32555
|
-
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
|
+
}
|
|
32556
33158
|
console.log(t("cli.current_provider"), config.provider.type);
|
|
32557
33159
|
console.log(t("cli.base_url"), config.provider.baseUrl);
|
|
32558
33160
|
});
|
|
32559
33161
|
provider.command("use").argument("<name>", "Provider name").description(t("cli.set_provider")).action(async (name) => {
|
|
32560
33162
|
const configPath = join46(homedir16(), ".mma", "config.json");
|
|
32561
|
-
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
|
+
}
|
|
32562
33173
|
config.provider.type = name;
|
|
32563
33174
|
const baseUrl = HOSTED_BASE_URLS[name];
|
|
32564
33175
|
if (baseUrl) {
|
|
@@ -32570,6 +33181,43 @@ function createProgram() {
|
|
|
32570
33181
|
console.log(t("cli.provider_base_hint", { baseUrl }));
|
|
32571
33182
|
}
|
|
32572
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
|
+
});
|
|
32573
33221
|
const session2 = program2.command("session").description(t("cli.manage_sessions"));
|
|
32574
33222
|
session2.command("list").description(t("cli.list_sessions")).action(async () => {
|
|
32575
33223
|
const { sessionManager } = await bootstrap();
|
|
@@ -33406,6 +34054,34 @@ class SubcommandProvider {
|
|
|
33406
34054
|
return this.subcommands.filter((s) => s.startsWith(ctx.partial));
|
|
33407
34055
|
}
|
|
33408
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
|
+
|
|
33409
34085
|
class SessionNameProvider {
|
|
33410
34086
|
name = "session-names";
|
|
33411
34087
|
getNames;
|
|
@@ -33672,6 +34348,9 @@ class FormattingStream {
|
|
|
33672
34348
|
return result;
|
|
33673
34349
|
}
|
|
33674
34350
|
}
|
|
34351
|
+
function formatWarning(text) {
|
|
34352
|
+
return pc2.yellow(pc2.bold(t("ui.warning_prefix")) + text);
|
|
34353
|
+
}
|
|
33675
34354
|
|
|
33676
34355
|
// src/ui/renderer.ts
|
|
33677
34356
|
init_spinner();
|
|
@@ -33818,8 +34497,9 @@ ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}
|
|
|
33818
34497
|
`);
|
|
33819
34498
|
}
|
|
33820
34499
|
}
|
|
33821
|
-
toolEnd(_tool, duration, error, ctxDelta, costUsd) {
|
|
34500
|
+
toolEnd(_tool, duration, error, ctxDelta, costUsd, provider, model) {
|
|
33822
34501
|
this.spinner.stop();
|
|
34502
|
+
const prov = provider && model ? `${pc2.dim(`${provider}·${model}`)}` : undefined;
|
|
33823
34503
|
if (!this.rich) {
|
|
33824
34504
|
const parts = [];
|
|
33825
34505
|
if (ctxDelta !== undefined && ctxDelta !== 0) {
|
|
@@ -33829,6 +34509,8 @@ ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}
|
|
|
33829
34509
|
if (costUsd !== undefined && costUsd > 0) {
|
|
33830
34510
|
parts.push(`${pc2.dim("cost")} ${pc2.yellow(formatUsd(costUsd))}`);
|
|
33831
34511
|
}
|
|
34512
|
+
if (prov)
|
|
34513
|
+
parts.push(prov);
|
|
33832
34514
|
if (parts.length > 0)
|
|
33833
34515
|
this.out.write(`${parts.join(" ")}
|
|
33834
34516
|
`);
|
|
@@ -33846,6 +34528,9 @@ ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}
|
|
|
33846
34528
|
if (costUsd !== undefined && costUsd > 0) {
|
|
33847
34529
|
footer += ` ${pc2.dim("cost")} ${pc2.yellow(formatUsd(costUsd))}`;
|
|
33848
34530
|
}
|
|
34531
|
+
if (prov) {
|
|
34532
|
+
footer += ` ${pc2.dim("via")} ${prov}`;
|
|
34533
|
+
}
|
|
33849
34534
|
if (this.toolStyle === "inline") {
|
|
33850
34535
|
this.out.write(`${GUTTER}${footer}
|
|
33851
34536
|
`);
|
|
@@ -33936,7 +34621,7 @@ async function probeLspServers(config, baseDir, deps = {}) {
|
|
|
33936
34621
|
const ok = [];
|
|
33937
34622
|
const failed = [];
|
|
33938
34623
|
const deadline = Date.now() + deadlineMs;
|
|
33939
|
-
const
|
|
34624
|
+
const probeOne2 = async (language, server) => {
|
|
33940
34625
|
const projectRoot = findProjectRoot(baseDir, baseDir, server.workspaceMarkers ?? []);
|
|
33941
34626
|
const serverTimeout = Math.min(server.timeout ?? timeoutCap, timeoutCap);
|
|
33942
34627
|
const started = Date.now();
|
|
@@ -33971,7 +34656,7 @@ async function probeLspServers(config, baseDir, deps = {}) {
|
|
|
33971
34656
|
break;
|
|
33972
34657
|
}
|
|
33973
34658
|
const wave = servers.slice(i, i + maxParallel);
|
|
33974
|
-
await Promise.all(wave.map(({ language, config: server }) =>
|
|
34659
|
+
await Promise.all(wave.map(({ language, config: server }) => probeOne2(language, server)));
|
|
33975
34660
|
}
|
|
33976
34661
|
return { ok, failed, noneApplicable: false };
|
|
33977
34662
|
}
|
|
@@ -34061,6 +34746,14 @@ function stepContextForTool(plan, tool, args) {
|
|
|
34061
34746
|
|
|
34062
34747
|
// src/cli/repl.ts
|
|
34063
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
|
|
34064
34757
|
function formatContextBar(used, limit, compactions, quality) {
|
|
34065
34758
|
const pct = Math.min(100, Math.round(used / limit * 100));
|
|
34066
34759
|
const barLen = 20;
|
|
@@ -34080,6 +34773,7 @@ function formatContextBar(used, limit, compactions, quality) {
|
|
|
34080
34773
|
|
|
34081
34774
|
class Repl {
|
|
34082
34775
|
completer = new Completer;
|
|
34776
|
+
modelCache = [];
|
|
34083
34777
|
running = false;
|
|
34084
34778
|
agentRunning = false;
|
|
34085
34779
|
inputLocked = false;
|
|
@@ -34175,6 +34869,28 @@ class Repl {
|
|
|
34175
34869
|
this.completer.registerProvider(new SubcommandProvider("skill", ["list", "loaded", "load", "unload", "search"]));
|
|
34176
34870
|
this.completer.registerProvider(new SkillNameProvider(this.skillsModule));
|
|
34177
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
|
+
})();
|
|
34178
34894
|
}
|
|
34179
34895
|
setupListeners() {
|
|
34180
34896
|
let multiLineBuffer = "";
|
|
@@ -34358,7 +35074,7 @@ ${t("image.clipboard_empty")}`));
|
|
|
34358
35074
|
if (ev.type === "start") {
|
|
34359
35075
|
renderer.toolStart(ev.tool, ev.args, stepContextForTool(this.execModule?.getActivePlan() ?? null, ev.tool, ev.args), ev.icon);
|
|
34360
35076
|
} else {
|
|
34361
|
-
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);
|
|
34362
35078
|
if (ev.tool === "plan" || ev.tool === "todo") {
|
|
34363
35079
|
this.renderPlan(renderer);
|
|
34364
35080
|
}
|
|
@@ -34557,6 +35273,18 @@ ${t("image.clipboard_empty")}`));
|
|
|
34557
35273
|
this.updateLspRow(headerWidth, lspSummary);
|
|
34558
35274
|
}).catch(() => {});
|
|
34559
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
|
+
}
|
|
34560
35288
|
}
|
|
34561
35289
|
updateLspRow(headerWidth, summary) {
|
|
34562
35290
|
if (this.agentRunning || !process.stdout.isTTY)
|
|
@@ -34619,6 +35347,10 @@ function printRunResult(result, flush) {
|
|
|
34619
35347
|
if (result.success) {
|
|
34620
35348
|
if (result.totalCost !== undefined && result.totalCost > 0) {
|
|
34621
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
|
+
}
|
|
34622
35354
|
} else if (result.totalTokens !== undefined && result.totalTokens > 0) {
|
|
34623
35355
|
console.log(pc2.dim(`${t("repl.tokens", { tokens: result.totalTokens })}`));
|
|
34624
35356
|
}
|
|
@@ -34833,7 +35565,8 @@ async function main() {
|
|
|
34833
35565
|
promptTokens: result2.promptTokens ?? null,
|
|
34834
35566
|
completionTokens: result2.completionTokens ?? null,
|
|
34835
35567
|
totalTokens: result2.totalTokens ?? null,
|
|
34836
|
-
totalCost: result2.totalCost ?? null
|
|
35568
|
+
totalCost: result2.totalCost ?? null,
|
|
35569
|
+
costBreakdown: result2.costBreakdown ?? []
|
|
34837
35570
|
}, null, 2));
|
|
34838
35571
|
process.stdout.write(`
|
|
34839
35572
|
`);
|