micro-models-agent 0.49.1 → 0.50.1
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 +916 -116
- package/package.json +2 -2
package/dist/main.js
CHANGED
|
@@ -1991,7 +1991,6 @@ var init_security = __esm(() => {
|
|
|
1991
1991
|
"python",
|
|
1992
1992
|
"python3",
|
|
1993
1993
|
"node",
|
|
1994
|
-
"bun",
|
|
1995
1994
|
"perl",
|
|
1996
1995
|
"ruby",
|
|
1997
1996
|
"del",
|
|
@@ -2013,8 +2012,6 @@ var init_security = __esm(() => {
|
|
|
2013
2012
|
"--delete",
|
|
2014
2013
|
"--remove",
|
|
2015
2014
|
"--dangerous",
|
|
2016
|
-
"--yes",
|
|
2017
|
-
"-y",
|
|
2018
2015
|
"--no-confirm"
|
|
2019
2016
|
],
|
|
2020
2017
|
dangerousOperators: [">", ">>", "2>", "2>>", ";", "&&", "||", "|", "`", "$("]
|
|
@@ -2539,6 +2536,8 @@ Command: {command}`,
|
|
|
2539
2536
|
{lines}`,
|
|
2540
2537
|
"proc.none": "No background processes running.",
|
|
2541
2538
|
"proc.not_found": "Process not found: {id}",
|
|
2539
|
+
"proc.missing_id": "Provide the process id: process_kill id=proc_xxx (the id is returned by bash when a command goes to the background, or by process_list).",
|
|
2540
|
+
"proc.missing_id_list": "No background processes are currently running.",
|
|
2542
2541
|
"proc.killed": "Process {id} (PID {pid}) killed.",
|
|
2543
2542
|
"proc.kill_failed": "Failed to kill process {id}",
|
|
2544
2543
|
"proc.list_header": "Background processes",
|
|
@@ -2595,6 +2594,8 @@ Fix the error and re-edit the file (a clean write clears the failure), or mark t
|
|
|
2595
2594
|
"plan.deleted": "Plan {id} deleted permanently.",
|
|
2596
2595
|
"plan.purged": "Deleted {count} plan(s). Plan storage is empty.",
|
|
2597
2596
|
"plan.no_deliverables": "note: step {step} names no files — its completion cannot be auto-verified; run an explicit check (e.g. build/tests) before your final answer.",
|
|
2597
|
+
"plan.bulk_update_rejected": 'Bulk steps[] rewrite via action=update is not supported: it would discard all step statuses. Use "re-plan" (keeps completed steps, replaces the rest) or mark steps individually: plan update step=N status=done|failed|skipped.',
|
|
2598
|
+
"plan.readonly": 'Plan {id} is {status} (read-only) — it cannot be updated. Use "plan switch" to activate it, or create a new plan for new work.',
|
|
2598
2599
|
"plan.list_legend": "[*] active · [ ] draft · [-] archived",
|
|
2599
2600
|
"exec.plan_nudge": "You made {count} file-changing tool call(s) without a plan. For tasks that create or modify files, or span multiple steps, create a plan first (plan create) with concrete steps (exact filenames, commands, deliverables), then continue.",
|
|
2600
2601
|
"todo.added": "Added {count} todo(s): {items}",
|
|
@@ -2676,6 +2677,8 @@ Fix the error and re-edit the file (a clean write clears the failure), or mark t
|
|
|
2676
2677
|
"cli.cert_fixture_missing": "Fixture missing for {id}: {path}",
|
|
2677
2678
|
"cli.cert_marks_hint": "Markers: ✔ certified on this provider, ○ certified on an older MMA version, · not certified",
|
|
2678
2679
|
"cli.manage_providers": "Manage providers",
|
|
2680
|
+
"cli.provider_check": "Probe configured providers (free listModels call)",
|
|
2681
|
+
"repl.provider_down": '[WARN] Provider "{name}" is unreachable: {error}',
|
|
2679
2682
|
"cli.list_providers": "List providers",
|
|
2680
2683
|
"cli.current_provider": "Current provider:",
|
|
2681
2684
|
"cli.base_url": "Base URL:",
|
|
@@ -2829,14 +2832,14 @@ Available commands:`,
|
|
|
2829
2832
|
"repl.wizard": "Run setup wizard",
|
|
2830
2833
|
"repl.wizard_usage": "Usage: /wizard",
|
|
2831
2834
|
"repl.wizard_running": "Running setup wizard... (current config will be overwritten)",
|
|
2832
|
-
"repl.provider_list": "
|
|
2835
|
+
"repl.provider_list": "List configured providers or hot-swap the active one",
|
|
2833
2836
|
"repl.provider_current": "Current provider",
|
|
2834
2837
|
"repl.provider_set": "Provider set to: {name}",
|
|
2835
|
-
"repl.provider_usage": "
|
|
2836
|
-
"repl.model_list": "
|
|
2838
|
+
"repl.provider_usage": "/provider list - show entries (* = active, prio = fallback order) | /provider use <name> - switch without restart",
|
|
2839
|
+
"repl.model_list": "List models of the active provider or switch the model",
|
|
2837
2840
|
"repl.model_current": "Current model",
|
|
2838
2841
|
"repl.model_set": "Model set to: {name}",
|
|
2839
|
-
"repl.model_usage": "
|
|
2842
|
+
"repl.model_usage": "/model list - fetch models from the active provider | /model use <name> - switch model",
|
|
2840
2843
|
"repl.group.general": "General",
|
|
2841
2844
|
"repl.group.agent": "Agent",
|
|
2842
2845
|
"repl.group.session": "Sessions",
|
|
@@ -2939,6 +2942,7 @@ Apply a matching solution from these results. If none is relevant — do NOT rep
|
|
|
2939
2942
|
"exec.error_search_failed": 'Web search returned nothing for "{query}".',
|
|
2940
2943
|
"exec.error_search_no_query": "Error output is not meaningful — skipping the web search.",
|
|
2941
2944
|
"exec.npm_exec_hint": '"could not determine executable to run" — no "bin" for that package/script. Use "npm run <script>" (script must exist in package.json) or "bunx <pkg>" for a package that declares a bin.',
|
|
2945
|
+
"exec.hidden_tool_hint": '"{tool}" is not a shell command — it is an MMA tool that is currently hidden. Call the enable_tools tool with tags ["shell"] to unlock it; it becomes available on the next iteration.',
|
|
2942
2946
|
"exec.task_reminder": "Task: {task}. Continue making progress — do not repeat failed actions.",
|
|
2943
2947
|
"hall.max_retries_exhausted": "Model returned empty or insufficient responses after multiple retries",
|
|
2944
2948
|
"hall.short_response": "Response too short or empty",
|
|
@@ -3047,7 +3051,12 @@ Apply a matching solution from these results. If none is relevant — do NOT rep
|
|
|
3047
3051
|
"tools.hidden_header": "Additional tools (enable on demand via enable_tools or route to subagent tool_tags):",
|
|
3048
3052
|
"tool.friendly.enable_tools": "Enable tools",
|
|
3049
3053
|
"cli.provider_base_hint": "Base URL set to: {baseUrl}",
|
|
3054
|
+
"cli.add_provider": "Add a provider to the config",
|
|
3055
|
+
"cli.provider_no_url": 'No base URL for provider "{name}". Pass --url.',
|
|
3056
|
+
"cli.provider_added": "Provider added: {name}",
|
|
3057
|
+
"cli.provider_switch_hint": "Switch to it with: mma provider use <name>",
|
|
3050
3058
|
"repl.cost": "Total cost: {cost}",
|
|
3059
|
+
"repl.cost_breakdown": "By provider: {breakdown}",
|
|
3051
3060
|
"repl.tokens": "Tokens used: {tokens}",
|
|
3052
3061
|
"repl.ctrl_c_interrupt": `
|
|
3053
3062
|
[Ctrl+C] Stopping agent... (press again to force)`,
|
|
@@ -3241,6 +3250,8 @@ var init_ru = __esm(() => {
|
|
|
3241
3250
|
{lines}`,
|
|
3242
3251
|
"proc.none": "Фоновых процессов нет.",
|
|
3243
3252
|
"proc.not_found": "Процесс не найден: {id}",
|
|
3253
|
+
"proc.missing_id": "Укажите id процесса: process_kill id=proc_xxx (id возвращается bash при уходе команды в фон или выводится через process_list).",
|
|
3254
|
+
"proc.missing_id_list": "Сейчас фоновых процессов нет.",
|
|
3244
3255
|
"proc.killed": "Процесс {id} (PID {pid}) остановлен.",
|
|
3245
3256
|
"proc.kill_failed": "Не удалось остановить процесс {id}",
|
|
3246
3257
|
"proc.list_header": "Фоновые процессы",
|
|
@@ -3297,6 +3308,8 @@ var init_ru = __esm(() => {
|
|
|
3297
3308
|
"plan.deleted": "План {id} удалён окончательно.",
|
|
3298
3309
|
"plan.purged": "Удалено планов: {count}. Хранилище планов пусто.",
|
|
3299
3310
|
"plan.no_deliverables": "примечание: шаг {step} не называет файлов — его выполнение нельзя проверить автоматически; перед финальным ответом явно проверь результат (например, запусти сборку/тесты).",
|
|
3311
|
+
"plan.bulk_update_rejected": 'Массовая перезапись steps[] через action=update не поддерживается: она бы стёрла статусы всех шагов. Используй "re-plan" (сохраняет завершённые шаги, заменяет остальные) или отмечай шаги по одному: plan update step=N status=done|failed|skipped.',
|
|
3312
|
+
"plan.readonly": 'План {id} имеет статус {status} (только чтение) — обновить его нельзя. Используй "plan switch", чтобы активировать его, или создай новый план для новой задачи.',
|
|
3300
3313
|
"plan.list_legend": "[*] активный · [ ] черновик · [-] архив",
|
|
3301
3314
|
"exec.plan_nudge": "Ты сделал(а) {count} вызов(ов), изменяющих файлы, без плана. Для задач, создающих/изменяющих файлы или состоящих из нескольких шагов, сначала создай план (plan create) с конкретными шагами (точные имена файлов, команды, результаты), а потом продолжай.",
|
|
3302
3315
|
"todo.added": "Добавлено {count} задач: {items}",
|
|
@@ -3374,6 +3387,8 @@ var init_ru = __esm(() => {
|
|
|
3374
3387
|
"cli.cert_fixture_missing": "Фикстура отсутствует для {id}: {path}",
|
|
3375
3388
|
"cli.cert_marks_hint": "Маркеры: ✔ сертифицировано на этом провайдере, ○ сертифицировано на старой версии MMA, · не сертифицировано",
|
|
3376
3389
|
"cli.manage_providers": "Управление провайдерами",
|
|
3390
|
+
"cli.provider_check": "Проверить доступность провайдеров (бесплатный вызов listModels)",
|
|
3391
|
+
"repl.provider_down": "[ВНИМАНИЕ] Провайдер «{name}» недоступен: {error}",
|
|
3377
3392
|
"cli.list_providers": "Список провайдеров",
|
|
3378
3393
|
"cli.current_provider": "Текущий провайдер:",
|
|
3379
3394
|
"cli.base_url": "Базовый URL:",
|
|
@@ -3527,14 +3542,14 @@ var init_ru = __esm(() => {
|
|
|
3527
3542
|
"repl.wizard": "Запустить мастер настройки",
|
|
3528
3543
|
"repl.wizard_usage": "Использование: /wizard",
|
|
3529
3544
|
"repl.wizard_running": "Запуск мастера настройки... (текущая конфигурация будет перезаписана)",
|
|
3530
|
-
"repl.provider_list": "
|
|
3545
|
+
"repl.provider_list": "Список настроенных провайдеров или горячая смена активного",
|
|
3531
3546
|
"repl.provider_current": "Текущий провайдер",
|
|
3532
3547
|
"repl.provider_set": "Провайдер установлен: {name}",
|
|
3533
|
-
"repl.provider_usage": "
|
|
3534
|
-
"repl.model_list": "
|
|
3548
|
+
"repl.provider_usage": "/provider list — записи (* = активный, prio = порядок fallback) | /provider use <имя> — смена без перезапуска",
|
|
3549
|
+
"repl.model_list": "Список моделей активного провайдера или смена модели",
|
|
3535
3550
|
"repl.model_current": "Текущая модель",
|
|
3536
3551
|
"repl.model_set": "Модель установлена: {name}",
|
|
3537
|
-
"repl.model_usage": "
|
|
3552
|
+
"repl.model_usage": "/model list — получить модели активного провайдера | /model use <имя> — сменить модель",
|
|
3538
3553
|
"repl.reload": "Перезагрузить агента с текущим конфигом",
|
|
3539
3554
|
"repl.reload_usage": "Использование: /reload",
|
|
3540
3555
|
"repl.reloading": "Перезагрузка агента...",
|
|
@@ -3641,6 +3656,7 @@ var init_ru = __esm(() => {
|
|
|
3641
3656
|
"exec.error_search_failed": 'Поиск в интернете для "{query}" ничего не дал.',
|
|
3642
3657
|
"exec.error_search_no_query": "Текст ошибки незначимый — поиск в интернете пропущен.",
|
|
3643
3658
|
"exec.npm_exec_hint": '"could not determine executable to run" — у пакета/скрипта нет "bin". Используй "npm run <script>" (скрипт должен быть в package.json) или "bunx <pkg>" для пакета с объявленным bin.',
|
|
3659
|
+
"exec.hidden_tool_hint": '"{tool}" — не команда оболочки, это инструмент MMA, который сейчас скрыт. Вызови тул enable_tools с tags ["shell"], чтобы включить его; он станет доступен на следующей итерации.',
|
|
3644
3660
|
"exec.task_reminder": "Задача: {task}. Продолжай работу — не повторяй неудачные действия.",
|
|
3645
3661
|
"hall.max_retries_exhausted": "Модель вернула пустой или недостаточный ответ после нескольких попыток",
|
|
3646
3662
|
"hall.short_response": "Слишком короткий или пустой ответ",
|
|
@@ -3752,7 +3768,12 @@ var init_ru = __esm(() => {
|
|
|
3752
3768
|
"tools.hidden_header": "Дополнительные тулы (включите по требованию через enable_tools или маршрутизируйте через subagent tool_tags):",
|
|
3753
3769
|
"tool.friendly.enable_tools": "Включить тулы",
|
|
3754
3770
|
"cli.provider_base_hint": "Базовый URL установлен: {baseUrl}",
|
|
3771
|
+
"cli.add_provider": "Добавить провайдера в конфиг",
|
|
3772
|
+
"cli.provider_no_url": 'Нет базового URL для провайдера "{name}". Укажите --url.',
|
|
3773
|
+
"cli.provider_added": "Провайдер добавлен: {name}",
|
|
3774
|
+
"cli.provider_switch_hint": "Переключитесь на него: mma provider use <имя>",
|
|
3755
3775
|
"repl.cost": "Итого потрачено: {cost}",
|
|
3776
|
+
"repl.cost_breakdown": "По провайдерам: {breakdown}",
|
|
3756
3777
|
"repl.tokens": "Потрачено токенов: {tokens}",
|
|
3757
3778
|
"repl.ctrl_c_interrupt": `
|
|
3758
3779
|
[Ctrl+C] Остановка агента... (ещё раз — принудительно)`,
|
|
@@ -4186,22 +4207,6 @@ function normalizeLspServerArgs(config) {
|
|
|
4186
4207
|
}
|
|
4187
4208
|
}
|
|
4188
4209
|
}
|
|
4189
|
-
function normalizeStaleSecurityConfig(config) {
|
|
4190
|
-
const sec = config.security;
|
|
4191
|
-
if (!sec || sec.enabled !== false)
|
|
4192
|
-
return;
|
|
4193
|
-
const bash = sec.bash;
|
|
4194
|
-
if (!bash || bash.enabled !== false || bash.blockDangerousFlags !== false)
|
|
4195
|
-
return;
|
|
4196
|
-
const LEGACY_OPERATORS = [">", ">>", "2>", "2>>", "`"];
|
|
4197
|
-
if (JSON.stringify(bash.dangerousOperators ?? []) !== JSON.stringify(LEGACY_OPERATORS))
|
|
4198
|
-
return;
|
|
4199
|
-
const defaultCmds = new Set(DEFAULT_SECURITY_CONFIG.bash.blacklist);
|
|
4200
|
-
const blacklist = Array.isArray(bash.blacklist) ? bash.blacklist : [];
|
|
4201
|
-
if (blacklist.some((cmd) => !defaultCmds.has(cmd)))
|
|
4202
|
-
return;
|
|
4203
|
-
config.security = JSON.parse(JSON.stringify(DEFAULT_SECURITY_CONFIG));
|
|
4204
|
-
}
|
|
4205
4210
|
function loadJSON(path) {
|
|
4206
4211
|
try {
|
|
4207
4212
|
if (existsSync4(path)) {
|
|
@@ -4275,7 +4280,6 @@ function loadConfig(options) {
|
|
|
4275
4280
|
config.security.contentScan.dangerousPatterns = restoreDangerousPatterns(config.security.contentScan.dangerousPatterns, DEFAULT_SECURITY_CONFIG.contentScan.dangerousPatterns);
|
|
4276
4281
|
}
|
|
4277
4282
|
normalizeLspServerArgs(config);
|
|
4278
|
-
normalizeStaleSecurityConfig(config);
|
|
4279
4283
|
config = applyEnvVars(config);
|
|
4280
4284
|
try {
|
|
4281
4285
|
const encryptor = new ConfigEncryptor;
|
|
@@ -4757,6 +4761,26 @@ var init_app_logger = __esm(() => {
|
|
|
4757
4761
|
};
|
|
4758
4762
|
});
|
|
4759
4763
|
|
|
4764
|
+
// src/modules/providers/registry.ts
|
|
4765
|
+
class ProviderRegistry {
|
|
4766
|
+
specs = new Map;
|
|
4767
|
+
register(spec) {
|
|
4768
|
+
this.specs.set(spec.type, spec);
|
|
4769
|
+
}
|
|
4770
|
+
get(type) {
|
|
4771
|
+
return this.specs.get(type);
|
|
4772
|
+
}
|
|
4773
|
+
has(type) {
|
|
4774
|
+
return this.specs.has(type);
|
|
4775
|
+
}
|
|
4776
|
+
list() {
|
|
4777
|
+
return [...this.specs.values()];
|
|
4778
|
+
}
|
|
4779
|
+
types() {
|
|
4780
|
+
return [...this.specs.keys()];
|
|
4781
|
+
}
|
|
4782
|
+
}
|
|
4783
|
+
|
|
4760
4784
|
// node_modules/base64-js/index.js
|
|
4761
4785
|
var require_base64_js = __commonJS((exports) => {
|
|
4762
4786
|
exports.byteLength = byteLength;
|
|
@@ -5646,13 +5670,13 @@ class OpenAICompatProvider {
|
|
|
5646
5670
|
headers
|
|
5647
5671
|
});
|
|
5648
5672
|
if (!response.ok) {
|
|
5649
|
-
|
|
5673
|
+
throw new Error(`HTTP ${response.status} from ${url}`);
|
|
5650
5674
|
}
|
|
5651
5675
|
const data = await response.json();
|
|
5652
5676
|
const models = (data.data || data || []).map((m) => m.id || m.name || m.model || "").filter(Boolean);
|
|
5653
5677
|
return models;
|
|
5654
|
-
} catch {
|
|
5655
|
-
|
|
5678
|
+
} catch (err) {
|
|
5679
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
5656
5680
|
}
|
|
5657
5681
|
}
|
|
5658
5682
|
async fetchWithRetry(url, init) {
|
|
@@ -5681,12 +5705,16 @@ class OpenAICompatProvider {
|
|
|
5681
5705
|
}
|
|
5682
5706
|
}
|
|
5683
5707
|
if (lastStatus === 429) {
|
|
5684
|
-
|
|
5708
|
+
const e429 = new Error(t("error.llm_429", {
|
|
5685
5709
|
model: this.model,
|
|
5686
5710
|
baseUrl: this.config.baseUrl
|
|
5687
5711
|
}));
|
|
5712
|
+
e429.llmStatus = lastStatus;
|
|
5713
|
+
throw e429;
|
|
5688
5714
|
}
|
|
5689
|
-
|
|
5715
|
+
const eRetry = new Error(t("error.llm_retries"));
|
|
5716
|
+
eRetry.llmStatus = lastStatus;
|
|
5717
|
+
throw eRetry;
|
|
5690
5718
|
}
|
|
5691
5719
|
isRetryable(status) {
|
|
5692
5720
|
return status === 429 || status >= 500;
|
|
@@ -5717,6 +5745,399 @@ var init_openai_compat = __esm(() => {
|
|
|
5717
5745
|
init_rate_limiter();
|
|
5718
5746
|
});
|
|
5719
5747
|
|
|
5748
|
+
// src/modules/providers/presets.ts
|
|
5749
|
+
function openaiCompat(opts) {
|
|
5750
|
+
const retry = opts.retry ?? {
|
|
5751
|
+
maxRetries: 3,
|
|
5752
|
+
baseDelay: 1000,
|
|
5753
|
+
maxDelay: 30000
|
|
5754
|
+
};
|
|
5755
|
+
const rateLimits = opts.rateLimits;
|
|
5756
|
+
return new OpenAICompatProvider({
|
|
5757
|
+
model: opts.model,
|
|
5758
|
+
baseUrl: opts.baseUrl,
|
|
5759
|
+
apiKey: opts.apiKey,
|
|
5760
|
+
contextWindow: opts.contextWindow,
|
|
5761
|
+
retry,
|
|
5762
|
+
rateLimits
|
|
5763
|
+
});
|
|
5764
|
+
}
|
|
5765
|
+
var OPENAI_COMPAT, OPENROUTER, OPENAI, ANTHROPIC, OPENCODE_ZEN, OPENCODE_GO, BUILTIN_PROVIDERS, HOSTED_BASE_URLS;
|
|
5766
|
+
var init_presets = __esm(() => {
|
|
5767
|
+
init_openai_compat();
|
|
5768
|
+
OPENAI_COMPAT = {
|
|
5769
|
+
type: "openai-compat",
|
|
5770
|
+
label: "OpenAI-compatible (LM Studio, Ollama, vLLM, etc.)",
|
|
5771
|
+
baseUrl: "",
|
|
5772
|
+
capabilities: {
|
|
5773
|
+
streaming: true,
|
|
5774
|
+
tools: true,
|
|
5775
|
+
vision: false,
|
|
5776
|
+
reasoningEffort: false,
|
|
5777
|
+
listModels: true,
|
|
5778
|
+
requiresKey: false,
|
|
5779
|
+
auth: "bearer"
|
|
5780
|
+
},
|
|
5781
|
+
create: openaiCompat
|
|
5782
|
+
};
|
|
5783
|
+
OPENROUTER = {
|
|
5784
|
+
type: "openrouter",
|
|
5785
|
+
label: "OpenRouter (openrouter.ai)",
|
|
5786
|
+
baseUrl: "https://openrouter.ai/api/v1",
|
|
5787
|
+
capabilities: {
|
|
5788
|
+
streaming: true,
|
|
5789
|
+
tools: true,
|
|
5790
|
+
vision: true,
|
|
5791
|
+
reasoningEffort: false,
|
|
5792
|
+
listModels: true,
|
|
5793
|
+
requiresKey: true,
|
|
5794
|
+
auth: "bearer"
|
|
5795
|
+
},
|
|
5796
|
+
create: openaiCompat
|
|
5797
|
+
};
|
|
5798
|
+
OPENAI = {
|
|
5799
|
+
type: "openai",
|
|
5800
|
+
label: "OpenAI API",
|
|
5801
|
+
baseUrl: "https://api.openai.com/v1",
|
|
5802
|
+
capabilities: {
|
|
5803
|
+
streaming: true,
|
|
5804
|
+
tools: true,
|
|
5805
|
+
vision: true,
|
|
5806
|
+
reasoningEffort: true,
|
|
5807
|
+
listModels: true,
|
|
5808
|
+
requiresKey: true,
|
|
5809
|
+
auth: "bearer"
|
|
5810
|
+
},
|
|
5811
|
+
create: openaiCompat
|
|
5812
|
+
};
|
|
5813
|
+
ANTHROPIC = {
|
|
5814
|
+
type: "anthropic",
|
|
5815
|
+
label: "Anthropic Claude",
|
|
5816
|
+
baseUrl: "https://api.anthropic.com",
|
|
5817
|
+
capabilities: {
|
|
5818
|
+
streaming: false,
|
|
5819
|
+
tools: false,
|
|
5820
|
+
vision: true,
|
|
5821
|
+
reasoningEffort: false,
|
|
5822
|
+
listModels: false,
|
|
5823
|
+
requiresKey: true,
|
|
5824
|
+
auth: "header"
|
|
5825
|
+
},
|
|
5826
|
+
create: openaiCompat
|
|
5827
|
+
};
|
|
5828
|
+
OPENCODE_ZEN = {
|
|
5829
|
+
type: "opencode-zen",
|
|
5830
|
+
label: "OpenCode Zen (opencode.ai/zen)",
|
|
5831
|
+
baseUrl: "https://opencode.ai/zen/v1",
|
|
5832
|
+
capabilities: {
|
|
5833
|
+
streaming: true,
|
|
5834
|
+
tools: true,
|
|
5835
|
+
vision: false,
|
|
5836
|
+
reasoningEffort: false,
|
|
5837
|
+
listModels: true,
|
|
5838
|
+
requiresKey: false,
|
|
5839
|
+
auth: "bearer"
|
|
5840
|
+
},
|
|
5841
|
+
create: openaiCompat
|
|
5842
|
+
};
|
|
5843
|
+
OPENCODE_GO = {
|
|
5844
|
+
type: "opencode-go",
|
|
5845
|
+
label: "OpenCode Go (opencode.ai/go)",
|
|
5846
|
+
baseUrl: "https://opencode.ai/zen/go/v1",
|
|
5847
|
+
capabilities: {
|
|
5848
|
+
streaming: true,
|
|
5849
|
+
tools: true,
|
|
5850
|
+
vision: false,
|
|
5851
|
+
reasoningEffort: false,
|
|
5852
|
+
listModels: true,
|
|
5853
|
+
requiresKey: false,
|
|
5854
|
+
auth: "bearer"
|
|
5855
|
+
},
|
|
5856
|
+
create: openaiCompat
|
|
5857
|
+
};
|
|
5858
|
+
BUILTIN_PROVIDERS = [
|
|
5859
|
+
OPENAI_COMPAT,
|
|
5860
|
+
OPENROUTER,
|
|
5861
|
+
OPENAI,
|
|
5862
|
+
ANTHROPIC,
|
|
5863
|
+
OPENCODE_ZEN,
|
|
5864
|
+
OPENCODE_GO
|
|
5865
|
+
];
|
|
5866
|
+
HOSTED_BASE_URLS = {};
|
|
5867
|
+
for (const spec of BUILTIN_PROVIDERS) {
|
|
5868
|
+
if (spec.baseUrl)
|
|
5869
|
+
HOSTED_BASE_URLS[spec.type] = spec.baseUrl;
|
|
5870
|
+
}
|
|
5871
|
+
});
|
|
5872
|
+
|
|
5873
|
+
// src/modules/providers/create.ts
|
|
5874
|
+
var exports_create = {};
|
|
5875
|
+
__export(exports_create, {
|
|
5876
|
+
defaultRegistry: () => defaultRegistry,
|
|
5877
|
+
createProvider: () => createProvider
|
|
5878
|
+
});
|
|
5879
|
+
function createProvider(type, opts, registry = defaultRegistry()) {
|
|
5880
|
+
const spec = registry.get(type);
|
|
5881
|
+
if (!spec) {
|
|
5882
|
+
return BUILTIN_PROVIDERS[0].create(opts);
|
|
5883
|
+
}
|
|
5884
|
+
return spec.create(opts);
|
|
5885
|
+
}
|
|
5886
|
+
function defaultRegistry() {
|
|
5887
|
+
const registry = new ProviderRegistry;
|
|
5888
|
+
for (const spec of BUILTIN_PROVIDERS) {
|
|
5889
|
+
registry.register(spec);
|
|
5890
|
+
}
|
|
5891
|
+
return registry;
|
|
5892
|
+
}
|
|
5893
|
+
var init_create = __esm(() => {
|
|
5894
|
+
init_presets();
|
|
5895
|
+
});
|
|
5896
|
+
|
|
5897
|
+
// src/modules/providers/manager.ts
|
|
5898
|
+
class ProviderManager {
|
|
5899
|
+
entries;
|
|
5900
|
+
activeProviderName;
|
|
5901
|
+
model;
|
|
5902
|
+
opts;
|
|
5903
|
+
registry;
|
|
5904
|
+
cache = new Map;
|
|
5905
|
+
constructor(config, opts) {
|
|
5906
|
+
this.opts = opts;
|
|
5907
|
+
this.registry = opts.registry ?? defaultRegistry();
|
|
5908
|
+
this.entries = this.normalizeEntries(config);
|
|
5909
|
+
this.activeProviderName = this.resolveActive(config);
|
|
5910
|
+
this.model = "";
|
|
5911
|
+
}
|
|
5912
|
+
get activeModel() {
|
|
5913
|
+
return this.model;
|
|
5914
|
+
}
|
|
5915
|
+
get activeName() {
|
|
5916
|
+
return this.activeProviderName;
|
|
5917
|
+
}
|
|
5918
|
+
get active() {
|
|
5919
|
+
return this.build(this.activeProviderName);
|
|
5920
|
+
}
|
|
5921
|
+
get activeEntry() {
|
|
5922
|
+
const entry = this.entryByName(this.activeProviderName);
|
|
5923
|
+
if (!entry)
|
|
5924
|
+
throw new Error(`Unknown active provider "${this.activeProviderName}"`);
|
|
5925
|
+
return entry;
|
|
5926
|
+
}
|
|
5927
|
+
list() {
|
|
5928
|
+
return this.entries.map((e) => {
|
|
5929
|
+
const preset = this.registry.get(e.type);
|
|
5930
|
+
return { ...e, label: e.label || preset?.label || e.type };
|
|
5931
|
+
}).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);
|
|
5932
|
+
}
|
|
5933
|
+
listNames() {
|
|
5934
|
+
return this.list().map((e) => e.label);
|
|
5935
|
+
}
|
|
5936
|
+
pickFor(needs) {
|
|
5937
|
+
const globalWindow = this.opts.contextWindow;
|
|
5938
|
+
if (!needs.vision && !needs.tools && needs.minContext === undefined) {
|
|
5939
|
+
return this.activeProviderName;
|
|
5940
|
+
}
|
|
5941
|
+
for (const e of this.list()) {
|
|
5942
|
+
const caps = this.registry.get(e.type)?.capabilities;
|
|
5943
|
+
if (needs.vision && !caps?.vision)
|
|
5944
|
+
continue;
|
|
5945
|
+
if (needs.tools && !caps?.tools)
|
|
5946
|
+
continue;
|
|
5947
|
+
if (needs.minContext !== undefined && (e.contextWindow ?? globalWindow) < needs.minContext) {
|
|
5948
|
+
continue;
|
|
5949
|
+
}
|
|
5950
|
+
return e.label;
|
|
5951
|
+
}
|
|
5952
|
+
return;
|
|
5953
|
+
}
|
|
5954
|
+
switch(name, model) {
|
|
5955
|
+
if (!this.entryByName(name)) {
|
|
5956
|
+
throw new Error(`Unknown provider "${name}"`);
|
|
5957
|
+
}
|
|
5958
|
+
this.activeProviderName = name;
|
|
5959
|
+
if (model !== undefined)
|
|
5960
|
+
this.model = model;
|
|
5961
|
+
return this.active;
|
|
5962
|
+
}
|
|
5963
|
+
setModel(model) {
|
|
5964
|
+
this.model = model;
|
|
5965
|
+
return this.active;
|
|
5966
|
+
}
|
|
5967
|
+
toConfig() {
|
|
5968
|
+
const active = this.activeEntry;
|
|
5969
|
+
return {
|
|
5970
|
+
active: this.activeProviderName,
|
|
5971
|
+
entries: this.entries,
|
|
5972
|
+
fallback: false,
|
|
5973
|
+
type: active.type,
|
|
5974
|
+
baseUrl: active.baseUrl,
|
|
5975
|
+
apiKey: active.apiKey
|
|
5976
|
+
};
|
|
5977
|
+
}
|
|
5978
|
+
normalizeEntries(config) {
|
|
5979
|
+
if (Array.isArray(config.entries) && config.entries.length > 0) {
|
|
5980
|
+
return config.entries.map((e) => ({ ...e, label: e.label || e.type }));
|
|
5981
|
+
}
|
|
5982
|
+
return [
|
|
5983
|
+
{
|
|
5984
|
+
type: config.type ?? "openai-compat",
|
|
5985
|
+
label: config.type ?? "openai-compat",
|
|
5986
|
+
baseUrl: config.baseUrl ?? "",
|
|
5987
|
+
apiKey: config.apiKey
|
|
5988
|
+
}
|
|
5989
|
+
];
|
|
5990
|
+
}
|
|
5991
|
+
resolveActive(config) {
|
|
5992
|
+
if (config.active && this.entryByName(config.active))
|
|
5993
|
+
return config.active;
|
|
5994
|
+
const first = this.entries[0];
|
|
5995
|
+
return first ? first.label ?? "openai-compat" : "openai-compat";
|
|
5996
|
+
}
|
|
5997
|
+
entryByName(name) {
|
|
5998
|
+
return this.entries.find((e) => e.label === name || e.type === name);
|
|
5999
|
+
}
|
|
6000
|
+
build(name) {
|
|
6001
|
+
const entry = this.entryByName(name);
|
|
6002
|
+
if (!entry)
|
|
6003
|
+
throw new Error(`Unknown provider "${name}"`);
|
|
6004
|
+
const key = `${name}|${this.model}`;
|
|
6005
|
+
const cached = this.cache.get(key);
|
|
6006
|
+
if (cached)
|
|
6007
|
+
return cached;
|
|
6008
|
+
const provider = createProvider(entry.type, {
|
|
6009
|
+
model: this.model,
|
|
6010
|
+
baseUrl: entry.baseUrl,
|
|
6011
|
+
apiKey: entry.apiKey,
|
|
6012
|
+
contextWindow: entry.contextWindow ?? this.opts.contextWindow,
|
|
6013
|
+
retry: entry.retry ?? this.opts.retry,
|
|
6014
|
+
rateLimits: entry.rateLimits ?? this.opts.rateLimits
|
|
6015
|
+
}, this.registry);
|
|
6016
|
+
this.cache.set(key, provider);
|
|
6017
|
+
return provider;
|
|
6018
|
+
}
|
|
6019
|
+
resetCache() {
|
|
6020
|
+
this.cache.clear();
|
|
6021
|
+
}
|
|
6022
|
+
}
|
|
6023
|
+
var init_manager = __esm(() => {
|
|
6024
|
+
init_create();
|
|
6025
|
+
init_presets();
|
|
6026
|
+
});
|
|
6027
|
+
|
|
6028
|
+
// src/modules/providers/health.ts
|
|
6029
|
+
async function probeProviders(targets, opts) {
|
|
6030
|
+
const timeoutMs = opts?.timeoutMs ?? 5000;
|
|
6031
|
+
const results = await Promise.all(targets.map((t2) => probeOne(t2, timeoutMs)));
|
|
6032
|
+
return results;
|
|
6033
|
+
}
|
|
6034
|
+
async function probeOne(target, timeoutMs) {
|
|
6035
|
+
const start = Date.now();
|
|
6036
|
+
try {
|
|
6037
|
+
const models = await withTimeout(target.listModels(), timeoutMs);
|
|
6038
|
+
return { name: target.name, ok: true, ms: Date.now() - start, models: models.length };
|
|
6039
|
+
} catch (err) {
|
|
6040
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6041
|
+
return {
|
|
6042
|
+
name: target.name,
|
|
6043
|
+
ok: false,
|
|
6044
|
+
ms: Date.now() - start,
|
|
6045
|
+
models: 0,
|
|
6046
|
+
error: message
|
|
6047
|
+
};
|
|
6048
|
+
}
|
|
6049
|
+
}
|
|
6050
|
+
function withTimeout(promise, ms) {
|
|
6051
|
+
return new Promise((resolve, reject) => {
|
|
6052
|
+
const timer = setTimeout(() => reject(new Error(`timeout after ${ms}ms`)), ms);
|
|
6053
|
+
promise.then((v) => {
|
|
6054
|
+
clearTimeout(timer);
|
|
6055
|
+
resolve(v);
|
|
6056
|
+
}, (e) => {
|
|
6057
|
+
clearTimeout(timer);
|
|
6058
|
+
reject(e);
|
|
6059
|
+
});
|
|
6060
|
+
});
|
|
6061
|
+
}
|
|
6062
|
+
function targetsFromProviders(entries) {
|
|
6063
|
+
return entries.map(({ name, provider }) => ({
|
|
6064
|
+
name,
|
|
6065
|
+
listModels: () => provider.listModels()
|
|
6066
|
+
}));
|
|
6067
|
+
}
|
|
6068
|
+
|
|
6069
|
+
// src/modules/providers/index.ts
|
|
6070
|
+
var init_providers = __esm(() => {
|
|
6071
|
+
init_create();
|
|
6072
|
+
init_manager();
|
|
6073
|
+
init_presets();
|
|
6074
|
+
});
|
|
6075
|
+
|
|
6076
|
+
// src/modules/providers/fallback.ts
|
|
6077
|
+
function isFailoverError(err) {
|
|
6078
|
+
const e = err;
|
|
6079
|
+
if (e?.name === "AbortError")
|
|
6080
|
+
return false;
|
|
6081
|
+
if (typeof e?.llmStatus === "number")
|
|
6082
|
+
return e.llmStatus === 429 || e.llmStatus >= 500;
|
|
6083
|
+
const normCode = (e?.code ?? "").replace(/[-_]/g, "").toLowerCase();
|
|
6084
|
+
if (/^abort/.test(normCode))
|
|
6085
|
+
return false;
|
|
6086
|
+
if (/conn(refused|reset|timedout)|enotfound|eai_again|namenotresolved/.test(normCode)) {
|
|
6087
|
+
return true;
|
|
6088
|
+
}
|
|
6089
|
+
return /fetch failed|unable to connect|network|econnrefused|enotfound|etimedout|socket hang up|connection refused|connection reset/i.test(e?.message ?? "");
|
|
6090
|
+
}
|
|
6091
|
+
|
|
6092
|
+
class FallbackProvider {
|
|
6093
|
+
source;
|
|
6094
|
+
onFailover;
|
|
6095
|
+
constructor(source, onFailover) {
|
|
6096
|
+
this.source = source;
|
|
6097
|
+
this.onFailover = onFailover;
|
|
6098
|
+
}
|
|
6099
|
+
get model() {
|
|
6100
|
+
return this.source.active.model;
|
|
6101
|
+
}
|
|
6102
|
+
get contextWindow() {
|
|
6103
|
+
return this.source.active.contextWindow;
|
|
6104
|
+
}
|
|
6105
|
+
async* chat(messages, tools, signal, options) {
|
|
6106
|
+
const names = this.source.listNames();
|
|
6107
|
+
if (names.length <= 1) {
|
|
6108
|
+
yield* this.source.active.chat(messages, tools, signal, options);
|
|
6109
|
+
return;
|
|
6110
|
+
}
|
|
6111
|
+
let emitted = false;
|
|
6112
|
+
let idx = Math.max(0, names.indexOf(this.source.activeName));
|
|
6113
|
+
let lastError;
|
|
6114
|
+
for (let attempt = 0;attempt < names.length; attempt++) {
|
|
6115
|
+
const name = names[idx];
|
|
6116
|
+
const provider = name === this.source.activeName ? this.source.active : this.source.switch(name);
|
|
6117
|
+
try {
|
|
6118
|
+
for await (const chunk of provider.chat(messages, tools, signal, options)) {
|
|
6119
|
+
emitted = true;
|
|
6120
|
+
yield chunk;
|
|
6121
|
+
}
|
|
6122
|
+
return;
|
|
6123
|
+
} catch (err) {
|
|
6124
|
+
if (emitted || !isFailoverError(err))
|
|
6125
|
+
throw err;
|
|
6126
|
+
lastError = err;
|
|
6127
|
+
idx = (idx + 1) % names.length;
|
|
6128
|
+
this.onFailover?.(name, names[idx], err);
|
|
6129
|
+
}
|
|
6130
|
+
}
|
|
6131
|
+
throw lastError;
|
|
6132
|
+
}
|
|
6133
|
+
countTokens(text) {
|
|
6134
|
+
return this.source.active.countTokens(text);
|
|
6135
|
+
}
|
|
6136
|
+
async listModels() {
|
|
6137
|
+
return this.source.active.listModels();
|
|
6138
|
+
}
|
|
6139
|
+
}
|
|
6140
|
+
|
|
5720
6141
|
// src/llm/model-loader.ts
|
|
5721
6142
|
class ModelLoader {
|
|
5722
6143
|
baseUrl;
|
|
@@ -6018,6 +6439,16 @@ function safeResolvePath(baseDir, userPath) {
|
|
|
6018
6439
|
return asIs;
|
|
6019
6440
|
}
|
|
6020
6441
|
const norm = normalize(userPath);
|
|
6442
|
+
if (isAbsolute(norm)) {
|
|
6443
|
+
if (existsSync7(norm) || existsSync7(dirname2(norm)))
|
|
6444
|
+
return norm;
|
|
6445
|
+
const stripped2 = norm.replace(/^[/\\]/, "");
|
|
6446
|
+
const relativeCandidate = resolve(baseDir, stripped2);
|
|
6447
|
+
if (existsSync7(relativeCandidate) || existsSync7(dirname2(relativeCandidate))) {
|
|
6448
|
+
return relativeCandidate;
|
|
6449
|
+
}
|
|
6450
|
+
return norm;
|
|
6451
|
+
}
|
|
6021
6452
|
const stripped = norm.replace(/^[/\\]/, "");
|
|
6022
6453
|
const resolved = resolve(baseDir, stripped);
|
|
6023
6454
|
if (existsSync7(resolved) || existsSync7(dirname2(resolved)))
|
|
@@ -8187,7 +8618,7 @@ Hint: ${t("exec.npm_exec_hint")}`;
|
|
|
8187
8618
|
}
|
|
8188
8619
|
return output;
|
|
8189
8620
|
}
|
|
8190
|
-
var BASH_GRACE_MS = 5000, SPAWN_SETTLE_MS = 100, BG_OUTPUT_PREVIEW_LINES = 15, bashGraceMs, FAILING_FIRST_WORDS, HARD_BLOCK_THRESHOLD = 3, UNIX_TO_WIN_HINTS, UNIX_TO_WIN_TRANSLATE, NEVER_TOOL_CALLS, CLI_FILE_RUN_RE, NPM_EXEC_RE, bashTool;
|
|
8621
|
+
var BASH_GRACE_MS = 5000, SPAWN_SETTLE_MS = 100, BG_OUTPUT_PREVIEW_LINES = 15, bashGraceMs, FAILING_FIRST_WORDS, HARD_BLOCK_THRESHOLD = 3, UNIX_TO_WIN_HINTS, UNIX_TO_WIN_TRANSLATE, NEVER_TOOL_CALLS, CLI_FILE_RUN_RE, NPM_EXEC_RE, HIDDEN_PROCESS_TOOLS, bashTool;
|
|
8191
8622
|
var init_bash = __esm(() => {
|
|
8192
8623
|
init_command_validator();
|
|
8193
8624
|
init_audit_log();
|
|
@@ -8253,6 +8684,7 @@ var init_bash = __esm(() => {
|
|
|
8253
8684
|
]);
|
|
8254
8685
|
CLI_FILE_RUN_RE = /\b(bun|node|deno|python|python3|tsx|ts-node|php|ruby|go\s+run)\S*\s+(run\s+)?["']?[\w./\\-]+\.(ts|js|tsx|jsx|mjs|cjs|py)\b/;
|
|
8255
8686
|
NPM_EXEC_RE = /could not determine executable to run/i;
|
|
8687
|
+
HIDDEN_PROCESS_TOOLS = ["process_list", "process_log", "process_kill"];
|
|
8256
8688
|
bashTool = {
|
|
8257
8689
|
name: "bash",
|
|
8258
8690
|
icon: "\uD83D\uDCBB",
|
|
@@ -8287,6 +8719,13 @@ ${redirected.output}`
|
|
|
8287
8719
|
};
|
|
8288
8720
|
}
|
|
8289
8721
|
const command = adaptCommandForWindows(originalCommand);
|
|
8722
|
+
const baseCmd = originalCommand.trim().split(/\s+/)[0];
|
|
8723
|
+
if (HIDDEN_PROCESS_TOOLS.includes(baseCmd) && ctx.toolExecutor?.hasTool?.(baseCmd)) {
|
|
8724
|
+
return {
|
|
8725
|
+
success: false,
|
|
8726
|
+
output: t("exec.hidden_tool_hint", { tool: baseCmd })
|
|
8727
|
+
};
|
|
8728
|
+
}
|
|
8290
8729
|
if (platform2() === "win32") {
|
|
8291
8730
|
const echoWrite = extractEchoFileWrite(originalCommand);
|
|
8292
8731
|
if (echoWrite) {
|
|
@@ -8489,6 +8928,14 @@ var init_process_log = __esm(() => {
|
|
|
8489
8928
|
required: ["id"]
|
|
8490
8929
|
},
|
|
8491
8930
|
handler: async (ctx, args) => {
|
|
8931
|
+
if (args.id === undefined || String(args.id).trim() === "") {
|
|
8932
|
+
const known = processRegistry.list();
|
|
8933
|
+
const ids = known.map((p) => p.id);
|
|
8934
|
+
const listPart = ids.length ? `
|
|
8935
|
+
${t("proc.list_header")}: ${ids.join(", ")}` : `
|
|
8936
|
+
${t("proc.missing_id_list")}`;
|
|
8937
|
+
return { success: false, output: `${t("proc.missing_id")}${listPart}` };
|
|
8938
|
+
}
|
|
8492
8939
|
const id = String(args.id);
|
|
8493
8940
|
const entry = processRegistry.get(id);
|
|
8494
8941
|
if (!entry) {
|
|
@@ -8530,6 +8977,14 @@ var init_process_kill = __esm(() => {
|
|
|
8530
8977
|
required: ["id"]
|
|
8531
8978
|
},
|
|
8532
8979
|
handler: async (ctx, args) => {
|
|
8980
|
+
if (args.id === undefined || String(args.id).trim() === "") {
|
|
8981
|
+
const known = processRegistry.list();
|
|
8982
|
+
const ids = known.map((p) => p.id);
|
|
8983
|
+
const listPart = ids.length ? `
|
|
8984
|
+
${t("proc.list_header")}: ${ids.join(", ")}` : `
|
|
8985
|
+
${t("proc.missing_id_list")}`;
|
|
8986
|
+
return { success: false, output: `${t("proc.missing_id")}${listPart}` };
|
|
8987
|
+
}
|
|
8533
8988
|
const id = String(args.id);
|
|
8534
8989
|
const entry = processRegistry.get(id);
|
|
8535
8990
|
if (!entry) {
|
|
@@ -8636,9 +9091,11 @@ import { readFileSync as readFileSync9, existsSync as existsSync18 } from "fs";
|
|
|
8636
9091
|
|
|
8637
9092
|
class SessionLogger {
|
|
8638
9093
|
session;
|
|
9094
|
+
getCaller;
|
|
8639
9095
|
logger;
|
|
8640
|
-
constructor(session, logger) {
|
|
9096
|
+
constructor(session, logger, getCaller) {
|
|
8641
9097
|
this.session = session;
|
|
9098
|
+
this.getCaller = getCaller;
|
|
8642
9099
|
this.logger = logger;
|
|
8643
9100
|
this.bindSessionDir();
|
|
8644
9101
|
}
|
|
@@ -8677,12 +9134,15 @@ class SessionLogger {
|
|
|
8677
9134
|
});
|
|
8678
9135
|
}
|
|
8679
9136
|
logAssistant(content, reasoning, toolCalls, iteration) {
|
|
9137
|
+
const caller = this.getCaller?.();
|
|
8680
9138
|
if (reasoning) {
|
|
8681
9139
|
this.session?.appendLog({
|
|
8682
9140
|
ts: new Date().toISOString(),
|
|
8683
9141
|
type: "reasoning",
|
|
8684
9142
|
content: reasoning,
|
|
8685
|
-
iteration
|
|
9143
|
+
iteration,
|
|
9144
|
+
provider: caller?.provider,
|
|
9145
|
+
model: caller?.model
|
|
8686
9146
|
});
|
|
8687
9147
|
}
|
|
8688
9148
|
this.session?.appendLog({
|
|
@@ -8696,14 +9156,19 @@ class SessionLogger {
|
|
|
8696
9156
|
arguments: tc.arguments
|
|
8697
9157
|
}))
|
|
8698
9158
|
} : {},
|
|
8699
|
-
iteration
|
|
9159
|
+
iteration,
|
|
9160
|
+
provider: caller?.provider,
|
|
9161
|
+
model: caller?.model
|
|
8700
9162
|
});
|
|
8701
9163
|
}
|
|
8702
9164
|
saveAssistantMessage(content) {
|
|
9165
|
+
const caller = this.getCaller?.();
|
|
8703
9166
|
this.session?.appendMessage({
|
|
8704
9167
|
role: "assistant",
|
|
8705
9168
|
content: content.slice(0, 500),
|
|
8706
|
-
timestamp: new Date().toISOString()
|
|
9169
|
+
timestamp: new Date().toISOString(),
|
|
9170
|
+
provider: caller?.provider,
|
|
9171
|
+
model: caller?.model
|
|
8707
9172
|
});
|
|
8708
9173
|
}
|
|
8709
9174
|
logToolDefs(toolCount, toolNames, iteration) {
|
|
@@ -8726,11 +9191,14 @@ class SessionLogger {
|
|
|
8726
9191
|
});
|
|
8727
9192
|
}
|
|
8728
9193
|
logToolResult(call, result, duration, iteration) {
|
|
9194
|
+
const caller = this.getCaller?.();
|
|
8729
9195
|
this.session?.appendMessage({
|
|
8730
9196
|
role: "tool",
|
|
8731
9197
|
content: sanitizeLogMessage(result.output.slice(0, 500)),
|
|
8732
9198
|
name: call.name,
|
|
8733
|
-
timestamp: new Date().toISOString()
|
|
9199
|
+
timestamp: new Date().toISOString(),
|
|
9200
|
+
provider: caller?.provider,
|
|
9201
|
+
model: caller?.model
|
|
8734
9202
|
});
|
|
8735
9203
|
this.session?.appendLog({
|
|
8736
9204
|
ts: new Date().toISOString(),
|
|
@@ -8741,7 +9209,9 @@ class SessionLogger {
|
|
|
8741
9209
|
content: sanitizeLogMessage(result.output.slice(0, 1000)),
|
|
8742
9210
|
diff: result.diff,
|
|
8743
9211
|
duration,
|
|
8744
|
-
iteration
|
|
9212
|
+
iteration,
|
|
9213
|
+
provider: caller?.provider,
|
|
9214
|
+
model: caller?.model
|
|
8745
9215
|
});
|
|
8746
9216
|
}
|
|
8747
9217
|
logCompaction(info) {
|
|
@@ -8768,6 +9238,8 @@ class SessionLogger {
|
|
|
8768
9238
|
type: "context",
|
|
8769
9239
|
content: info.kind === "start" ? "context snapshot at session start" : `context at iteration ${info.iteration}`,
|
|
8770
9240
|
iteration: info.iteration,
|
|
9241
|
+
provider: info.provider,
|
|
9242
|
+
model: info.model,
|
|
8771
9243
|
window: info.window,
|
|
8772
9244
|
systemBudget: info.systemBudget,
|
|
8773
9245
|
reserveBudget: info.reserveBudget,
|
|
@@ -8787,6 +9259,8 @@ class SessionLogger {
|
|
|
8787
9259
|
ts: new Date().toISOString(),
|
|
8788
9260
|
type: "llm_usage",
|
|
8789
9261
|
iteration,
|
|
9262
|
+
provider: usage.provider,
|
|
9263
|
+
model: usage.model,
|
|
8790
9264
|
promptTokens: usage.promptTokens,
|
|
8791
9265
|
completionTokens: usage.completionTokens,
|
|
8792
9266
|
totalTokens: usage.totalTokens,
|
|
@@ -9622,18 +10096,20 @@ class OrchestratorClient {
|
|
|
9622
10096
|
this.config = config;
|
|
9623
10097
|
if (config.model) {
|
|
9624
10098
|
if (config.provider) {
|
|
9625
|
-
this.provider =
|
|
10099
|
+
this.provider = createProvider(config.provider.type ?? "openai-compat", {
|
|
9626
10100
|
model: config.model,
|
|
9627
10101
|
baseUrl: config.provider.baseUrl || "http://localhost:1234/v1",
|
|
9628
10102
|
apiKey: config.provider.apiKey,
|
|
10103
|
+
contextWindow: 32768,
|
|
9629
10104
|
retry: config.retry
|
|
9630
10105
|
});
|
|
9631
10106
|
} else if (defaultProvider) {
|
|
9632
10107
|
this.provider = defaultProvider;
|
|
9633
10108
|
} else {
|
|
9634
|
-
this.provider =
|
|
10109
|
+
this.provider = createProvider("openai-compat", {
|
|
9635
10110
|
model: config.model,
|
|
9636
10111
|
baseUrl: "http://localhost:1234/v1",
|
|
10112
|
+
contextWindow: 32768,
|
|
9637
10113
|
retry: config.retry
|
|
9638
10114
|
});
|
|
9639
10115
|
}
|
|
@@ -9800,7 +10276,7 @@ Respond with JSON only:
|
|
|
9800
10276
|
|
|
9801
10277
|
Max 3 re-plan cycles. After 3 cycles, return partial result.`;
|
|
9802
10278
|
var init_orchestrator = __esm(() => {
|
|
9803
|
-
|
|
10279
|
+
init_create();
|
|
9804
10280
|
init_esm();
|
|
9805
10281
|
});
|
|
9806
10282
|
|
|
@@ -10118,6 +10594,12 @@ class StuckDetector {
|
|
|
10118
10594
|
this.consecutiveFailures = 0;
|
|
10119
10595
|
this.lastFailedTool = "";
|
|
10120
10596
|
this.escalationCount = 0;
|
|
10597
|
+
for (const [tool, count] of this.toolErrors) {
|
|
10598
|
+
if (count <= 1)
|
|
10599
|
+
this.toolErrors.delete(tool);
|
|
10600
|
+
else
|
|
10601
|
+
this.toolErrors.set(tool, count - 1);
|
|
10602
|
+
}
|
|
10121
10603
|
if (this.iterationsOnCurrentStep > 0) {
|
|
10122
10604
|
this.iterationsOnCurrentStep = Math.max(0, this.iterationsOnCurrentStep - 1);
|
|
10123
10605
|
}
|
|
@@ -10308,6 +10790,20 @@ class StuckDetector {
|
|
|
10308
10790
|
count: this.repetitionThreshold
|
|
10309
10791
|
});
|
|
10310
10792
|
}
|
|
10793
|
+
getWarningKey() {
|
|
10794
|
+
if (this.isStuck())
|
|
10795
|
+
return "stuck";
|
|
10796
|
+
const errorTool = Array.from(this.toolErrors.entries()).find(([_, c]) => c >= this.errorThreshold);
|
|
10797
|
+
if (errorTool)
|
|
10798
|
+
return `tool-errors:${errorTool[0]}`;
|
|
10799
|
+
if (this.hasConsecutiveFailures())
|
|
10800
|
+
return "consecutive";
|
|
10801
|
+
if (this.hasRepetitiveToolCalls())
|
|
10802
|
+
return "repetitive";
|
|
10803
|
+
if (this.hasReadOnlyLoop())
|
|
10804
|
+
return "read-only";
|
|
10805
|
+
return "";
|
|
10806
|
+
}
|
|
10311
10807
|
getStuckReason() {
|
|
10312
10808
|
if (this.isStuck()) {
|
|
10313
10809
|
return t("exec.stuck", {
|
|
@@ -11849,6 +12345,7 @@ class CostTracker {
|
|
|
11849
12345
|
model;
|
|
11850
12346
|
_total = 0;
|
|
11851
12347
|
_known = false;
|
|
12348
|
+
byProvider = new Map;
|
|
11852
12349
|
constructor(model, config) {
|
|
11853
12350
|
this.model = model;
|
|
11854
12351
|
this.config = config;
|
|
@@ -11856,16 +12353,36 @@ class CostTracker {
|
|
|
11856
12353
|
setModel(model) {
|
|
11857
12354
|
this.model = model;
|
|
11858
12355
|
}
|
|
11859
|
-
record(promptTokens, completionTokens) {
|
|
12356
|
+
record(promptTokens, completionTokens, provider) {
|
|
12357
|
+
const key = provider || "unknown";
|
|
11860
12358
|
const cost = calculateCost(this.model, promptTokens, completionTokens, this.config);
|
|
11861
12359
|
if (cost === undefined)
|
|
11862
12360
|
return;
|
|
11863
12361
|
this._known = true;
|
|
11864
12362
|
this._total += cost;
|
|
12363
|
+
let entry = this.byProvider.get(key);
|
|
12364
|
+
if (!entry) {
|
|
12365
|
+
entry = { total: 0, known: false };
|
|
12366
|
+
this.byProvider.set(key, entry);
|
|
12367
|
+
}
|
|
12368
|
+
entry.known = true;
|
|
12369
|
+
entry.total += cost;
|
|
11865
12370
|
}
|
|
11866
12371
|
get total() {
|
|
11867
12372
|
return this._known ? this._total : undefined;
|
|
11868
12373
|
}
|
|
12374
|
+
totalFor(provider) {
|
|
12375
|
+
const entry = this.byProvider.get(provider);
|
|
12376
|
+
return entry && entry.known ? entry.total : undefined;
|
|
12377
|
+
}
|
|
12378
|
+
breakdown() {
|
|
12379
|
+
const entries = [];
|
|
12380
|
+
for (const [provider, entry] of this.byProvider) {
|
|
12381
|
+
if (entry.known)
|
|
12382
|
+
entries.push({ provider, cost: entry.total });
|
|
12383
|
+
}
|
|
12384
|
+
return entries.sort((a, b) => b.cost - a.cost);
|
|
12385
|
+
}
|
|
11869
12386
|
get formatted() {
|
|
11870
12387
|
return this._known ? formatCost(this._total) : undefined;
|
|
11871
12388
|
}
|
|
@@ -11908,6 +12425,12 @@ class Agent {
|
|
|
11908
12425
|
get contextManager() {
|
|
11909
12426
|
return this.deps.contextManager;
|
|
11910
12427
|
}
|
|
12428
|
+
callerProvenance() {
|
|
12429
|
+
return {
|
|
12430
|
+
provider: this.deps.config.provider?.type || "unknown",
|
|
12431
|
+
model: this.deps.config.model
|
|
12432
|
+
};
|
|
12433
|
+
}
|
|
11911
12434
|
getModule(name) {
|
|
11912
12435
|
return this.deps.moduleRegistry?.get(name);
|
|
11913
12436
|
}
|
|
@@ -12022,7 +12545,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
12022
12545
|
sessionManager,
|
|
12023
12546
|
baseDir
|
|
12024
12547
|
} = this.deps;
|
|
12025
|
-
const slog = new SessionLogger(sessionManager, logger);
|
|
12548
|
+
const slog = new SessionLogger(sessionManager, logger, () => this.callerProvenance());
|
|
12026
12549
|
if (sessionManager && !sessionManager.getActive()) {
|
|
12027
12550
|
sessionManager.create();
|
|
12028
12551
|
logger.debug(`Session started: ${sessionManager.getActive()}`);
|
|
@@ -12076,7 +12599,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
12076
12599
|
sessionManager,
|
|
12077
12600
|
baseDir
|
|
12078
12601
|
} = this.deps;
|
|
12079
|
-
const slog = new SessionLogger(sessionManager, logger);
|
|
12602
|
+
const slog = new SessionLogger(sessionManager, logger, () => this.callerProvenance());
|
|
12080
12603
|
this.abortController = new AbortController;
|
|
12081
12604
|
let iteration = 0;
|
|
12082
12605
|
let lastText = "";
|
|
@@ -12255,7 +12778,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
12255
12778
|
source,
|
|
12256
12779
|
durationMs: Date.now() - llmStart
|
|
12257
12780
|
});
|
|
12258
|
-
this.costTracker.record(prompt, completion);
|
|
12781
|
+
this.costTracker.record(prompt, completion, this.callerProvenance().provider);
|
|
12259
12782
|
}
|
|
12260
12783
|
if (this.shutdownRequested) {
|
|
12261
12784
|
break;
|
|
@@ -12608,6 +13131,7 @@ ${warnLine}
|
|
|
12608
13131
|
completionTokens: usageTokens.completion,
|
|
12609
13132
|
totalTokens: usageTokens.total,
|
|
12610
13133
|
totalCost: this.costTracker.total,
|
|
13134
|
+
costBreakdown: this.costTracker.breakdown(),
|
|
12611
13135
|
compactionCount: contextManager.getCompactionCount(),
|
|
12612
13136
|
contextQuality: contextManager.getQuality()
|
|
12613
13137
|
};
|
|
@@ -12623,6 +13147,7 @@ ${warnLine}
|
|
|
12623
13147
|
completionTokens: usageTokens.completion,
|
|
12624
13148
|
totalTokens: usageTokens.total,
|
|
12625
13149
|
totalCost: this.costTracker.total,
|
|
13150
|
+
costBreakdown: this.costTracker.breakdown(),
|
|
12626
13151
|
compactionCount: contextManager.getCompactionCount(),
|
|
12627
13152
|
contextQuality: contextManager.getQuality()
|
|
12628
13153
|
};
|
|
@@ -12649,6 +13174,45 @@ ${warnLine}
|
|
|
12649
13174
|
this.deps.config = config;
|
|
12650
13175
|
this.costTracker.setModel(config.model);
|
|
12651
13176
|
}
|
|
13177
|
+
setProvider(name, model) {
|
|
13178
|
+
const manager = new ProviderManager(this.deps.config.provider, {
|
|
13179
|
+
contextWindow: this.deps.config.contextWindow,
|
|
13180
|
+
retry: this.deps.config.retry,
|
|
13181
|
+
rateLimits: this.deps.config.security?.rateLimits
|
|
13182
|
+
});
|
|
13183
|
+
manager.switch(name, model);
|
|
13184
|
+
const providerCfg = manager.toConfig();
|
|
13185
|
+
this.deps.config.provider = providerCfg;
|
|
13186
|
+
this.deps.llmProvider = manager.active;
|
|
13187
|
+
this.deps.toolExecutor.updateProvider(manager.active);
|
|
13188
|
+
this.deps.toolExecutor.ctx.llmProvider = manager.active;
|
|
13189
|
+
}
|
|
13190
|
+
listProviders() {
|
|
13191
|
+
const manager = new ProviderManager(this.deps.config.provider, {
|
|
13192
|
+
contextWindow: this.deps.config.contextWindow,
|
|
13193
|
+
retry: this.deps.config.retry,
|
|
13194
|
+
rateLimits: this.deps.config.security?.rateLimits
|
|
13195
|
+
});
|
|
13196
|
+
const activeName = this.deps.config.provider.active;
|
|
13197
|
+
return manager.list().map((e) => ({
|
|
13198
|
+
...e,
|
|
13199
|
+
active: e.label === activeName || e.type === activeName
|
|
13200
|
+
}));
|
|
13201
|
+
}
|
|
13202
|
+
getProviderFor(name) {
|
|
13203
|
+
try {
|
|
13204
|
+
const manager = new ProviderManager(this.deps.config.provider, {
|
|
13205
|
+
contextWindow: this.deps.config.contextWindow,
|
|
13206
|
+
retry: this.deps.config.retry,
|
|
13207
|
+
rateLimits: this.deps.config.security?.rateLimits
|
|
13208
|
+
});
|
|
13209
|
+
manager.setModel(this.deps.config.model);
|
|
13210
|
+
manager.switch(name);
|
|
13211
|
+
return manager.active;
|
|
13212
|
+
} catch {
|
|
13213
|
+
return;
|
|
13214
|
+
}
|
|
13215
|
+
}
|
|
12652
13216
|
setContext(messages) {
|
|
12653
13217
|
const { contextManager } = this.deps;
|
|
12654
13218
|
contextManager.clear();
|
|
@@ -12683,6 +13247,7 @@ ${warnLine}
|
|
|
12683
13247
|
}
|
|
12684
13248
|
var TOOL_RESULT_MAX_TOKENS_RATIO = 0.3, TOOL_RESULT_ABSOLUTE_MAX_CHARS = 15000, QUALITY_TRIGGER_THRESHOLD = 40, FORCED_COMPACTION_COOLDOWN = 3;
|
|
12685
13249
|
var init_agent = __esm(() => {
|
|
13250
|
+
init_manager();
|
|
12686
13251
|
init_i18n();
|
|
12687
13252
|
init_colors();
|
|
12688
13253
|
init_prompt_builder();
|
|
@@ -13158,7 +13723,7 @@ ${lines.join(`
|
|
|
13158
13723
|
}
|
|
13159
13724
|
}
|
|
13160
13725
|
var COMPACTION_INTERVAL = 15, KEEP_LAST_N = 6;
|
|
13161
|
-
var
|
|
13726
|
+
var init_manager2 = __esm(() => {
|
|
13162
13727
|
init_fact_extractor();
|
|
13163
13728
|
});
|
|
13164
13729
|
|
|
@@ -13828,7 +14393,7 @@ class PluginManager {
|
|
|
13828
14393
|
return chunk;
|
|
13829
14394
|
}
|
|
13830
14395
|
}
|
|
13831
|
-
var
|
|
14396
|
+
var init_manager3 = __esm(() => {
|
|
13832
14397
|
init_checker();
|
|
13833
14398
|
});
|
|
13834
14399
|
|
|
@@ -13848,9 +14413,9 @@ Workflow rules:
|
|
|
13848
14413
|
var init_subagent = __esm(() => {
|
|
13849
14414
|
init_i18n();
|
|
13850
14415
|
init_agent();
|
|
13851
|
-
init_manager();
|
|
13852
|
-
init_detector();
|
|
13853
14416
|
init_manager2();
|
|
14417
|
+
init_detector();
|
|
14418
|
+
init_manager3();
|
|
13854
14419
|
init_audit_log();
|
|
13855
14420
|
init_session_isolation();
|
|
13856
14421
|
init_store();
|
|
@@ -14958,8 +15523,8 @@ var init_pipeline_run = __esm(() => {
|
|
|
14958
15523
|
init_engine();
|
|
14959
15524
|
init_parser();
|
|
14960
15525
|
init_agent();
|
|
14961
|
-
init_manager();
|
|
14962
15526
|
init_manager2();
|
|
15527
|
+
init_manager3();
|
|
14963
15528
|
init_detector();
|
|
14964
15529
|
init_audit_log();
|
|
14965
15530
|
init_session_isolation();
|
|
@@ -17503,6 +18068,16 @@ import { spawn as spawn6, execSync } from "child_process";
|
|
|
17503
18068
|
import { existsSync as existsSync32, readFileSync as readFileSync17 } from "fs";
|
|
17504
18069
|
import { resolve as resolve18, extname as extname4, join as join26 } from "path";
|
|
17505
18070
|
import { platform as platform5 } from "os";
|
|
18071
|
+
function lintCacheKey(baseDir, lintScript) {
|
|
18072
|
+
return `${baseDir}::${lintScript}`;
|
|
18073
|
+
}
|
|
18074
|
+
function isMissingBinaryError(err, combinedOutput) {
|
|
18075
|
+
if (err.status === 127)
|
|
18076
|
+
return true;
|
|
18077
|
+
if (err.code === "ENOENT")
|
|
18078
|
+
return true;
|
|
18079
|
+
return MISSING_BINARY_RE.test(combinedOutput);
|
|
18080
|
+
}
|
|
17506
18081
|
function contentHash(content) {
|
|
17507
18082
|
let h = 5381;
|
|
17508
18083
|
for (let i = 0;i < content.length; i++) {
|
|
@@ -17612,23 +18187,35 @@ class LintOnWritePlugin {
|
|
|
17612
18187
|
return null;
|
|
17613
18188
|
}
|
|
17614
18189
|
async runProjectLint(ctx, result, signal) {
|
|
18190
|
+
let lintScript;
|
|
17615
18191
|
try {
|
|
17616
18192
|
const packageJsonPath = join26(ctx.baseDir, "package.json");
|
|
17617
18193
|
if (!existsSync32(packageJsonPath)) {
|
|
17618
18194
|
return;
|
|
17619
18195
|
}
|
|
17620
18196
|
const packageJson = JSON.parse(readFileSync17(packageJsonPath, "utf-8"));
|
|
17621
|
-
|
|
18197
|
+
lintScript = packageJson.scripts?.lint;
|
|
17622
18198
|
if (!lintScript) {
|
|
17623
18199
|
return;
|
|
17624
18200
|
}
|
|
18201
|
+
if (missingLintBinaries.has(lintCacheKey(ctx.baseDir, lintScript))) {
|
|
18202
|
+
ctx.logger.debug(`Lint skipped (linter for "${lintScript}" not installed)`);
|
|
18203
|
+
return;
|
|
18204
|
+
}
|
|
17625
18205
|
ctx.logger.debug(`Running lint: ${lintScript}`);
|
|
17626
18206
|
await runAsync(lintScript, ctx.baseDir, 30000, signal);
|
|
17627
18207
|
ctx.logger.debug("Lint passed");
|
|
17628
18208
|
} catch (err) {
|
|
17629
18209
|
const stderr = (err.stderr?.toString?.() || "").trim();
|
|
17630
18210
|
const stdout = (err.stdout?.toString?.() || "").trim();
|
|
17631
|
-
const
|
|
18211
|
+
const combined = `${stderr}
|
|
18212
|
+
${stdout}`;
|
|
18213
|
+
if (lintScript && isMissingBinaryError(err, combined)) {
|
|
18214
|
+
missingLintBinaries.add(lintCacheKey(ctx.baseDir, lintScript));
|
|
18215
|
+
ctx.logger.debug(`Lint binary missing — skipping project lint for this session`);
|
|
18216
|
+
return;
|
|
18217
|
+
}
|
|
18218
|
+
const detail = combined.trim().split(`
|
|
17632
18219
|
`).filter(Boolean).slice(-5).join(`
|
|
17633
18220
|
`);
|
|
17634
18221
|
ctx.logger.warn(`Lint failed: ${err.message}`);
|
|
@@ -17733,9 +18320,11 @@ function runAsync(command, cwd, timeoutMs, signal) {
|
|
|
17733
18320
|
});
|
|
17734
18321
|
});
|
|
17735
18322
|
}
|
|
17736
|
-
var TYPE_CHECK_DEBOUNCE_MS = 2000, syntaxCache, _winDecoder, plugin;
|
|
18323
|
+
var TYPE_CHECK_DEBOUNCE_MS = 2000, missingLintBinaries, MISSING_BINARY_RE, syntaxCache, _winDecoder, plugin;
|
|
17737
18324
|
var init_lint_on_write = __esm(() => {
|
|
17738
18325
|
init_project_root();
|
|
18326
|
+
missingLintBinaries = new Set;
|
|
18327
|
+
MISSING_BINARY_RE = /command not found|not recognized|не является внутренней|не распознан|не є внутрішньою|".+?" не является/i;
|
|
17739
18328
|
syntaxCache = new Map;
|
|
17740
18329
|
plugin = new LintOnWritePlugin;
|
|
17741
18330
|
});
|
|
@@ -17763,6 +18352,13 @@ function generatePlanId() {
|
|
|
17763
18352
|
}
|
|
17764
18353
|
return `plan_${id}`;
|
|
17765
18354
|
}
|
|
18355
|
+
function getPlanProgress(plan) {
|
|
18356
|
+
const done = plan.steps.filter((s) => s.status === "done").length;
|
|
18357
|
+
const skipped = plan.steps.filter((s) => s.status === "skipped").length;
|
|
18358
|
+
const total = plan.steps.length;
|
|
18359
|
+
const settled = done + skipped;
|
|
18360
|
+
return { done, skipped, total, settled, terminal: total > 0 && settled === total };
|
|
18361
|
+
}
|
|
17766
18362
|
|
|
17767
18363
|
class PlanCreator {
|
|
17768
18364
|
static createPlan(title, stepDescriptions, baseDir, kinds) {
|
|
@@ -17803,9 +18399,9 @@ class PlanCreator {
|
|
|
17803
18399
|
}
|
|
17804
18400
|
static toPromptBlock(plan, currentStepIndex) {
|
|
17805
18401
|
const date = plan.createdAt.slice(0, 10);
|
|
17806
|
-
const
|
|
17807
|
-
const
|
|
17808
|
-
const progress = terminal ? `${
|
|
18402
|
+
const p = getPlanProgress(plan);
|
|
18403
|
+
const skippedPart = p.skipped > 0 ? `, ${p.skipped} skipped` : "";
|
|
18404
|
+
const progress = p.terminal ? `${p.settled}/${p.total} done${skippedPart}, complete` : `${p.settled}/${p.total} done${skippedPart}, current: step ${currentStepIndex + 1}`;
|
|
17809
18405
|
const lines = [
|
|
17810
18406
|
`[${plan.id}] ${plan.title}`,
|
|
17811
18407
|
`Dir: ${plan.baseDir}`,
|
|
@@ -17871,13 +18467,15 @@ class PlanTracker {
|
|
|
17871
18467
|
return this.plan.steps.every((s) => s.status === "done" || s.status === "skipped");
|
|
17872
18468
|
}
|
|
17873
18469
|
getProgressString() {
|
|
17874
|
-
const
|
|
17875
|
-
const total =
|
|
17876
|
-
const
|
|
18470
|
+
const p = getPlanProgress(this.plan);
|
|
18471
|
+
const total = p.total;
|
|
18472
|
+
const settled = p.settled;
|
|
18473
|
+
const pct = total > 0 ? Math.round(settled / total * 100) : 0;
|
|
17877
18474
|
const barWidth = 10;
|
|
17878
|
-
const filled = Math.round(
|
|
18475
|
+
const filled = Math.round(settled / total * barWidth);
|
|
17879
18476
|
const bar = "█".repeat(filled) + "░".repeat(barWidth - filled);
|
|
17880
|
-
|
|
18477
|
+
const skippedPart = p.skipped > 0 ? ` (${p.skipped} skipped)` : "";
|
|
18478
|
+
return `[${this.plan.id}] ${this.plan.title} ${settled}/${total}${skippedPart} ${bar} ${pct}%`;
|
|
17881
18479
|
}
|
|
17882
18480
|
toPromptBlock() {
|
|
17883
18481
|
return PlanCreator.toPromptBlock(this.plan, this.currentStepIndex);
|
|
@@ -18255,13 +18853,20 @@ function createExecutionPlugin(deps) {
|
|
|
18255
18853
|
}
|
|
18256
18854
|
const stuckReason = deps.stuckDetector.getStuckReason();
|
|
18257
18855
|
if (stuckReason) {
|
|
18258
|
-
const
|
|
18856
|
+
const isStuck = deps.stuckDetector.isStuck();
|
|
18857
|
+
const warnKey = deps.stuckDetector.getWarningKey();
|
|
18858
|
+
const iter = typeof ctx.iteration === "number" ? ctx.iteration : 0;
|
|
18859
|
+
const logIt = isStuck ? !deps.state.stuckNotified : warnKey !== deps.state.lastStuckWarnKey || iter - deps.state.lastStuckWarnIter >= STUCK_WARN_REPEAT_EVERY;
|
|
18259
18860
|
if (logIt) {
|
|
18260
18861
|
ctx.logger?.warn(stuckReason);
|
|
18261
18862
|
ctx.sessionLog?.plan("stuck-warning", stuckReason, typeof ctx.iteration === "number" ? ctx.iteration : undefined);
|
|
18262
|
-
|
|
18863
|
+
deps.state.lastStuckWarnKey = warnKey;
|
|
18864
|
+
deps.state.lastStuckWarnIter = iter;
|
|
18865
|
+
if (isStuck)
|
|
18263
18866
|
deps.state.stuckNotified = true;
|
|
18264
18867
|
}
|
|
18868
|
+
} else {
|
|
18869
|
+
deps.state.lastStuckWarnKey = "";
|
|
18265
18870
|
}
|
|
18266
18871
|
if (deps.stuckDetector.isStuck() || deps.stuckDetector.hasRepetitiveToolCalls() || deps.stuckDetector.hasReadOnlyLoop()) {
|
|
18267
18872
|
const currentIter = typeof ctx.iteration === "number" ? ctx.iteration : 0;
|
|
@@ -18442,7 +19047,7 @@ Last compile error: ${first[1]}`;
|
|
|
18442
19047
|
}
|
|
18443
19048
|
};
|
|
18444
19049
|
}
|
|
18445
|
-
var STUCK_RECOVERY_COOLDOWN = 5, MAX_PLAN_WARNINGS_BEFORE_BLOCK = 3, FORCE_SKIP_THRESHOLD = 10, PLAN_NUDGE_THRESHOLD = 2;
|
|
19050
|
+
var STUCK_RECOVERY_COOLDOWN = 5, MAX_PLAN_WARNINGS_BEFORE_BLOCK = 3, FORCE_SKIP_THRESHOLD = 10, STUCK_WARN_REPEAT_EVERY = 5, PLAN_NUDGE_THRESHOLD = 2;
|
|
18446
19051
|
var init_execution_plugin = __esm(() => {
|
|
18447
19052
|
init_i18n();
|
|
18448
19053
|
init_bash();
|
|
@@ -18697,12 +19302,12 @@ ${display}`,
|
|
|
18697
19302
|
deps.store.archivePlan(plan);
|
|
18698
19303
|
deps.recordCompleted(plan);
|
|
18699
19304
|
deps.trackerRef.current = null;
|
|
18700
|
-
const
|
|
19305
|
+
const p = getPlanProgress(plan);
|
|
18701
19306
|
return {
|
|
18702
19307
|
success: true,
|
|
18703
19308
|
output: t("plan.completed_archived", {
|
|
18704
19309
|
id: plan.id,
|
|
18705
|
-
done: String(
|
|
19310
|
+
done: String(p.settled),
|
|
18706
19311
|
total: String(plan.steps.length)
|
|
18707
19312
|
})
|
|
18708
19313
|
};
|
|
@@ -18796,13 +19401,13 @@ ${t("plan.no_deliverables", { step: String(stepId) })}` : "";
|
|
|
18796
19401
|
deps.store.archivePlan(plan);
|
|
18797
19402
|
deps.recordCompleted(plan);
|
|
18798
19403
|
deps.trackerRef.current = null;
|
|
18799
|
-
const
|
|
19404
|
+
const p = getPlanProgress(plan);
|
|
18800
19405
|
return {
|
|
18801
19406
|
success: true,
|
|
18802
19407
|
output: `${t("plan.step_status", { step: String(args.step), status: String(args.status || "done") })}
|
|
18803
19408
|
${t("plan.completed_archived", {
|
|
18804
19409
|
id: plan.id,
|
|
18805
|
-
done: String(
|
|
19410
|
+
done: String(p.settled),
|
|
18806
19411
|
total: String(plan.steps.length)
|
|
18807
19412
|
})}${vacuousNote}`
|
|
18808
19413
|
};
|
|
@@ -18817,8 +19422,15 @@ ${progress}${vacuousNote}`,
|
|
|
18817
19422
|
display
|
|
18818
19423
|
};
|
|
18819
19424
|
}
|
|
18820
|
-
if (action === "update" && Array.isArray(args.steps) && args.steps.length > 0 &&
|
|
19425
|
+
if (action === "update" && Array.isArray(args.steps) && args.steps.length > 0 && !args.step) {
|
|
18821
19426
|
const tracker = deps.trackerRef.current;
|
|
19427
|
+
if (!tracker) {
|
|
19428
|
+
return { success: false, output: t("plan.no_active") };
|
|
19429
|
+
}
|
|
19430
|
+
const progressed = tracker.getPlan().steps.some((s) => s.status !== "pending");
|
|
19431
|
+
if (progressed) {
|
|
19432
|
+
return { success: false, output: t("plan.bulk_update_rejected") };
|
|
19433
|
+
}
|
|
18822
19434
|
const title = String(args.title || tracker.getPlan().title);
|
|
18823
19435
|
const steps = args.steps.map(String);
|
|
18824
19436
|
const parsed = deps.parseKinds(args, steps);
|
|
@@ -18905,6 +19517,15 @@ ${progress}${vacuousNote}`,
|
|
|
18905
19517
|
};
|
|
18906
19518
|
}
|
|
18907
19519
|
if (!deps.trackerRef.current) {
|
|
19520
|
+
if (args.id) {
|
|
19521
|
+
const found = deps.store.find(String(args.id));
|
|
19522
|
+
if (found && found.status !== "active") {
|
|
19523
|
+
return {
|
|
19524
|
+
success: false,
|
|
19525
|
+
output: t("plan.readonly", { id: found.plan.id, status: found.status })
|
|
19526
|
+
};
|
|
19527
|
+
}
|
|
19528
|
+
}
|
|
18908
19529
|
return { success: false, output: t("plan.no_active") };
|
|
18909
19530
|
}
|
|
18910
19531
|
return {
|
|
@@ -19079,6 +19700,8 @@ class ExecutionModule {
|
|
|
19079
19700
|
consecutivePlanWarnings: 0,
|
|
19080
19701
|
lastStepId: -1,
|
|
19081
19702
|
stuckNotified: false,
|
|
19703
|
+
lastStuckWarnKey: "",
|
|
19704
|
+
lastStuckWarnIter: -STUCK_WARN_REPEAT_EVERY,
|
|
19082
19705
|
depsGateHints: new Map,
|
|
19083
19706
|
mutationsWithoutPlan: 0,
|
|
19084
19707
|
planNudgeSent: false,
|
|
@@ -20016,7 +20639,7 @@ class SessionManager {
|
|
|
20016
20639
|
}
|
|
20017
20640
|
}
|
|
20018
20641
|
}
|
|
20019
|
-
var
|
|
20642
|
+
var init_manager4 = __esm(() => {
|
|
20020
20643
|
init_i18n();
|
|
20021
20644
|
init_session_isolation();
|
|
20022
20645
|
});
|
|
@@ -20052,7 +20675,7 @@ var init_module2 = __esm(() => {
|
|
|
20052
20675
|
// src/modules/session/index.ts
|
|
20053
20676
|
var init_session2 = __esm(() => {
|
|
20054
20677
|
init_store3();
|
|
20055
|
-
|
|
20678
|
+
init_manager4();
|
|
20056
20679
|
init_module2();
|
|
20057
20680
|
});
|
|
20058
20681
|
|
|
@@ -22274,14 +22897,18 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
22274
22897
|
const profile = new UserProfile(join39(dir));
|
|
22275
22898
|
profile.load() || profile.collect();
|
|
22276
22899
|
profile.save();
|
|
22277
|
-
const
|
|
22278
|
-
model: config.model,
|
|
22279
|
-
baseUrl: config.provider.baseUrl,
|
|
22280
|
-
apiKey: config.provider.apiKey,
|
|
22900
|
+
const providerManager = new ProviderManager(config.provider, {
|
|
22281
22901
|
contextWindow: config.contextWindow,
|
|
22282
22902
|
retry: config.retry,
|
|
22283
22903
|
rateLimits: config.security?.rateLimits
|
|
22284
22904
|
});
|
|
22905
|
+
providerManager.setModel(config.model);
|
|
22906
|
+
let llmProvider = providerManager.active;
|
|
22907
|
+
if (config.provider.fallback && providerManager.listNames().length > 1) {
|
|
22908
|
+
llmProvider = new FallbackProvider(providerManager, (from, to, error) => {
|
|
22909
|
+
logger.warn(`provider failover: ${from} -> ${to} (${error.message.slice(0, 120)})`);
|
|
22910
|
+
});
|
|
22911
|
+
}
|
|
22285
22912
|
const baseDir = projectDir ? resolve23(projectDir) : process.cwd();
|
|
22286
22913
|
const envReport = collectEnvironment({
|
|
22287
22914
|
configDir: dir,
|
|
@@ -22572,15 +23199,15 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
22572
23199
|
var init_bootstrap = __esm(() => {
|
|
22573
23200
|
init_config2();
|
|
22574
23201
|
init_app_logger();
|
|
22575
|
-
|
|
23202
|
+
init_providers();
|
|
22576
23203
|
init_tools();
|
|
22577
23204
|
init_hidden_tools_block();
|
|
22578
23205
|
init_executor();
|
|
22579
|
-
|
|
23206
|
+
init_manager3();
|
|
22580
23207
|
init_loader();
|
|
22581
23208
|
init_lint_on_write();
|
|
22582
23209
|
init_notify();
|
|
22583
|
-
|
|
23210
|
+
init_manager2();
|
|
22584
23211
|
init_token_counter();
|
|
22585
23212
|
init_detector();
|
|
22586
23213
|
init_module();
|
|
@@ -23324,35 +23951,24 @@ async function runSetup(externalRl) {
|
|
|
23324
23951
|
console.log(l);
|
|
23325
23952
|
return answers;
|
|
23326
23953
|
}
|
|
23327
|
-
var PROVIDER_TYPES, KNOWN_PORTS
|
|
23954
|
+
var PROVIDER_TYPES, KNOWN_PORTS;
|
|
23328
23955
|
var init_setup = __esm(() => {
|
|
23329
23956
|
init_colors();
|
|
23330
23957
|
init_i18n();
|
|
23331
23958
|
init_spinner();
|
|
23959
|
+
init_presets();
|
|
23332
23960
|
init_box();
|
|
23333
23961
|
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
|
-
];
|
|
23962
|
+
PROVIDER_TYPES = BUILTIN_PROVIDERS.map((spec) => ({
|
|
23963
|
+
value: spec.type,
|
|
23964
|
+
label: spec.label
|
|
23965
|
+
}));
|
|
23344
23966
|
KNOWN_PORTS = [
|
|
23345
23967
|
{ port: 1234, label: "LM Studio" },
|
|
23346
23968
|
{ port: 11434, label: "Ollama" },
|
|
23347
23969
|
{ port: 8080, label: "vLLM / custom" },
|
|
23348
23970
|
{ port: 4891, label: "GPT4All" }
|
|
23349
23971
|
];
|
|
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
23972
|
});
|
|
23357
23973
|
|
|
23358
23974
|
// src/modules/certification/manifest.ts
|
|
@@ -31176,7 +31792,13 @@ function registerMmaCommands(ctx) {
|
|
|
31176
31792
|
const sys = Math.floor(ctxW * cfg.contextBudget.systemPrompt);
|
|
31177
31793
|
const res = Math.floor(ctxW * cfg.contextBudget.responseReserve);
|
|
31178
31794
|
console.log(`${t("repl.model")} ${cfg.model}`);
|
|
31179
|
-
|
|
31795
|
+
const providers = ctx.agent.listProviders();
|
|
31796
|
+
const active = providers.find((p) => p.active);
|
|
31797
|
+
if (active) {
|
|
31798
|
+
console.log(`${t("repl.provider")} ${active.label} (${pc2.dim(active.type)}) → ${pc2.dim(active.baseUrl)}`);
|
|
31799
|
+
} else {
|
|
31800
|
+
console.log(`${t("repl.provider")} ${cfg.provider.type} → ${cfg.provider.baseUrl}`);
|
|
31801
|
+
}
|
|
31180
31802
|
console.log(`${t("repl.context")} ${ctxW} (sys:${sys} res:${res} hist:${ctxW - sys - res})`);
|
|
31181
31803
|
console.log(`${t("repl.max_iters")} ${cfg.maxToolIterations}`);
|
|
31182
31804
|
console.log(`${t("repl.stuck_thresh")} ${cfg.stuckThreshold}`);
|
|
@@ -31204,8 +31826,14 @@ function registerMmaCommands(ctx) {
|
|
|
31204
31826
|
description: t("repl.status"),
|
|
31205
31827
|
usage: t("repl.status_usage"),
|
|
31206
31828
|
action: () => {
|
|
31829
|
+
const providers = ctx.agent.listProviders();
|
|
31830
|
+
const active = providers.find((p) => p.active);
|
|
31207
31831
|
console.log(`${t("repl.model")} ${ctx.config.model}`);
|
|
31208
|
-
|
|
31832
|
+
if (active) {
|
|
31833
|
+
console.log(`${t("repl.provider")} ${active.label} (${pc2.dim(active.type)}) @ ${pc2.dim(active.baseUrl)}`);
|
|
31834
|
+
} else {
|
|
31835
|
+
console.log(`${t("repl.provider")} ${ctx.config.provider.type} @ ${ctx.config.provider.baseUrl}`);
|
|
31836
|
+
}
|
|
31209
31837
|
const meta = ctx.sessionManager?.getActiveMeta();
|
|
31210
31838
|
if (meta) {
|
|
31211
31839
|
console.log(`${t("repl.session_label")} ${meta.name} (${meta.id}) — ${meta.messageCount} messages`);
|
|
@@ -31264,8 +31892,17 @@ Excluded blocks: ${info.excluded.length}`));
|
|
|
31264
31892
|
action: async (args) => {
|
|
31265
31893
|
const subcmd = args[0];
|
|
31266
31894
|
if (!subcmd || subcmd === "list") {
|
|
31267
|
-
|
|
31268
|
-
|
|
31895
|
+
const providers = ctx.agent.listProviders();
|
|
31896
|
+
if (providers.length === 0) {
|
|
31897
|
+
console.log(`${t("repl.provider_current")} ${ctx.config.provider.type}`);
|
|
31898
|
+
console.log(` ${ctx.config.provider.baseUrl}`);
|
|
31899
|
+
} else {
|
|
31900
|
+
console.log(t("repl.provider_current"));
|
|
31901
|
+
for (const p of providers) {
|
|
31902
|
+
const marker = p.active ? pc2.green("* ") : " ";
|
|
31903
|
+
console.log(` ${marker}${p.label} (${pc2.dim(p.type)}) ${pc2.dim(p.baseUrl)}`);
|
|
31904
|
+
}
|
|
31905
|
+
}
|
|
31269
31906
|
return;
|
|
31270
31907
|
}
|
|
31271
31908
|
if (subcmd === "use") {
|
|
@@ -31274,11 +31911,13 @@ Excluded blocks: ${info.excluded.length}`));
|
|
|
31274
31911
|
console.log(t("repl.provider_usage"));
|
|
31275
31912
|
return;
|
|
31276
31913
|
}
|
|
31277
|
-
|
|
31278
|
-
|
|
31279
|
-
|
|
31280
|
-
|
|
31281
|
-
|
|
31914
|
+
try {
|
|
31915
|
+
await ctx.agent.setProvider(name);
|
|
31916
|
+
ctx.refreshModelCache();
|
|
31917
|
+
console.log(pc2.green(t("repl.provider_set", { name })));
|
|
31918
|
+
} catch (e) {
|
|
31919
|
+
console.log(pc2.red(e.message));
|
|
31920
|
+
}
|
|
31282
31921
|
return;
|
|
31283
31922
|
}
|
|
31284
31923
|
console.log(t("repl.provider_usage"));
|
|
@@ -31306,6 +31945,7 @@ Excluded blocks: ${info.excluded.length}`));
|
|
|
31306
31945
|
const models = await provider.listModels();
|
|
31307
31946
|
s.stop();
|
|
31308
31947
|
if (models.length > 0) {
|
|
31948
|
+
ctx.refreshModelCache();
|
|
31309
31949
|
console.log(t("cli.available_models"));
|
|
31310
31950
|
const { getCertMark: getCertMark2 } = await Promise.resolve().then(() => (init_manifest(), exports_manifest));
|
|
31311
31951
|
for (const m of models) {
|
|
@@ -31777,6 +32417,7 @@ init_bootstrap();
|
|
|
31777
32417
|
init_config2();
|
|
31778
32418
|
init_setup();
|
|
31779
32419
|
init_i18n();
|
|
32420
|
+
init_colors();
|
|
31780
32421
|
import { join as join46 } from "path";
|
|
31781
32422
|
import { homedir as homedir16 } from "os";
|
|
31782
32423
|
|
|
@@ -32413,7 +33054,7 @@ function createPluginCommand(program2) {
|
|
|
32413
33054
|
}
|
|
32414
33055
|
|
|
32415
33056
|
// src/cli/commands.ts
|
|
32416
|
-
|
|
33057
|
+
init_presets();
|
|
32417
33058
|
init_version();
|
|
32418
33059
|
var version = readMmaVersion();
|
|
32419
33060
|
function createProgram() {
|
|
@@ -32496,8 +33137,8 @@ function createProgram() {
|
|
|
32496
33137
|
model.command("list").description(t("cli.list_models")).action(async () => {
|
|
32497
33138
|
const { config } = await bootstrap();
|
|
32498
33139
|
console.log(t("cli.current_model"), config.model);
|
|
32499
|
-
const {
|
|
32500
|
-
const provider2 =
|
|
33140
|
+
const { createProvider: createProvider2 } = await Promise.resolve().then(() => (init_create(), exports_create));
|
|
33141
|
+
const provider2 = createProvider2(config.provider.type, {
|
|
32501
33142
|
model: config.model,
|
|
32502
33143
|
baseUrl: config.provider.baseUrl,
|
|
32503
33144
|
apiKey: config.provider.apiKey,
|
|
@@ -32576,14 +33217,51 @@ function createProgram() {
|
|
|
32576
33217
|
console.log(t("cli.context_set", { size: contextWindow }));
|
|
32577
33218
|
});
|
|
32578
33219
|
const provider = program2.command("provider").description(t("cli.manage_providers"));
|
|
33220
|
+
provider.command("check").description(t("cli.provider_check")).option("--timeout <ms>", "Per-provider probe timeout in ms").action(async (opts) => {
|
|
33221
|
+
const { config, agent } = await bootstrap();
|
|
33222
|
+
const providers = agent.listProviders();
|
|
33223
|
+
const targets = targetsFromProviders(providers.length > 0 ? providers.map((p) => ({ name: p.label, provider: agent.getProviderFor(p.label) })).filter((x) => Boolean(x.provider)) : [
|
|
33224
|
+
{
|
|
33225
|
+
name: config.provider.type,
|
|
33226
|
+
provider: agent.getProviderFor(config.provider.active || config.provider.type)
|
|
33227
|
+
}
|
|
33228
|
+
].filter((x) => Boolean(x.provider)));
|
|
33229
|
+
const results = await probeProviders(targets, {
|
|
33230
|
+
timeoutMs: opts.timeout ? Number(opts.timeout) : 5000
|
|
33231
|
+
});
|
|
33232
|
+
for (const r of results) {
|
|
33233
|
+
const mark = r.ok ? pc2.green("✓") : pc2.red("✗");
|
|
33234
|
+
const detail = r.ok ? pc2.dim(`${r.ms}ms, ${r.models} models`) : pc2.red(r.error ?? "failed");
|
|
33235
|
+
console.log(` ${mark} ${r.name.padEnd(16)} ${detail}`);
|
|
33236
|
+
}
|
|
33237
|
+
});
|
|
32579
33238
|
provider.command("list").description(t("cli.list_providers")).action(async () => {
|
|
32580
|
-
const { config } = await bootstrap();
|
|
33239
|
+
const { config, agent } = await bootstrap();
|
|
33240
|
+
const providers = agent.listProviders();
|
|
33241
|
+
if (providers.length > 0) {
|
|
33242
|
+
console.log(t("cli.current_provider"));
|
|
33243
|
+
for (const p of providers) {
|
|
33244
|
+
const marker = p.active ? pc2.green("* ") : " ";
|
|
33245
|
+
const prio = p.priority !== undefined ? ` ${pc2.dim(`prio=${p.priority}`)}` : "";
|
|
33246
|
+
console.log(` ${marker}${p.label} (${pc2.dim(p.type)}) ${pc2.dim(p.baseUrl)}${prio}`);
|
|
33247
|
+
}
|
|
33248
|
+
return;
|
|
33249
|
+
}
|
|
32581
33250
|
console.log(t("cli.current_provider"), config.provider.type);
|
|
32582
33251
|
console.log(t("cli.base_url"), config.provider.baseUrl);
|
|
32583
33252
|
});
|
|
32584
33253
|
provider.command("use").argument("<name>", "Provider name").description(t("cli.set_provider")).action(async (name) => {
|
|
32585
33254
|
const configPath = join46(homedir16(), ".mma", "config.json");
|
|
32586
|
-
const { config } = await bootstrap();
|
|
33255
|
+
const { config, agent } = await bootstrap();
|
|
33256
|
+
if (config.provider.entries && config.provider.entries.length > 0) {
|
|
33257
|
+
try {
|
|
33258
|
+
await agent.setProvider(name);
|
|
33259
|
+
console.log(t("cli.provider_set", { name }));
|
|
33260
|
+
} catch (e) {
|
|
33261
|
+
console.log(pc2.red(e.message));
|
|
33262
|
+
}
|
|
33263
|
+
return;
|
|
33264
|
+
}
|
|
32587
33265
|
config.provider.type = name;
|
|
32588
33266
|
const baseUrl = HOSTED_BASE_URLS[name];
|
|
32589
33267
|
if (baseUrl) {
|
|
@@ -32595,6 +33273,43 @@ function createProgram() {
|
|
|
32595
33273
|
console.log(t("cli.provider_base_hint", { baseUrl }));
|
|
32596
33274
|
}
|
|
32597
33275
|
});
|
|
33276
|
+
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) => {
|
|
33277
|
+
const configPath = join46(homedir16(), ".mma", "config.json");
|
|
33278
|
+
const { config } = await bootstrap();
|
|
33279
|
+
const entries = Array.isArray(config.provider.entries) ? config.provider.entries : [];
|
|
33280
|
+
if (entries.length === 0) {
|
|
33281
|
+
entries.push({
|
|
33282
|
+
type: config.provider.type,
|
|
33283
|
+
label: config.provider.type,
|
|
33284
|
+
baseUrl: config.provider.baseUrl,
|
|
33285
|
+
apiKey: config.provider.apiKey
|
|
33286
|
+
});
|
|
33287
|
+
}
|
|
33288
|
+
const baseUrl = opts.url || HOSTED_BASE_URLS[name] || HOSTED_BASE_URLS[`opencode-${name}`] || "";
|
|
33289
|
+
if (!baseUrl) {
|
|
33290
|
+
console.log(pc2.red(t("cli.provider_no_url", { name })));
|
|
33291
|
+
return;
|
|
33292
|
+
}
|
|
33293
|
+
entries.push({
|
|
33294
|
+
type: name,
|
|
33295
|
+
label: name,
|
|
33296
|
+
baseUrl,
|
|
33297
|
+
apiKey: opts.key,
|
|
33298
|
+
...opts.priority !== undefined ? { priority: Number(opts.priority) } : {},
|
|
33299
|
+
...opts.contextWindow !== undefined ? { contextWindow: Number(opts.contextWindow) } : {},
|
|
33300
|
+
...opts.rpm !== undefined || opts.parallel !== undefined ? {
|
|
33301
|
+
rateLimits: {
|
|
33302
|
+
maxRequestsPerMinute: Number(opts.rpm ?? 0) || 60,
|
|
33303
|
+
maxParallelTasks: Number(opts.parallel ?? 0) || 1
|
|
33304
|
+
}
|
|
33305
|
+
} : {}
|
|
33306
|
+
});
|
|
33307
|
+
config.provider.entries = entries;
|
|
33308
|
+
config.provider.active = config.provider.active || config.provider.type;
|
|
33309
|
+
saveConfig(config, configPath);
|
|
33310
|
+
console.log(pc2.green(t("cli.provider_added", { name })));
|
|
33311
|
+
console.log(t("cli.provider_switch_hint"));
|
|
33312
|
+
});
|
|
32598
33313
|
const session2 = program2.command("session").description(t("cli.manage_sessions"));
|
|
32599
33314
|
session2.command("list").description(t("cli.list_sessions")).action(async () => {
|
|
32600
33315
|
const { sessionManager } = await bootstrap();
|
|
@@ -33431,6 +34146,34 @@ class SubcommandProvider {
|
|
|
33431
34146
|
return this.subcommands.filter((s) => s.startsWith(ctx.partial));
|
|
33432
34147
|
}
|
|
33433
34148
|
}
|
|
34149
|
+
|
|
34150
|
+
class ModelArgProvider {
|
|
34151
|
+
name = "model-args";
|
|
34152
|
+
getModels;
|
|
34153
|
+
constructor(getModels) {
|
|
34154
|
+
this.getModels = getModels;
|
|
34155
|
+
}
|
|
34156
|
+
match(ctx) {
|
|
34157
|
+
return ctx.tokenIndex === 2 && ctx.tokens[0] === "/model" && ctx.tokens[1] === "use";
|
|
34158
|
+
}
|
|
34159
|
+
complete(ctx) {
|
|
34160
|
+
return this.getModels().filter((m) => m.startsWith(ctx.partial));
|
|
34161
|
+
}
|
|
34162
|
+
}
|
|
34163
|
+
class ProviderArgProvider {
|
|
34164
|
+
name = "provider-args";
|
|
34165
|
+
providers;
|
|
34166
|
+
constructor(providers) {
|
|
34167
|
+
this.providers = providers;
|
|
34168
|
+
}
|
|
34169
|
+
match(ctx) {
|
|
34170
|
+
return ctx.tokenIndex === 2 && ctx.tokens[0] === "/provider" && ctx.tokens[1] === "use";
|
|
34171
|
+
}
|
|
34172
|
+
complete(ctx) {
|
|
34173
|
+
return this.providers.filter((p) => p.startsWith(ctx.partial));
|
|
34174
|
+
}
|
|
34175
|
+
}
|
|
34176
|
+
|
|
33434
34177
|
class SessionNameProvider {
|
|
33435
34178
|
name = "session-names";
|
|
33436
34179
|
getNames;
|
|
@@ -33697,6 +34440,9 @@ class FormattingStream {
|
|
|
33697
34440
|
return result;
|
|
33698
34441
|
}
|
|
33699
34442
|
}
|
|
34443
|
+
function formatWarning(text) {
|
|
34444
|
+
return pc2.yellow(pc2.bold(t("ui.warning_prefix")) + text);
|
|
34445
|
+
}
|
|
33700
34446
|
|
|
33701
34447
|
// src/ui/renderer.ts
|
|
33702
34448
|
init_spinner();
|
|
@@ -33843,8 +34589,9 @@ ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}
|
|
|
33843
34589
|
`);
|
|
33844
34590
|
}
|
|
33845
34591
|
}
|
|
33846
|
-
toolEnd(_tool, duration, error, ctxDelta, costUsd) {
|
|
34592
|
+
toolEnd(_tool, duration, error, ctxDelta, costUsd, provider, model) {
|
|
33847
34593
|
this.spinner.stop();
|
|
34594
|
+
const prov = provider && model ? `${pc2.dim(`${provider}·${model}`)}` : undefined;
|
|
33848
34595
|
if (!this.rich) {
|
|
33849
34596
|
const parts = [];
|
|
33850
34597
|
if (ctxDelta !== undefined && ctxDelta !== 0) {
|
|
@@ -33854,6 +34601,8 @@ ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}
|
|
|
33854
34601
|
if (costUsd !== undefined && costUsd > 0) {
|
|
33855
34602
|
parts.push(`${pc2.dim("cost")} ${pc2.yellow(formatUsd(costUsd))}`);
|
|
33856
34603
|
}
|
|
34604
|
+
if (prov)
|
|
34605
|
+
parts.push(prov);
|
|
33857
34606
|
if (parts.length > 0)
|
|
33858
34607
|
this.out.write(`${parts.join(" ")}
|
|
33859
34608
|
`);
|
|
@@ -33871,6 +34620,9 @@ ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}
|
|
|
33871
34620
|
if (costUsd !== undefined && costUsd > 0) {
|
|
33872
34621
|
footer += ` ${pc2.dim("cost")} ${pc2.yellow(formatUsd(costUsd))}`;
|
|
33873
34622
|
}
|
|
34623
|
+
if (prov) {
|
|
34624
|
+
footer += ` ${pc2.dim("via")} ${prov}`;
|
|
34625
|
+
}
|
|
33874
34626
|
if (this.toolStyle === "inline") {
|
|
33875
34627
|
this.out.write(`${GUTTER}${footer}
|
|
33876
34628
|
`);
|
|
@@ -33961,7 +34713,7 @@ async function probeLspServers(config, baseDir, deps = {}) {
|
|
|
33961
34713
|
const ok = [];
|
|
33962
34714
|
const failed = [];
|
|
33963
34715
|
const deadline = Date.now() + deadlineMs;
|
|
33964
|
-
const
|
|
34716
|
+
const probeOne2 = async (language, server) => {
|
|
33965
34717
|
const projectRoot = findProjectRoot(baseDir, baseDir, server.workspaceMarkers ?? []);
|
|
33966
34718
|
const serverTimeout = Math.min(server.timeout ?? timeoutCap, timeoutCap);
|
|
33967
34719
|
const started = Date.now();
|
|
@@ -33996,7 +34748,7 @@ async function probeLspServers(config, baseDir, deps = {}) {
|
|
|
33996
34748
|
break;
|
|
33997
34749
|
}
|
|
33998
34750
|
const wave = servers.slice(i, i + maxParallel);
|
|
33999
|
-
await Promise.all(wave.map(({ language, config: server }) =>
|
|
34751
|
+
await Promise.all(wave.map(({ language, config: server }) => probeOne2(language, server)));
|
|
34000
34752
|
}
|
|
34001
34753
|
return { ok, failed, noneApplicable: false };
|
|
34002
34754
|
}
|
|
@@ -34086,6 +34838,14 @@ function stepContextForTool(plan, tool, args) {
|
|
|
34086
34838
|
|
|
34087
34839
|
// src/cli/repl.ts
|
|
34088
34840
|
init_prices();
|
|
34841
|
+
|
|
34842
|
+
// src/ui/output.ts
|
|
34843
|
+
function writeWarning(text) {
|
|
34844
|
+
process.stdout.write(formatWarning(text) + `
|
|
34845
|
+
`);
|
|
34846
|
+
}
|
|
34847
|
+
|
|
34848
|
+
// src/cli/repl.ts
|
|
34089
34849
|
function formatContextBar(used, limit, compactions, quality) {
|
|
34090
34850
|
const pct = Math.min(100, Math.round(used / limit * 100));
|
|
34091
34851
|
const barLen = 20;
|
|
@@ -34105,6 +34865,7 @@ function formatContextBar(used, limit, compactions, quality) {
|
|
|
34105
34865
|
|
|
34106
34866
|
class Repl {
|
|
34107
34867
|
completer = new Completer;
|
|
34868
|
+
modelCache = [];
|
|
34108
34869
|
running = false;
|
|
34109
34870
|
agentRunning = false;
|
|
34110
34871
|
inputLocked = false;
|
|
@@ -34200,6 +34961,28 @@ class Repl {
|
|
|
34200
34961
|
this.completer.registerProvider(new SubcommandProvider("skill", ["list", "loaded", "load", "unload", "search"]));
|
|
34201
34962
|
this.completer.registerProvider(new SkillNameProvider(this.skillsModule));
|
|
34202
34963
|
}
|
|
34964
|
+
this.completer.registerProvider(new SubcommandProvider("provider", ["list", "use"]));
|
|
34965
|
+
this.completer.registerProvider(new SubcommandProvider("model", ["list", "use"]));
|
|
34966
|
+
const entryLabels = this.config.provider?.entries?.map((e) => e.label || e.type) ?? [];
|
|
34967
|
+
if (entryLabels.length > 0) {
|
|
34968
|
+
this.completer.registerProvider(new ProviderArgProvider(entryLabels));
|
|
34969
|
+
}
|
|
34970
|
+
this.completer.registerProvider(new ModelArgProvider(() => this.modelCache));
|
|
34971
|
+
this.refreshModelCache();
|
|
34972
|
+
}
|
|
34973
|
+
refreshModelCache() {
|
|
34974
|
+
(async () => {
|
|
34975
|
+
try {
|
|
34976
|
+
const name = this.config.provider.active || this.config.provider.entries?.[0]?.label || this.config.provider.type;
|
|
34977
|
+
const provider = this.agent.getProviderFor(name);
|
|
34978
|
+
if (!provider)
|
|
34979
|
+
return;
|
|
34980
|
+
const models = await provider.listModels();
|
|
34981
|
+
if (models.length > 0) {
|
|
34982
|
+
this.modelCache = models.sort();
|
|
34983
|
+
}
|
|
34984
|
+
} catch {}
|
|
34985
|
+
})();
|
|
34203
34986
|
}
|
|
34204
34987
|
setupListeners() {
|
|
34205
34988
|
let multiLineBuffer = "";
|
|
@@ -34383,7 +35166,7 @@ ${t("image.clipboard_empty")}`));
|
|
|
34383
35166
|
if (ev.type === "start") {
|
|
34384
35167
|
renderer.toolStart(ev.tool, ev.args, stepContextForTool(this.execModule?.getActivePlan() ?? null, ev.tool, ev.args), ev.icon);
|
|
34385
35168
|
} else {
|
|
34386
|
-
renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta, ev.costUsd);
|
|
35169
|
+
renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta, ev.costUsd, ev.provider, ev.model);
|
|
34387
35170
|
if (ev.tool === "plan" || ev.tool === "todo") {
|
|
34388
35171
|
this.renderPlan(renderer);
|
|
34389
35172
|
}
|
|
@@ -34582,6 +35365,18 @@ ${t("image.clipboard_empty")}`));
|
|
|
34582
35365
|
this.updateLspRow(headerWidth, lspSummary);
|
|
34583
35366
|
}).catch(() => {});
|
|
34584
35367
|
}
|
|
35368
|
+
if ((this.config.provider.entries?.length ?? 0) > 1) {
|
|
35369
|
+
probeProviders(targetsFromProviders((this.config.provider.entries ?? []).map((e) => ({
|
|
35370
|
+
name: e.label || e.type,
|
|
35371
|
+
provider: this.agent.getProviderFor(e.label || e.type)
|
|
35372
|
+
})).filter((x) => Boolean(x.provider))), { timeoutMs: 5000 }).then((results) => {
|
|
35373
|
+
for (const r of results) {
|
|
35374
|
+
if (!r.ok && r.name !== this.config.provider.active) {
|
|
35375
|
+
writeWarning(t("repl.provider_down", { name: r.name, error: r.error ?? "" }));
|
|
35376
|
+
}
|
|
35377
|
+
}
|
|
35378
|
+
}).catch(() => {});
|
|
35379
|
+
}
|
|
34585
35380
|
}
|
|
34586
35381
|
updateLspRow(headerWidth, summary) {
|
|
34587
35382
|
if (this.agentRunning || !process.stdout.isTTY)
|
|
@@ -34644,6 +35439,10 @@ function printRunResult(result, flush) {
|
|
|
34644
35439
|
if (result.success) {
|
|
34645
35440
|
if (result.totalCost !== undefined && result.totalCost > 0) {
|
|
34646
35441
|
console.log(pc2.dim(`${t("repl.cost", { cost: formatCost(result.totalCost) })}`));
|
|
35442
|
+
if (result.costBreakdown && result.costBreakdown.length > 1) {
|
|
35443
|
+
const parts = result.costBreakdown.map((e) => `${e.provider} ${formatCost(e.cost)}`);
|
|
35444
|
+
console.log(pc2.dim(` ${t("repl.cost_breakdown", { breakdown: parts.join(", ") })}`));
|
|
35445
|
+
}
|
|
34647
35446
|
} else if (result.totalTokens !== undefined && result.totalTokens > 0) {
|
|
34648
35447
|
console.log(pc2.dim(`${t("repl.tokens", { tokens: result.totalTokens })}`));
|
|
34649
35448
|
}
|
|
@@ -34858,7 +35657,8 @@ async function main() {
|
|
|
34858
35657
|
promptTokens: result2.promptTokens ?? null,
|
|
34859
35658
|
completionTokens: result2.completionTokens ?? null,
|
|
34860
35659
|
totalTokens: result2.totalTokens ?? null,
|
|
34861
|
-
totalCost: result2.totalCost ?? null
|
|
35660
|
+
totalCost: result2.totalCost ?? null,
|
|
35661
|
+
costBreakdown: result2.costBreakdown ?? []
|
|
34862
35662
|
}, null, 2));
|
|
34863
35663
|
process.stdout.write(`
|
|
34864
35664
|
`);
|