micro-models-agent 2.12.0 → 2.13.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/CHANGELOG.md +29 -0
- package/dist/assets/app.js +22 -2
- package/dist/assets/index.html +39 -9
- package/dist/assets/js/dom.js +15 -0
- package/dist/assets/js/feed.js +129 -10
- package/dist/assets/js/sidebar.js +512 -0
- package/dist/assets/js/tabs.js +8 -2
- package/dist/assets/setup.js +267 -8
- package/dist/assets/styles.css +168 -3
- package/dist/i18n/en.json +89 -3
- package/dist/i18n/ru.json +89 -3
- package/dist/main.js +615 -92
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -2609,6 +2609,8 @@ The path was joined onto the working directory because it does not exist as give
|
|
|
2609
2609
|
{issues}
|
|
2610
2610
|
Summary: {summary}`,
|
|
2611
2611
|
"critic.advice": `A verifier flagged issues worth checking before you finish:
|
|
2612
|
+
{issues}`,
|
|
2613
|
+
"critic.repeat": `The verifier repeats a previously flagged problem — do not produce the same answer again. Change the approach, or finish honestly if the complaint does not apply:
|
|
2612
2614
|
{issues}`,
|
|
2613
2615
|
"tool.subagent_queued": "Sub-agent task queued: {task}",
|
|
2614
2616
|
"tool.subagent_artifact": `Sub-agent completed. Full result saved to artifact: {path}
|
|
@@ -3404,6 +3406,7 @@ Apply a matching solution from these results. If none is relevant — do NOT rep
|
|
|
3404
3406
|
"ui.todo_action": "{action} · {count}",
|
|
3405
3407
|
"ui.tool_running": "{tool}…",
|
|
3406
3408
|
"ui.step_context": "step {id}: {desc}",
|
|
3409
|
+
"ui.draft_retracted": "↳ draft rejected by verification, refining…",
|
|
3407
3410
|
"indexer.map_header": "Project map",
|
|
3408
3411
|
"indexer.top_directories": "Top directories",
|
|
3409
3412
|
"indexer.files": "Files",
|
|
@@ -3578,6 +3581,46 @@ Updated: {updatedAt}`,
|
|
|
3578
3581
|
"web.sidebar.session": "session",
|
|
3579
3582
|
"web.sidebar.usage": "usage",
|
|
3580
3583
|
"web.sidebar.env": "environment",
|
|
3584
|
+
"web.sidebar.tab_session": "Session",
|
|
3585
|
+
"web.sidebar.tab_map": "Map",
|
|
3586
|
+
"web.sidebar.tab_trace": "Trace",
|
|
3587
|
+
"web.sidebar.map_status": "status",
|
|
3588
|
+
"web.sidebar.map_ready": "ready",
|
|
3589
|
+
"web.sidebar.map_indexing": "indexing…",
|
|
3590
|
+
"web.sidebar.map_stale": "stale",
|
|
3591
|
+
"web.sidebar.map_updated": "updated",
|
|
3592
|
+
"web.sidebar.map_never": "never",
|
|
3593
|
+
"web.sidebar.map_today": "today, {time}",
|
|
3594
|
+
"web.sidebar.map_watcher": "watcher",
|
|
3595
|
+
"web.sidebar.map_on": "on",
|
|
3596
|
+
"web.sidebar.map_off": "off",
|
|
3597
|
+
"web.sidebar.map_files": "files",
|
|
3598
|
+
"web.sidebar.map_symbols": "symbols",
|
|
3599
|
+
"web.sidebar.map_tokens": "tokens",
|
|
3600
|
+
"web.sidebar.map_root": "root",
|
|
3601
|
+
"web.sidebar.map_languages": "languages",
|
|
3602
|
+
"web.sidebar.map_reindex": "Re-index",
|
|
3603
|
+
"web.sidebar.map_reindexing": "re-indexing…",
|
|
3604
|
+
"web.sidebar.map_reindex_failed": "re-index failed",
|
|
3605
|
+
"web.sidebar.trace_empty": "no events yet",
|
|
3606
|
+
"web.sidebar.trace_turn": "turn {n}",
|
|
3607
|
+
"web.sidebar.trace_text": "composing… {count} chars",
|
|
3608
|
+
"web.sidebar.trace_plan": "plan update",
|
|
3609
|
+
"web.sidebar.trace_question": "question",
|
|
3610
|
+
"web.sidebar.detail_metadata": "metadata",
|
|
3611
|
+
"web.sidebar.detail_arguments": "arguments",
|
|
3612
|
+
"web.sidebar.detail_output": "output",
|
|
3613
|
+
"web.sidebar.detail_error": "error",
|
|
3614
|
+
"web.sidebar.detail_text": "text",
|
|
3615
|
+
"web.sidebar.detail_plan": "plan",
|
|
3616
|
+
"web.sidebar.detail_reasoning": "reasoning",
|
|
3617
|
+
"web.sidebar.detail_status": "status",
|
|
3618
|
+
"web.sidebar.detail_ok": "ok",
|
|
3619
|
+
"web.sidebar.detail_fail": "failed",
|
|
3620
|
+
"web.sidebar.detail_duration": "duration",
|
|
3621
|
+
"web.sidebar.detail_time": "time",
|
|
3622
|
+
"web.sidebar.detail_tool_call": "tool call",
|
|
3623
|
+
"web.sidebar.detail_session": "session",
|
|
3581
3624
|
"web.env.dir": "dir",
|
|
3582
3625
|
"web.env.agents": "AGENTS.md",
|
|
3583
3626
|
"web.env.map": "map",
|
|
@@ -3972,6 +4015,7 @@ Updated: {updatedAt}`,
|
|
|
3972
4015
|
"web.history.loading": "loading…",
|
|
3973
4016
|
"web.history.unavailable": "— history unavailable",
|
|
3974
4017
|
"web.history.empty": "— empty session —",
|
|
4018
|
+
"web.scroll.latest": "latest",
|
|
3975
4019
|
"web.tool.output_one": "output · {count} char",
|
|
3976
4020
|
"web.tool.output_few": "output · {count} chars",
|
|
3977
4021
|
"web.tool.output_many": "output · {count} chars",
|
|
@@ -4015,7 +4059,7 @@ Updated: {updatedAt}`,
|
|
|
4015
4059
|
"web.setup.language.title": "Choose your language",
|
|
4016
4060
|
"web.setup.language.hint": "Used for the interface.",
|
|
4017
4061
|
"web.setup.provider.title": "Choose a provider",
|
|
4018
|
-
"web.setup.provider.hint": "For a local model pick the OpenAI-compatible option
|
|
4062
|
+
"web.setup.provider.hint": "For a local model pick the OpenAI-compatible option, scan for your server, then test the connection before continuing.",
|
|
4019
4063
|
"web.setup.provider.base_url": "Base URL",
|
|
4020
4064
|
"web.setup.provider.scan": "Scan for local servers",
|
|
4021
4065
|
"web.setup.provider.scan_found_one": "Found {count} server",
|
|
@@ -4024,6 +4068,10 @@ Updated: {updatedAt}`,
|
|
|
4024
4068
|
"web.setup.provider.scan_none": "No local servers found — enter the URL manually.",
|
|
4025
4069
|
"web.setup.provider.scan_failed": "Scan failed. Enter the URL manually.",
|
|
4026
4070
|
"web.setup.provider.required": "Please enter a base URL.",
|
|
4071
|
+
"web.setup.provider.test": "Test connection",
|
|
4072
|
+
"web.setup.provider.testing": "Testing…",
|
|
4073
|
+
"web.setup.provider.test_ok": "Server reachable.",
|
|
4074
|
+
"web.setup.provider.test_fail": "Could not reach the server at this URL.",
|
|
4027
4075
|
"web.setup.key.title": "API key",
|
|
4028
4076
|
"web.setup.key.hint": "Leave empty for local servers. Stored only in your config file and never shown again.",
|
|
4029
4077
|
"web.setup.key.label": "Key",
|
|
@@ -4042,15 +4090,54 @@ Updated: {updatedAt}`,
|
|
|
4042
4090
|
"web.setup.model.test_fail": "Could not reach the model.",
|
|
4043
4091
|
"web.setup.model.required": "Please choose or type a model.",
|
|
4044
4092
|
"web.setup.context.title": "Context & iterations",
|
|
4045
|
-
"web.setup.context.hint": "
|
|
4093
|
+
"web.setup.context.hint": "Must match the context your provider gives this model. Press Detect, or copy the value from LM Studio / Ollama / provider docs.",
|
|
4046
4094
|
"web.setup.context.contextWindow": "Context window (tokens)",
|
|
4047
4095
|
"web.setup.context.maxToolIterations": "Max tool iterations",
|
|
4048
4096
|
"web.setup.context.required": "Enter a positive number.",
|
|
4097
|
+
"web.setup.context.detect": "Detect from provider",
|
|
4098
|
+
"web.setup.context.detecting": "Detecting…",
|
|
4099
|
+
"web.setup.context.detect_ok": "Context set from provider: {count} tokens.",
|
|
4100
|
+
"web.setup.context.detect_fail": "Could not detect context — enter it manually.",
|
|
4049
4101
|
"web.setup.security.title": "Security",
|
|
4050
|
-
"web.setup.security.hint": "Safe defaults are on; turn off only what you need.",
|
|
4102
|
+
"web.setup.security.hint": "Safe defaults are on; turn off only what you need. Nothing is blocked silently — each toggle only asks the agent to confirm first.",
|
|
4051
4103
|
"web.setup.security.paths": "Restrict file access to the workspace",
|
|
4104
|
+
"web.setup.security.paths_desc": "The agent reads and writes only inside the workspace, and asks before touching .git, .env, keys and other sensitive paths.",
|
|
4052
4105
|
"web.setup.security.bash": "Block dangerous shell commands",
|
|
4106
|
+
"web.setup.security.bash_desc": "Blocks destructive commands (rm, sudo, ssh, curl, shutdown, …). The agent asks instead of running them.",
|
|
4053
4107
|
"web.setup.security.flags": "Block dangerous command flags",
|
|
4108
|
+
"web.setup.security.flags_desc": "Blocks risky flags (--force, -rf, …) that make safe commands dangerous. Works together with the command block.",
|
|
4109
|
+
"web.setup.security.configure": "Configure",
|
|
4110
|
+
"web.setup.security.advanced": "Advanced security settings",
|
|
4111
|
+
"web.setup.security.popup_hint": "One item per line. Empty list = disabled. Changes apply only to this install; you can edit them later in Settings → Security.",
|
|
4112
|
+
"web.setup.security.popup_done": "Done",
|
|
4113
|
+
"web.setup.security.reset": "Reset to defaults",
|
|
4114
|
+
"web.setup.security.denied": "Always denied paths",
|
|
4115
|
+
"web.setup.security.allowed": "Only allowed paths (empty = all except denied)",
|
|
4116
|
+
"web.setup.security.blacklist": "Always blocked commands",
|
|
4117
|
+
"web.setup.security.whitelist": "Only allowed commands (empty = all except blocked)",
|
|
4118
|
+
"web.setup.security.log_commands": "Log every executed command to the audit log",
|
|
4119
|
+
"web.setup.security.dangerous_flags": "Blocked command flags",
|
|
4120
|
+
"web.setup.security.dangerous_operators": "Blocked shell operators",
|
|
4121
|
+
"web.setup.security.network_enabled": "Network protection (block private/internal addresses)",
|
|
4122
|
+
"web.setup.security.denied_domains": "Always denied domains",
|
|
4123
|
+
"web.setup.security.allowed_domains": "Only allowed domains (empty = all except denied)",
|
|
4124
|
+
"web.setup.security.request_timeout": "Network request timeout (ms)",
|
|
4125
|
+
"web.setup.security.max_response_size": "Max response size (bytes)",
|
|
4126
|
+
"web.setup.security.max_recursion": "Max subagent recursion depth",
|
|
4127
|
+
"web.setup.security.max_file_ops": "Max file operations per session",
|
|
4128
|
+
"web.setup.security.rate_rpm": "Max LLM requests per minute",
|
|
4129
|
+
"web.setup.security.rate_parallel": "Max parallel tasks",
|
|
4130
|
+
"web.setup.security.content_scan": "Scan file content for dangerous patterns",
|
|
4131
|
+
"web.setup.security.session_encryption": "Encrypt session logs on disk",
|
|
4132
|
+
"web.setup.security.audit_enabled": "Send security events to the audit notifier",
|
|
4133
|
+
"web.setup.security.audit_severity": "Minimum severity to notify",
|
|
4134
|
+
"web.setup.security.audit_events": "Notified event types",
|
|
4135
|
+
"web.setup.security.audit_retries": "Webhook max retries",
|
|
4136
|
+
"web.setup.security.audit_timeout": "Webhook timeout (ms)",
|
|
4137
|
+
"web.setup.security.severity.low": "low",
|
|
4138
|
+
"web.setup.security.severity.medium": "medium",
|
|
4139
|
+
"web.setup.security.severity.high": "high",
|
|
4140
|
+
"web.setup.security.severity.critical": "critical",
|
|
4054
4141
|
"web.setup.review.title": "Review",
|
|
4055
4142
|
"web.setup.review.hint": "Check the values, then finish. Settings are saved to your config.",
|
|
4056
4143
|
"web.setup.review.provider": "Provider",
|
|
@@ -4423,6 +4510,8 @@ var init_ru = __esm(() => {
|
|
|
4423
4510
|
{issues}
|
|
4424
4511
|
Сводка: {summary}`,
|
|
4425
4512
|
"critic.advice": `Верификатор отметил проблемы, которые стоит проверить до завершения:
|
|
4513
|
+
{issues}`,
|
|
4514
|
+
"critic.repeat": `Верификатор повторяет уже отмеченную проблему — не выдавай тот же ответ снова. Смени подход или заверши честно, если жалоба неприменима:
|
|
4426
4515
|
{issues}`,
|
|
4427
4516
|
"tool.subagent_queued": "Задача подагенту добавлена: {task}",
|
|
4428
4517
|
"tool.subagent_artifact": `Суб-агент завершён. Полный результат сохранён в артефакт: {path}
|
|
@@ -5221,6 +5310,7 @@ var init_ru = __esm(() => {
|
|
|
5221
5310
|
"ui.todo_action": "{action} · {count}",
|
|
5222
5311
|
"ui.tool_running": "{tool}…",
|
|
5223
5312
|
"ui.step_context": "шаг {id}: {desc}",
|
|
5313
|
+
"ui.draft_retracted": "↳ черновик отклонён проверкой, уточняю…",
|
|
5224
5314
|
"indexer.map_header": "Карта проекта",
|
|
5225
5315
|
"indexer.top_directories": "Основные директории",
|
|
5226
5316
|
"indexer.files": "Файлы",
|
|
@@ -5398,6 +5488,46 @@ var init_ru = __esm(() => {
|
|
|
5398
5488
|
"web.sidebar.session": "сессия",
|
|
5399
5489
|
"web.sidebar.usage": "использование",
|
|
5400
5490
|
"web.sidebar.env": "окружение",
|
|
5491
|
+
"web.sidebar.tab_session": "Сессия",
|
|
5492
|
+
"web.sidebar.tab_map": "Карта",
|
|
5493
|
+
"web.sidebar.tab_trace": "Трассировка",
|
|
5494
|
+
"web.sidebar.map_status": "статус",
|
|
5495
|
+
"web.sidebar.map_ready": "готово",
|
|
5496
|
+
"web.sidebar.map_indexing": "индексация…",
|
|
5497
|
+
"web.sidebar.map_stale": "устарело",
|
|
5498
|
+
"web.sidebar.map_updated": "обновлено",
|
|
5499
|
+
"web.sidebar.map_never": "никогда",
|
|
5500
|
+
"web.sidebar.map_today": "сегодня, {time}",
|
|
5501
|
+
"web.sidebar.map_watcher": "вотчер",
|
|
5502
|
+
"web.sidebar.map_on": "вкл.",
|
|
5503
|
+
"web.sidebar.map_off": "выкл.",
|
|
5504
|
+
"web.sidebar.map_files": "файлы",
|
|
5505
|
+
"web.sidebar.map_symbols": "символы",
|
|
5506
|
+
"web.sidebar.map_tokens": "токены",
|
|
5507
|
+
"web.sidebar.map_root": "корень",
|
|
5508
|
+
"web.sidebar.map_languages": "языки",
|
|
5509
|
+
"web.sidebar.map_reindex": "Переиндексировать",
|
|
5510
|
+
"web.sidebar.map_reindexing": "переиндексация…",
|
|
5511
|
+
"web.sidebar.map_reindex_failed": "не удалось переиндексировать",
|
|
5512
|
+
"web.sidebar.trace_empty": "событий пока нет",
|
|
5513
|
+
"web.sidebar.trace_turn": "ход {n}",
|
|
5514
|
+
"web.sidebar.trace_text": "печатает… {count} симв.",
|
|
5515
|
+
"web.sidebar.trace_plan": "обновление плана",
|
|
5516
|
+
"web.sidebar.trace_question": "вопрос",
|
|
5517
|
+
"web.sidebar.detail_metadata": "метаданные",
|
|
5518
|
+
"web.sidebar.detail_arguments": "аргументы",
|
|
5519
|
+
"web.sidebar.detail_output": "вывод",
|
|
5520
|
+
"web.sidebar.detail_error": "ошибка",
|
|
5521
|
+
"web.sidebar.detail_text": "текст",
|
|
5522
|
+
"web.sidebar.detail_plan": "план",
|
|
5523
|
+
"web.sidebar.detail_reasoning": "размышление",
|
|
5524
|
+
"web.sidebar.detail_status": "статус",
|
|
5525
|
+
"web.sidebar.detail_ok": "ok",
|
|
5526
|
+
"web.sidebar.detail_fail": "ошибка",
|
|
5527
|
+
"web.sidebar.detail_duration": "длительность",
|
|
5528
|
+
"web.sidebar.detail_time": "время",
|
|
5529
|
+
"web.sidebar.detail_tool_call": "вызов тула",
|
|
5530
|
+
"web.sidebar.detail_session": "сессия",
|
|
5401
5531
|
"web.env.dir": "директория",
|
|
5402
5532
|
"web.env.agents": "AGENTS.md",
|
|
5403
5533
|
"web.env.map": "карта",
|
|
@@ -5765,6 +5895,7 @@ var init_ru = __esm(() => {
|
|
|
5765
5895
|
"web.history.loading": "загрузка…",
|
|
5766
5896
|
"web.history.unavailable": "— история недоступна",
|
|
5767
5897
|
"web.history.empty": "— пустая сессия —",
|
|
5898
|
+
"web.scroll.latest": "к новому",
|
|
5768
5899
|
"web.tool.output_one": "вывод · {count} символ",
|
|
5769
5900
|
"web.tool.output_few": "вывод · {count} символа",
|
|
5770
5901
|
"web.tool.output_many": "вывод · {count} символов",
|
|
@@ -5835,7 +5966,7 @@ var init_ru = __esm(() => {
|
|
|
5835
5966
|
"web.setup.language.title": "Выберите язык",
|
|
5836
5967
|
"web.setup.language.hint": "Используется для интерфейса.",
|
|
5837
5968
|
"web.setup.provider.title": "Выберите провайдера",
|
|
5838
|
-
"web.setup.provider.hint": "Для локальной модели выберите OpenAI-совместимый
|
|
5969
|
+
"web.setup.provider.hint": "Для локальной модели выберите OpenAI-совместимый вариант, просканируйте сервер и проверьте подключение перед продолжением.",
|
|
5839
5970
|
"web.setup.provider.base_url": "Базовый URL",
|
|
5840
5971
|
"web.setup.provider.scan": "Найти локальные серверы",
|
|
5841
5972
|
"web.setup.provider.scan_found_one": "Найден {count} сервер",
|
|
@@ -5844,6 +5975,10 @@ var init_ru = __esm(() => {
|
|
|
5844
5975
|
"web.setup.provider.scan_none": "Локальные серверы не найдены — введите URL вручную.",
|
|
5845
5976
|
"web.setup.provider.scan_failed": "Сканирование не удалось. Введите URL вручную.",
|
|
5846
5977
|
"web.setup.provider.required": "Укажите базовый URL.",
|
|
5978
|
+
"web.setup.provider.test": "Проверить подключение",
|
|
5979
|
+
"web.setup.provider.testing": "Проверка…",
|
|
5980
|
+
"web.setup.provider.test_ok": "Сервер отвечает.",
|
|
5981
|
+
"web.setup.provider.test_fail": "Не удалось связаться с сервером по этому URL.",
|
|
5847
5982
|
"web.setup.key.title": "API-ключ",
|
|
5848
5983
|
"web.setup.key.hint": "Для локальных серверов можно оставить пустым. Хранится только в файле конфигурации и больше не показывается.",
|
|
5849
5984
|
"web.setup.key.label": "Ключ",
|
|
@@ -5862,15 +5997,54 @@ var init_ru = __esm(() => {
|
|
|
5862
5997
|
"web.setup.model.test_fail": "Не удалось связаться с моделью.",
|
|
5863
5998
|
"web.setup.model.required": "Выберите или введите модель.",
|
|
5864
5999
|
"web.setup.context.title": "Контекст и итерации",
|
|
5865
|
-
"web.setup.context.hint": "
|
|
6000
|
+
"web.setup.context.hint": "Должно совпадать с контекстом, который провайдер выделяет этой модели. Нажмите «Определить» либо сверьтесь с LM Studio / Ollama / документацией провайдера.",
|
|
5866
6001
|
"web.setup.context.contextWindow": "Окно контекста (токены)",
|
|
5867
6002
|
"web.setup.context.maxToolIterations": "Максимум итераций тулов",
|
|
5868
6003
|
"web.setup.context.required": "Введите положительное число.",
|
|
6004
|
+
"web.setup.context.detect": "Определить у провайдера",
|
|
6005
|
+
"web.setup.context.detecting": "Определение…",
|
|
6006
|
+
"web.setup.context.detect_ok": "Контекст от провайдера: {count} токенов.",
|
|
6007
|
+
"web.setup.context.detect_fail": "Не удалось определить контекст — введите вручную.",
|
|
5869
6008
|
"web.setup.security.title": "Безопасность",
|
|
5870
|
-
"web.setup.security.hint": "Безопасные значения включены; отключайте только нужное.",
|
|
6009
|
+
"web.setup.security.hint": "Безопасные значения включены; отключайте только нужное. Ничего не блокируется молча — каждый переключатель лишь требует подтверждения перед опасным действием.",
|
|
5871
6010
|
"web.setup.security.paths": "Ограничить доступ к файлам рабочей папкой",
|
|
6011
|
+
"web.setup.security.paths_desc": "Агент читает и пишет только внутри рабочей папки, а перед доступом к .git, .env, ключам и другим чувствительным путям спросит подтверждения.",
|
|
5872
6012
|
"web.setup.security.bash": "Блокировать опасные shell-команды",
|
|
6013
|
+
"web.setup.security.bash_desc": "Блокирует разрушительные команды (rm, sudo, ssh, curl, shutdown, …). Вместо запуска агент спросит подтверждения.",
|
|
5873
6014
|
"web.setup.security.flags": "Блокировать опасные флаги команд",
|
|
6015
|
+
"web.setup.security.flags_desc": "Блокирует рискованные флаги (--force, -rf, …), которые делают безопасные команды опасными. Работает вместе с блокировкой команд.",
|
|
6016
|
+
"web.setup.security.configure": "Настроить",
|
|
6017
|
+
"web.setup.security.advanced": "Расширенные настройки безопасности",
|
|
6018
|
+
"web.setup.security.popup_hint": "По одному элементу в строке. Пустой список = выключено. Изменения применяются только к этой установке; позже их можно править в Настройках → Безопасность.",
|
|
6019
|
+
"web.setup.security.popup_done": "Готово",
|
|
6020
|
+
"web.setup.security.reset": "Сбросить к значениям по умолчанию",
|
|
6021
|
+
"web.setup.security.denied": "Всегда запрещённые пути",
|
|
6022
|
+
"web.setup.security.allowed": "Разрешены только эти пути (пусто = все, кроме запрещённых)",
|
|
6023
|
+
"web.setup.security.blacklist": "Всегда блокируемые команды",
|
|
6024
|
+
"web.setup.security.whitelist": "Разрешены только эти команды (пусто = все, кроме блокируемых)",
|
|
6025
|
+
"web.setup.security.log_commands": "Писать каждую выполненную команду в аудит-лог",
|
|
6026
|
+
"web.setup.security.dangerous_flags": "Блокируемые флаги команд",
|
|
6027
|
+
"web.setup.security.dangerous_operators": "Блокируемые shell-операторы",
|
|
6028
|
+
"web.setup.security.network_enabled": "Сетевая защита (блокировать приватные/внутренние адреса)",
|
|
6029
|
+
"web.setup.security.denied_domains": "Всегда запрещённые домены",
|
|
6030
|
+
"web.setup.security.allowed_domains": "Разрешены только эти домены (пусто = все, кроме запрещённых)",
|
|
6031
|
+
"web.setup.security.request_timeout": "Таймаут сетевых запросов (мс)",
|
|
6032
|
+
"web.setup.security.max_response_size": "Максимум размера ответа (байты)",
|
|
6033
|
+
"web.setup.security.max_recursion": "Максимум глубины рекурсии субагентов",
|
|
6034
|
+
"web.setup.security.max_file_ops": "Максимум файловых операций за сессию",
|
|
6035
|
+
"web.setup.security.rate_rpm": "Максимум LLM-запросов в минуту",
|
|
6036
|
+
"web.setup.security.rate_parallel": "Максимум параллельных задач",
|
|
6037
|
+
"web.setup.security.content_scan": "Сканировать содержимое файлов на опасные паттерны",
|
|
6038
|
+
"web.setup.security.session_encryption": "Шифровать логи сессий на диске",
|
|
6039
|
+
"web.setup.security.audit_enabled": "Отправлять события безопасности в аудит-нотификатор",
|
|
6040
|
+
"web.setup.security.audit_severity": "Минимальная severity для уведомления",
|
|
6041
|
+
"web.setup.security.audit_events": "Типы событий для уведомления",
|
|
6042
|
+
"web.setup.security.audit_retries": "Повторы вебхука",
|
|
6043
|
+
"web.setup.security.audit_timeout": "Таймаут вебхука (мс)",
|
|
6044
|
+
"web.setup.security.severity.low": "низкая",
|
|
6045
|
+
"web.setup.security.severity.medium": "средняя",
|
|
6046
|
+
"web.setup.security.severity.high": "высокая",
|
|
6047
|
+
"web.setup.security.severity.critical": "критическая",
|
|
5874
6048
|
"web.setup.review.title": "Проверка",
|
|
5875
6049
|
"web.setup.review.hint": "Проверьте значения и завершите. Настройки сохранятся в конфиг.",
|
|
5876
6050
|
"web.setup.review.provider": "Провайдер",
|
|
@@ -9465,6 +9639,9 @@ class OpenAICompatProvider {
|
|
|
9465
9639
|
this.userAgent = config.userAgent ?? defaultUserAgent();
|
|
9466
9640
|
this.debug = config.logger ? config.logger.debug.bind(config.logger) : null;
|
|
9467
9641
|
}
|
|
9642
|
+
get idleTimeoutMs() {
|
|
9643
|
+
return this.retryConfig.noDataTimeoutMs ?? 180000;
|
|
9644
|
+
}
|
|
9468
9645
|
async* chat(messages, tools, signal, options) {
|
|
9469
9646
|
if (!this.rateLimiter.canMakeRequest()) {
|
|
9470
9647
|
const waitMs = Math.min(this.rateLimiter.msUntilRequest(), LLM_MAX_RATE_WAIT_MS);
|
|
@@ -9495,7 +9672,7 @@ class OpenAICompatProvider {
|
|
|
9495
9672
|
}
|
|
9496
9673
|
if (!hasToolCall && !hasText) {
|
|
9497
9674
|
this.debug?.("LLM streaming produced no text/tool_call — falling back to non-streaming");
|
|
9498
|
-
const fallback = await this.doNonStreaming(effectiveMessages, tools, signal, options);
|
|
9675
|
+
const fallback = await this.doNonStreaming(effectiveMessages, tools, signal, options, this.idleTimeoutMs);
|
|
9499
9676
|
this.debug?.("LLM non-streaming fallback result", { chunks: fallback.length });
|
|
9500
9677
|
for (const chunk of fallback) {
|
|
9501
9678
|
if (hasReasoning && chunk.type === "reasoning")
|
|
@@ -9522,9 +9699,9 @@ ${tag}`;
|
|
|
9522
9699
|
return copy;
|
|
9523
9700
|
}
|
|
9524
9701
|
async* doStream(messages, tools, signal, options) {
|
|
9525
|
-
const { baseDelay, maxDelay, maxStreamRetries
|
|
9702
|
+
const { baseDelay, maxDelay, maxStreamRetries } = this.retryConfig;
|
|
9526
9703
|
const streamRetries = maxStreamRetries ?? 2;
|
|
9527
|
-
const idleTimeoutMs =
|
|
9704
|
+
const idleTimeoutMs = this.idleTimeoutMs;
|
|
9528
9705
|
let usedNonStreamingFallback = false;
|
|
9529
9706
|
for (let attempt = 0;; attempt++) {
|
|
9530
9707
|
let emitted = false;
|
|
@@ -9543,7 +9720,7 @@ ${tag}`;
|
|
|
9543
9720
|
if (err?.stalledAfterToolCall === true && !usedNonStreamingFallback) {
|
|
9544
9721
|
usedNonStreamingFallback = true;
|
|
9545
9722
|
this.debug?.("LLM stream stalled after tool_call — falling back to non-streaming request");
|
|
9546
|
-
const fallback = await this.doNonStreaming(messages, tools, signal, options);
|
|
9723
|
+
const fallback = await this.doNonStreaming(messages, tools, signal, options, idleTimeoutMs);
|
|
9547
9724
|
this.debug?.("LLM non-streaming fallback result", { chunks: fallback.length });
|
|
9548
9725
|
for (const chunk of fallback) {
|
|
9549
9726
|
if (chunk.type === "reasoning")
|
|
@@ -9590,8 +9767,12 @@ ${tag}`;
|
|
|
9590
9767
|
signal: abortSignal
|
|
9591
9768
|
});
|
|
9592
9769
|
} catch (err) {
|
|
9593
|
-
if (err?.name === "AbortError")
|
|
9770
|
+
if (err?.name === "AbortError") {
|
|
9771
|
+
if (isTimeout()) {
|
|
9772
|
+
throw new Error(t("error.llm_timeout", { timeout: LLM_REQUEST_TIMEOUT_MS }));
|
|
9773
|
+
}
|
|
9594
9774
|
throw err;
|
|
9775
|
+
}
|
|
9595
9776
|
const wrapped = err instanceof Error ? err : new Error(String(err));
|
|
9596
9777
|
if (wrapped instanceof LlmError) {
|
|
9597
9778
|
wrapped.markTerminal();
|
|
@@ -9618,6 +9799,7 @@ ${tag}`;
|
|
|
9618
9799
|
cleanup();
|
|
9619
9800
|
throw new Error(t("error.no_response_body"));
|
|
9620
9801
|
}
|
|
9802
|
+
cleanup();
|
|
9621
9803
|
const decoder = new TextDecoder;
|
|
9622
9804
|
let buffer = "";
|
|
9623
9805
|
const toolCallAccs = new Map;
|
|
@@ -9783,7 +9965,7 @@ ${tag}`;
|
|
|
9783
9965
|
streamUsage: this.cache?.requestStreamUsage === true
|
|
9784
9966
|
};
|
|
9785
9967
|
}
|
|
9786
|
-
buildRequestSetup(signal) {
|
|
9968
|
+
buildRequestSetup(signal, totalTimeoutMs = LLM_REQUEST_TIMEOUT_MS) {
|
|
9787
9969
|
const headers = {
|
|
9788
9970
|
"Content-Type": "application/json",
|
|
9789
9971
|
"User-Agent": this.userAgent
|
|
@@ -9800,7 +9982,7 @@ ${tag}`;
|
|
|
9800
9982
|
const timeoutId = setTimeout(() => {
|
|
9801
9983
|
timedOut = true;
|
|
9802
9984
|
controller.abort();
|
|
9803
|
-
},
|
|
9985
|
+
}, totalTimeoutMs);
|
|
9804
9986
|
const abortSignal = (() => {
|
|
9805
9987
|
if (!signal)
|
|
9806
9988
|
return controller.signal;
|
|
@@ -9824,7 +10006,7 @@ ${tag}`;
|
|
|
9824
10006
|
controller
|
|
9825
10007
|
};
|
|
9826
10008
|
}
|
|
9827
|
-
async doNonStreaming(messages, tools, signal, options) {
|
|
10009
|
+
async doNonStreaming(messages, tools, signal, options, totalTimeoutMs = LLM_REQUEST_TIMEOUT_MS) {
|
|
9828
10010
|
const maxTokens = options?.maxTokens ?? this.config.maxCompletionTokens ?? DEFAULT_MAX_COMPLETION_TOKENS;
|
|
9829
10011
|
const body = buildRequestBody({
|
|
9830
10012
|
model: this.model,
|
|
@@ -9836,7 +10018,7 @@ ${tag}`;
|
|
|
9836
10018
|
reasoningStrategy: options?.reasoningStrategy,
|
|
9837
10019
|
...this.cacheHints()
|
|
9838
10020
|
});
|
|
9839
|
-
const { headers, abortSignal, cleanup, isTimeout } = this.buildRequestSetup(signal);
|
|
10021
|
+
const { headers, abortSignal, cleanup, isTimeout } = this.buildRequestSetup(signal, totalTimeoutMs);
|
|
9840
10022
|
try {
|
|
9841
10023
|
const response = await this.fetchWithRetry(`${this.config.baseUrl}/chat/completions`, {
|
|
9842
10024
|
method: "POST",
|
|
@@ -9901,7 +10083,7 @@ ${tag}`;
|
|
|
9901
10083
|
return chunks;
|
|
9902
10084
|
} catch (err) {
|
|
9903
10085
|
if (isTimeout() && err?.name === "AbortError") {
|
|
9904
|
-
throw new Error(t("error.llm_timeout", { timeout:
|
|
10086
|
+
throw new Error(t("error.llm_timeout", { timeout: totalTimeoutMs }));
|
|
9905
10087
|
}
|
|
9906
10088
|
throw err instanceof Error ? err : new Error(String(err));
|
|
9907
10089
|
} finally {
|
|
@@ -11730,6 +11912,7 @@ import { join as join20 } from "path";
|
|
|
11730
11912
|
class IndexCache {
|
|
11731
11913
|
cachePath;
|
|
11732
11914
|
cache = null;
|
|
11915
|
+
cachedBuiltAt = null;
|
|
11733
11916
|
root;
|
|
11734
11917
|
signature;
|
|
11735
11918
|
constructor(cacheDir, opts = {}) {
|
|
@@ -11751,17 +11934,21 @@ class IndexCache {
|
|
|
11751
11934
|
if (this.signature && parsed.signature !== this.currentSignature())
|
|
11752
11935
|
return null;
|
|
11753
11936
|
this.cache = parsed.result;
|
|
11937
|
+
this.cachedBuiltAt = typeof parsed.builtAt === "number" ? parsed.builtAt : null;
|
|
11754
11938
|
return this.cache;
|
|
11755
11939
|
} catch {
|
|
11756
11940
|
return null;
|
|
11757
11941
|
}
|
|
11758
11942
|
}
|
|
11759
11943
|
save(result) {
|
|
11944
|
+
const now = Date.now();
|
|
11760
11945
|
this.cache = result;
|
|
11946
|
+
this.cachedBuiltAt = now;
|
|
11761
11947
|
const envelope = {
|
|
11762
11948
|
version: CACHE_VERSION,
|
|
11763
11949
|
root: this.root ?? "",
|
|
11764
11950
|
signature: this.currentSignature(),
|
|
11951
|
+
builtAt: now,
|
|
11765
11952
|
result
|
|
11766
11953
|
};
|
|
11767
11954
|
const dir = join20(this.cachePath, "..");
|
|
@@ -11771,12 +11958,16 @@ class IndexCache {
|
|
|
11771
11958
|
}
|
|
11772
11959
|
invalidate() {
|
|
11773
11960
|
this.cache = null;
|
|
11961
|
+
this.cachedBuiltAt = null;
|
|
11774
11962
|
if (existsSync18(this.cachePath)) {
|
|
11775
11963
|
try {
|
|
11776
11964
|
rmSync(this.cachePath);
|
|
11777
11965
|
} catch {}
|
|
11778
11966
|
}
|
|
11779
11967
|
}
|
|
11968
|
+
getBuiltAt() {
|
|
11969
|
+
return this.cachedBuiltAt;
|
|
11970
|
+
}
|
|
11780
11971
|
currentSignature() {
|
|
11781
11972
|
if (!this.signature)
|
|
11782
11973
|
return "";
|
|
@@ -12104,7 +12295,11 @@ class IndexerModule {
|
|
|
12104
12295
|
cache;
|
|
12105
12296
|
baseDir;
|
|
12106
12297
|
index = null;
|
|
12107
|
-
|
|
12298
|
+
mapCache = null;
|
|
12299
|
+
buildPromise = null;
|
|
12300
|
+
indexing = false;
|
|
12301
|
+
builtAt = null;
|
|
12302
|
+
watcherActive = false;
|
|
12108
12303
|
rebuildTimeout = null;
|
|
12109
12304
|
constructor(opts) {
|
|
12110
12305
|
this.baseDir = opts.baseDir;
|
|
@@ -12114,26 +12309,38 @@ class IndexerModule {
|
|
|
12114
12309
|
signature: () => this.indexer.scanSignature()
|
|
12115
12310
|
});
|
|
12116
12311
|
}
|
|
12117
|
-
|
|
12312
|
+
buildIndex() {
|
|
12118
12313
|
if (this.index)
|
|
12119
|
-
return this.index;
|
|
12314
|
+
return Promise.resolve(this.index);
|
|
12315
|
+
if (this.buildPromise)
|
|
12316
|
+
return this.buildPromise;
|
|
12120
12317
|
const cached = this.cache.load();
|
|
12121
12318
|
if (cached) {
|
|
12122
12319
|
this.index = cached;
|
|
12123
|
-
|
|
12124
|
-
|
|
12125
|
-
try {
|
|
12126
|
-
const result = await this.indexer.walk();
|
|
12127
|
-
this.cache.save(result);
|
|
12128
|
-
this.index = result;
|
|
12129
|
-
return result;
|
|
12130
|
-
} catch {
|
|
12131
|
-
return null;
|
|
12320
|
+
this.builtAt = this.cache.getBuiltAt();
|
|
12321
|
+
return Promise.resolve(cached);
|
|
12132
12322
|
}
|
|
12323
|
+
this.indexing = true;
|
|
12324
|
+
this.buildPromise = (async () => {
|
|
12325
|
+
try {
|
|
12326
|
+
const result = await this.indexer.walk();
|
|
12327
|
+
this.cache.save(result);
|
|
12328
|
+
this.index = result;
|
|
12329
|
+
this.builtAt = Date.now();
|
|
12330
|
+
return result;
|
|
12331
|
+
} catch {
|
|
12332
|
+
return null;
|
|
12333
|
+
} finally {
|
|
12334
|
+
this.indexing = false;
|
|
12335
|
+
this.buildPromise = null;
|
|
12336
|
+
}
|
|
12337
|
+
})();
|
|
12338
|
+
return this.buildPromise;
|
|
12133
12339
|
}
|
|
12134
12340
|
async refresh() {
|
|
12135
12341
|
this.cache.invalidate();
|
|
12136
12342
|
this.index = null;
|
|
12343
|
+
this.builtAt = null;
|
|
12137
12344
|
return this.buildIndex();
|
|
12138
12345
|
}
|
|
12139
12346
|
find(query) {
|
|
@@ -12173,9 +12380,11 @@ class IndexerModule {
|
|
|
12173
12380
|
this.refresh().catch(() => {});
|
|
12174
12381
|
}, 2000);
|
|
12175
12382
|
});
|
|
12383
|
+
this.watcherActive = true;
|
|
12176
12384
|
} catch {}
|
|
12177
12385
|
}
|
|
12178
12386
|
unwatch() {
|
|
12387
|
+
this.watcherActive = false;
|
|
12179
12388
|
if (this.rebuildTimeout) {
|
|
12180
12389
|
clearTimeout(this.rebuildTimeout);
|
|
12181
12390
|
this.rebuildTimeout = null;
|
|
@@ -12194,19 +12403,47 @@ class IndexerModule {
|
|
|
12194
12403
|
kind: "project-map"
|
|
12195
12404
|
};
|
|
12196
12405
|
}
|
|
12197
|
-
|
|
12198
|
-
if (!this.index)
|
|
12199
|
-
return
|
|
12200
|
-
|
|
12201
|
-
|
|
12406
|
+
mapInfo() {
|
|
12407
|
+
if (!this.index) {
|
|
12408
|
+
return {
|
|
12409
|
+
indexed: false,
|
|
12410
|
+
indexing: this.indexing,
|
|
12411
|
+
files: 0,
|
|
12412
|
+
symbols: 0,
|
|
12413
|
+
tokens: 0,
|
|
12414
|
+
updatedAt: this.builtAt,
|
|
12415
|
+
languages: [],
|
|
12416
|
+
watcher: this.watcherActive
|
|
12417
|
+
};
|
|
12418
|
+
}
|
|
12419
|
+
if (this.mapCache && this.mapCache.index === this.index) {
|
|
12420
|
+
return { ...this.mapCache.value, indexing: this.indexing, watcher: this.watcherActive };
|
|
12421
|
+
}
|
|
12202
12422
|
const symbols = this.index.files.reduce((n, f) => n + f.exports.length, 0);
|
|
12423
|
+
const langMap = new Map;
|
|
12424
|
+
for (const f of this.index.files) {
|
|
12425
|
+
const lang = f.language;
|
|
12426
|
+
const cur = langMap.get(lang) ?? { files: 0, symbols: 0 };
|
|
12427
|
+
cur.files += 1;
|
|
12428
|
+
cur.symbols += f.exports.length;
|
|
12429
|
+
langMap.set(lang, cur);
|
|
12430
|
+
}
|
|
12431
|
+
const languages = [...langMap.entries()].map(([language, v]) => ({ language, ...v })).sort((a, b) => b.files - a.files || a.language.localeCompare(b.language)).slice(0, 8);
|
|
12203
12432
|
const value = {
|
|
12433
|
+
indexed: true,
|
|
12204
12434
|
files: this.index.files.length,
|
|
12205
12435
|
symbols,
|
|
12206
|
-
tokens: this.estimateTokens(this.formatMap(this.index))
|
|
12436
|
+
tokens: this.estimateTokens(this.formatMap(this.index)),
|
|
12437
|
+
updatedAt: this.builtAt,
|
|
12438
|
+
languages,
|
|
12439
|
+
watcher: this.watcherActive
|
|
12207
12440
|
};
|
|
12208
|
-
this.
|
|
12209
|
-
return value;
|
|
12441
|
+
this.mapCache = { index: this.index, value };
|
|
12442
|
+
return { ...value, indexing: this.indexing };
|
|
12443
|
+
}
|
|
12444
|
+
stats() {
|
|
12445
|
+
const info = this.mapInfo();
|
|
12446
|
+
return info.indexed ? { files: info.files, symbols: info.symbols, tokens: info.tokens } : null;
|
|
12210
12447
|
}
|
|
12211
12448
|
getToolDefinitions() {
|
|
12212
12449
|
return [this.createProjectMapTool()];
|
|
@@ -16828,6 +17065,15 @@ class SessionLogger {
|
|
|
16828
17065
|
...data
|
|
16829
17066
|
});
|
|
16830
17067
|
}
|
|
17068
|
+
logClaimRetry(data) {
|
|
17069
|
+
this.session?.appendLog({
|
|
17070
|
+
ts: new Date().toISOString(),
|
|
17071
|
+
type: "claim_retry",
|
|
17072
|
+
content: data.reason,
|
|
17073
|
+
kind: data.kind,
|
|
17074
|
+
iteration: data.iteration
|
|
17075
|
+
});
|
|
17076
|
+
}
|
|
16831
17077
|
logCritic(event, data = {}) {
|
|
16832
17078
|
this.session?.appendLog({
|
|
16833
17079
|
ts: new Date().toISOString(),
|
|
@@ -19418,8 +19664,17 @@ function isFilePathLike(s) {
|
|
|
19418
19664
|
return true;
|
|
19419
19665
|
}
|
|
19420
19666
|
function extractFileLikeTokens(text) {
|
|
19421
|
-
const
|
|
19422
|
-
|
|
19667
|
+
const out = new Set;
|
|
19668
|
+
const re = new RegExp(FILE_TOKEN_RE.source, "gi");
|
|
19669
|
+
for (const m of text.matchAll(re)) {
|
|
19670
|
+
const token = m[0];
|
|
19671
|
+
const after = text[(m.index ?? 0) + token.length];
|
|
19672
|
+
if (after === "(")
|
|
19673
|
+
continue;
|
|
19674
|
+
if (isFilePathLike(token))
|
|
19675
|
+
out.add(token);
|
|
19676
|
+
}
|
|
19677
|
+
return [...out];
|
|
19423
19678
|
}
|
|
19424
19679
|
function stripUrls(text) {
|
|
19425
19680
|
return text.replace(/\bhttps?:\/\/[^\s"'<>]+/gi, " ");
|
|
@@ -20747,6 +21002,7 @@ function createLoopState() {
|
|
|
20747
21002
|
criticCalls: 0,
|
|
20748
21003
|
criticRepairCycles: 0,
|
|
20749
21004
|
mutationsSinceCritic: 0,
|
|
21005
|
+
criticHistoryMark: 0,
|
|
20750
21006
|
seenCriticIssues: new Set,
|
|
20751
21007
|
emptyResponseRetries: 0,
|
|
20752
21008
|
emptyResponseExhausted: false,
|
|
@@ -21557,6 +21813,7 @@ class HallucinationGate {
|
|
|
21557
21813
|
return { action: "warn" };
|
|
21558
21814
|
}
|
|
21559
21815
|
if (result.status === "retry") {
|
|
21816
|
+
this.retractDraft(deps, result.kind ?? "retry", result.reason ?? "");
|
|
21560
21817
|
return this.applyRetry(deps, textContent, result);
|
|
21561
21818
|
}
|
|
21562
21819
|
const claimRetry = this.checkSuccessClaim(deps, textContent);
|
|
@@ -21605,30 +21862,35 @@ class HallucinationGate {
|
|
|
21605
21862
|
return this.retryClaim(deps, textContent, t("hall.claim_contradiction", {
|
|
21606
21863
|
tool: contradiction.tool,
|
|
21607
21864
|
snippet: contradiction.snippet
|
|
21608
|
-
}), `Success-claim contradiction: ${contradiction.tool}: ${contradiction.snippet}
|
|
21865
|
+
}), `Success-claim contradiction: ${contradiction.tool}: ${contradiction.snippet}`, "success");
|
|
21609
21866
|
}
|
|
21610
21867
|
}
|
|
21611
21868
|
if (state.artifactClaimRetries < MAX_CLAIM_CHECK_RETRIES && deps.artifacts && deps.artifacts.length > 0) {
|
|
21612
21869
|
const artifact = checkArtifactClaims(text, deps.artifacts);
|
|
21613
21870
|
if (artifact) {
|
|
21614
21871
|
state.artifactClaimRetries++;
|
|
21615
|
-
return this.retryClaim(deps, textContent, t("hall.claim_artifact", { path: artifact.path, detail: artifact.snippet }), `Artifact-claim contradiction: ${artifact.snippet}
|
|
21872
|
+
return this.retryClaim(deps, textContent, t("hall.claim_artifact", { path: artifact.path, detail: artifact.snippet }), `Artifact-claim contradiction: ${artifact.snippet}`, "artifact");
|
|
21616
21873
|
}
|
|
21617
21874
|
}
|
|
21618
21875
|
if (state.outputClaimRetries < MAX_CLAIM_CHECK_RETRIES) {
|
|
21619
21876
|
const unverified = checkUnverifiedOutput(text, evidence);
|
|
21620
21877
|
if (unverified) {
|
|
21621
21878
|
state.outputClaimRetries++;
|
|
21622
|
-
return this.retryClaim(deps, textContent, t("hall.claim_output_unverified", { detail: unverified.snippet }), `Unverified output: ${unverified.snippet}
|
|
21879
|
+
return this.retryClaim(deps, textContent, t("hall.claim_output_unverified", { detail: unverified.snippet }), `Unverified output: ${unverified.snippet}`, "output");
|
|
21623
21880
|
}
|
|
21624
21881
|
}
|
|
21625
21882
|
return null;
|
|
21626
21883
|
}
|
|
21627
|
-
|
|
21884
|
+
retractDraft(deps, kind, reason) {
|
|
21885
|
+
deps.onMeta?.(t("ui.draft_retracted"), "retract");
|
|
21886
|
+
deps.slog?.logClaimRetry({ kind, reason, iteration: deps.state.iteration });
|
|
21887
|
+
}
|
|
21888
|
+
retryClaim(deps, textContent, reason, logLine, kind) {
|
|
21628
21889
|
const { state, logger: logger3 } = deps;
|
|
21629
21890
|
state.iterHallucinationRetry = true;
|
|
21630
21891
|
state.suppressRepetitionRetry = true;
|
|
21631
21892
|
logger3.diagnostic("warn", logLine);
|
|
21893
|
+
this.retractDraft(deps, kind, reason);
|
|
21632
21894
|
if (textContent) {
|
|
21633
21895
|
deps.contextManager.addMessage({ role: "assistant", content: textContent });
|
|
21634
21896
|
}
|
|
@@ -21687,27 +21949,30 @@ function resolveArtifactStates(baseDir, response) {
|
|
|
21687
21949
|
const rel = relative6(baseDir, abs);
|
|
21688
21950
|
if (rel.startsWith("..") || isAbsolute4(rel))
|
|
21689
21951
|
continue;
|
|
21690
|
-
const state = resolveOneArtifact(token,
|
|
21952
|
+
const state = resolveOneArtifact(token, baseDir);
|
|
21691
21953
|
if (state)
|
|
21692
21954
|
out.push(state);
|
|
21693
21955
|
}
|
|
21694
21956
|
return out;
|
|
21695
21957
|
}
|
|
21696
|
-
function resolveOneArtifact(token,
|
|
21958
|
+
function resolveOneArtifact(token, baseDir) {
|
|
21959
|
+
const found = findExistingFile(baseDir, token);
|
|
21960
|
+
if (!found)
|
|
21961
|
+
return { path: token, exists: false };
|
|
21697
21962
|
let size;
|
|
21698
21963
|
try {
|
|
21699
|
-
const st = statSync8(
|
|
21964
|
+
const st = statSync8(found);
|
|
21700
21965
|
if (!st.isFile())
|
|
21701
21966
|
return { path: token, exists: false };
|
|
21702
21967
|
size = st.size;
|
|
21703
21968
|
} catch {
|
|
21704
|
-
return { path: token, exists:
|
|
21969
|
+
return { path: token, exists: true };
|
|
21705
21970
|
}
|
|
21706
21971
|
if (size > MAX_BYTES)
|
|
21707
21972
|
return { path: token, exists: true };
|
|
21708
21973
|
let content;
|
|
21709
21974
|
try {
|
|
21710
|
-
content = readFileSync25(
|
|
21975
|
+
content = readFileSync25(found, "utf-8");
|
|
21711
21976
|
} catch {
|
|
21712
21977
|
return { path: token, exists: true };
|
|
21713
21978
|
}
|
|
@@ -21717,6 +21982,7 @@ var MAX_PATHS = 10, MAX_BYTES, JSON_EXTS, XML_EXTS;
|
|
|
21717
21982
|
var init_artifact_resolve = __esm(() => {
|
|
21718
21983
|
init_js_identifiers();
|
|
21719
21984
|
init_syntax_validator();
|
|
21985
|
+
init_auditor();
|
|
21720
21986
|
MAX_BYTES = 500 * 1024;
|
|
21721
21987
|
JSON_EXTS = new Set([".json", ".jsonc"]);
|
|
21722
21988
|
XML_EXTS = new Set([".xml", ".drawio", ".svg", ".xaml", ".plist", ".iml"]);
|
|
@@ -21754,6 +22020,7 @@ class AuditGate {
|
|
|
21754
22020
|
return { action: "exhausted", summary: audit.summary };
|
|
21755
22021
|
}
|
|
21756
22022
|
state.suppressRepetitionRetry = true;
|
|
22023
|
+
deps.onMeta?.(t("ui.draft_retracted"), "retract");
|
|
21757
22024
|
return { action: "reject" };
|
|
21758
22025
|
}
|
|
21759
22026
|
}
|
|
@@ -21765,10 +22032,17 @@ var init_audit_gate = __esm(() => {
|
|
|
21765
22032
|
function issueSignature(issue) {
|
|
21766
22033
|
return `${issue.severity}:${issue.claim}`.toLowerCase().replace(/\s+/g, " ").slice(0, 120);
|
|
21767
22034
|
}
|
|
22035
|
+
function normalizedSignature(issue) {
|
|
22036
|
+
const body = `${issue.severity}:${issue.claim}`.toLowerCase().replace(/[^a-z0-9а-яё\s]/gi, " ").replace(/\s+/g, " ").trim().slice(0, 120);
|
|
22037
|
+
return `~${body}`;
|
|
22038
|
+
}
|
|
21768
22039
|
function renderIssues(issues) {
|
|
21769
22040
|
return issues.slice(0, 5).map((i) => `- [${i.severity}] ${i.problem || i.claim}${i.suggestion ? ` → ${i.suggestion}` : ""}`).join(`
|
|
21770
22041
|
`);
|
|
21771
22042
|
}
|
|
22043
|
+
function isRepeatedVerdict(state, issues) {
|
|
22044
|
+
return issues.length > 0 && issues.every((i) => state.seenCriticIssues.has(normalizedSignature(i)));
|
|
22045
|
+
}
|
|
21772
22046
|
|
|
21773
22047
|
class CriticGate {
|
|
21774
22048
|
opts;
|
|
@@ -21784,18 +22058,26 @@ class CriticGate {
|
|
|
21784
22058
|
const verdict = await this.verifySafely(deps);
|
|
21785
22059
|
if (!verdict || verdict.status === "ok")
|
|
21786
22060
|
return "pass";
|
|
22061
|
+
const isRepeat = isRepeatedVerdict(deps.state, verdict.issues);
|
|
21787
22062
|
const fresh = this.takeFreshIssues(deps.state, verdict.issues);
|
|
21788
22063
|
if (fresh.length === 0)
|
|
21789
22064
|
return "pass";
|
|
21790
22065
|
this.logVerdict(deps, verdict, fresh.length);
|
|
21791
|
-
|
|
21792
|
-
|
|
22066
|
+
if (!this.shouldRepair(deps, verdict, isRepeat)) {
|
|
22067
|
+
this.injectRepeatOrAdvice(deps, fresh, isRepeat);
|
|
22068
|
+
return "pass";
|
|
22069
|
+
}
|
|
22070
|
+
this.injectRepair(deps, verdict, fresh);
|
|
21793
22071
|
return "retry";
|
|
21794
22072
|
}
|
|
22073
|
+
shouldRepair(deps, verdict, isRepeat) {
|
|
22074
|
+
return verdict.status === "critical" && deps.beforeFinish && deps.state.criticRepairCycles < this.opts.maxRepairCycles && !isRepeat;
|
|
22075
|
+
}
|
|
21795
22076
|
async verifySafely(deps) {
|
|
21796
22077
|
try {
|
|
21797
22078
|
return await this.opts.verify(this.opts.buildPack(deps), this.opts.signal);
|
|
21798
|
-
} catch {
|
|
22079
|
+
} catch (err) {
|
|
22080
|
+
deps.logger.diagnostic("warn", `Critic verify failed (fail-open): ${String(err?.message ?? err)}`);
|
|
21799
22081
|
return null;
|
|
21800
22082
|
}
|
|
21801
22083
|
}
|
|
@@ -21804,9 +22086,8 @@ class CriticGate {
|
|
|
21804
22086
|
return false;
|
|
21805
22087
|
if (deps.state.criticCalls >= this.opts.maxCalls)
|
|
21806
22088
|
return false;
|
|
21807
|
-
if (!deps.beforeFinish
|
|
22089
|
+
if (!deps.beforeFinish)
|
|
21808
22090
|
return false;
|
|
21809
|
-
}
|
|
21810
22091
|
return deps.toolEvents.length !== 0;
|
|
21811
22092
|
}
|
|
21812
22093
|
takeFreshIssues(state, issues) {
|
|
@@ -21816,6 +22097,7 @@ class CriticGate {
|
|
|
21816
22097
|
if (state.seenCriticIssues.has(sig))
|
|
21817
22098
|
continue;
|
|
21818
22099
|
state.seenCriticIssues.add(sig);
|
|
22100
|
+
state.seenCriticIssues.add(normalizedSignature(issue));
|
|
21819
22101
|
fresh.push(issue);
|
|
21820
22102
|
}
|
|
21821
22103
|
return fresh;
|
|
@@ -21836,15 +22118,15 @@ class CriticGate {
|
|
|
21836
22118
|
});
|
|
21837
22119
|
deps.logger.diagnostic("info", `Critic ${verdict.status}: ${verdict.summary} (${count} issue(s))`);
|
|
21838
22120
|
}
|
|
21839
|
-
|
|
21840
|
-
|
|
21841
|
-
|
|
21842
|
-
|
|
21843
|
-
|
|
21844
|
-
|
|
21845
|
-
|
|
21846
|
-
}
|
|
22121
|
+
injectRepeatOrAdvice(deps, fresh, isRepeat) {
|
|
22122
|
+
deps.contextManager.addMessage({
|
|
22123
|
+
role: "user",
|
|
22124
|
+
content: `<system-summary>${isRepeat ? t("critic.repeat", { issues: renderIssues(fresh) }) : t("critic.advice", { issues: renderIssues(fresh) })}</system-summary>`
|
|
22125
|
+
});
|
|
22126
|
+
}
|
|
22127
|
+
injectRepair(deps, verdict, fresh) {
|
|
21847
22128
|
deps.state.criticRepairCycles++;
|
|
22129
|
+
deps.onMeta?.(t("ui.draft_retracted"), "retract");
|
|
21848
22130
|
deps.slog.logCritic("critic_escalation", {
|
|
21849
22131
|
summary: verdict.summary,
|
|
21850
22132
|
cycle: deps.state.criticRepairCycles
|
|
@@ -21858,9 +22140,11 @@ class CriticGate {
|
|
|
21858
22140
|
});
|
|
21859
22141
|
}
|
|
21860
22142
|
}
|
|
21861
|
-
function gatherTurnToolEvents(contextManager) {
|
|
22143
|
+
function gatherTurnToolEvents(contextManager, fromIndex = 0) {
|
|
21862
22144
|
const events = [];
|
|
21863
|
-
|
|
22145
|
+
const history = contextManager.getActiveHistory();
|
|
22146
|
+
const start = Math.max(0, Math.min(fromIndex, history.length));
|
|
22147
|
+
for (const m of history.slice(start)) {
|
|
21864
22148
|
if (m.role !== "tool" || !m.name)
|
|
21865
22149
|
continue;
|
|
21866
22150
|
const output = typeof m.content === "string" ? m.content.slice(-MAX_EVENT_OUTPUT_CHARS) : undefined;
|
|
@@ -21868,7 +22152,7 @@ function gatherTurnToolEvents(contextManager) {
|
|
|
21868
22152
|
}
|
|
21869
22153
|
return events;
|
|
21870
22154
|
}
|
|
21871
|
-
var
|
|
22155
|
+
var MAX_EVENT_OUTPUT_CHARS = 600;
|
|
21872
22156
|
var init_critic_gate = __esm(() => {
|
|
21873
22157
|
init_i18n();
|
|
21874
22158
|
});
|
|
@@ -22055,7 +22339,9 @@ ${f.excerpt}`);
|
|
|
22055
22339
|
pack.toolDigest || "(empty)",
|
|
22056
22340
|
files.length > 0 ? `FILE CONTENTS:
|
|
22057
22341
|
${files.join(`
|
|
22058
|
-
`)}` : ""
|
|
22342
|
+
`)}` : "",
|
|
22343
|
+
pack.answerExcerpt ? `ANSWER (the agent's chat delivery — may satisfy the task without files):
|
|
22344
|
+
${pack.answerExcerpt}` : ""
|
|
22059
22345
|
].filter(Boolean).join(`
|
|
22060
22346
|
`);
|
|
22061
22347
|
}
|
|
@@ -22109,6 +22395,7 @@ Rules:
|
|
|
22109
22395
|
- Flag ONLY issues that can be verified from the evidence provided. Do not speculate or invent.
|
|
22110
22396
|
- Do not nitpick style, wording, or preferences unless the user's task demanded it.
|
|
22111
22397
|
- If the evidence is insufficient to judge, return status "ok" (prefer silence over noise).
|
|
22398
|
+
- A chat-delivered answer (ANSWER section) can fully satisfy the task without any files or commands; absence of changed files is NOT itself an issue when ANSWER contains the requested result.
|
|
22112
22399
|
- "critical" = the task's stated result is demonstrably not delivered. "concern" = a concrete, checkable gap. "info" = minor.
|
|
22113
22400
|
- Every issue must reference concrete evidence (file, command, output).
|
|
22114
22401
|
Reply with JSON ONLY:
|
|
@@ -22123,8 +22410,12 @@ var init_client = __esm(() => {
|
|
|
22123
22410
|
// src/modules/critic/provider.ts
|
|
22124
22411
|
function buildCriticProvider(critic, config, defaultProvider, deps) {
|
|
22125
22412
|
const model = critic?.model?.trim();
|
|
22126
|
-
if (!model)
|
|
22413
|
+
if (!model) {
|
|
22414
|
+
if (critic?.provider && Object.keys(critic.provider).length > 0) {
|
|
22415
|
+
deps?.logger?.warn("critic.provider is set without critic.model — provider ignored, using active provider");
|
|
22416
|
+
}
|
|
22127
22417
|
return defaultProvider;
|
|
22418
|
+
}
|
|
22128
22419
|
try {
|
|
22129
22420
|
const providerCfg = critic?.provider ?? {
|
|
22130
22421
|
type: "openai-compat",
|
|
@@ -22182,7 +22473,8 @@ function buildEvidencePack(input) {
|
|
|
22182
22473
|
commands,
|
|
22183
22474
|
toolDigest: digest.slice(-MAX_DIGEST_LINES).join(`
|
|
22184
22475
|
`),
|
|
22185
|
-
fileExcerpts: input.baseDir ? readExcerpts(input.baseDir, [...changedFiles]) : undefined
|
|
22476
|
+
fileExcerpts: input.baseDir ? readExcerpts(input.baseDir, [...changedFiles]) : undefined,
|
|
22477
|
+
answerExcerpt: input.answerText ? redactContextSecrets(input.answerText).slice(0, MAX_ANSWER_CHARS) : undefined
|
|
22186
22478
|
};
|
|
22187
22479
|
}
|
|
22188
22480
|
function digestLine(ev) {
|
|
@@ -22203,7 +22495,7 @@ function commandOf(ev) {
|
|
|
22203
22495
|
return;
|
|
22204
22496
|
return {
|
|
22205
22497
|
command: ev.args.command,
|
|
22206
|
-
exitCode: ev.ok === false ? 1 :
|
|
22498
|
+
exitCode: ev.ok === false ? 1 : undefined,
|
|
22207
22499
|
output: ev.output?.slice(-MAX_DIGEST_OUTPUT_CHARS)
|
|
22208
22500
|
};
|
|
22209
22501
|
}
|
|
@@ -22221,9 +22513,10 @@ function readExcerpts(baseDir, paths) {
|
|
|
22221
22513
|
}
|
|
22222
22514
|
return out;
|
|
22223
22515
|
}
|
|
22224
|
-
var MUTATING_TOOLS, MAX_DIGEST_LINES = 40, MAX_DIGEST_OUTPUT_CHARS = 300, MAX_EXCERPT_FILES = 5, MAX_EXCERPT_CHARS = 1200;
|
|
22516
|
+
var MUTATING_TOOLS, MAX_DIGEST_LINES = 40, MAX_DIGEST_OUTPUT_CHARS = 300, MAX_EXCERPT_FILES = 5, MAX_EXCERPT_CHARS = 1200, MAX_ANSWER_CHARS = 2000;
|
|
22225
22517
|
var init_evidence = __esm(() => {
|
|
22226
22518
|
init_tool_output();
|
|
22519
|
+
init_context_log();
|
|
22227
22520
|
MUTATING_TOOLS = new Set(["write_file", "edit_file", "delete_file", "move_file", "create_dir"]);
|
|
22228
22521
|
});
|
|
22229
22522
|
|
|
@@ -22918,6 +23211,7 @@ class Agent {
|
|
|
22918
23211
|
} = this.deps;
|
|
22919
23212
|
this.abortController = new AbortController;
|
|
22920
23213
|
const state = createLoopState();
|
|
23214
|
+
state.criticHistoryMark = contextManager.getActiveHistory().length;
|
|
22921
23215
|
const compactionService = new CompactionService({
|
|
22922
23216
|
contextManager,
|
|
22923
23217
|
logger: logger3,
|
|
@@ -22975,7 +23269,8 @@ class Agent {
|
|
|
22975
23269
|
task: d.task,
|
|
22976
23270
|
planSummary: d.planSummary,
|
|
22977
23271
|
toolEvents: d.toolEvents,
|
|
22978
|
-
baseDir: this.deps.baseDir
|
|
23272
|
+
baseDir: this.deps.baseDir,
|
|
23273
|
+
answerText: d.answerText
|
|
22979
23274
|
}),
|
|
22980
23275
|
signal: this.abortController?.signal
|
|
22981
23276
|
});
|
|
@@ -23256,7 +23551,8 @@ class Agent {
|
|
|
23256
23551
|
onMeta,
|
|
23257
23552
|
onChunk,
|
|
23258
23553
|
verbose: resolveVerbosity(this.deps.config.ui) === "verbose",
|
|
23259
|
-
artifacts: resolveArtifactStates(this.deps.baseDir, textContent)
|
|
23554
|
+
artifacts: resolveArtifactStates(this.deps.baseDir, textContent),
|
|
23555
|
+
slog
|
|
23260
23556
|
});
|
|
23261
23557
|
if (verdict.action === "block") {
|
|
23262
23558
|
return {
|
|
@@ -23309,8 +23605,10 @@ class Agent {
|
|
|
23309
23605
|
slog,
|
|
23310
23606
|
task: input,
|
|
23311
23607
|
planSummary: this.deps.planSummary?.() ?? undefined,
|
|
23312
|
-
toolEvents: gatherTurnToolEvents(contextManager),
|
|
23313
|
-
beforeFinish: true
|
|
23608
|
+
toolEvents: gatherTurnToolEvents(contextManager, state.criticHistoryMark),
|
|
23609
|
+
beforeFinish: true,
|
|
23610
|
+
answerText: state.mutationsSinceCritic === 0 ? textContent : undefined,
|
|
23611
|
+
onMeta
|
|
23314
23612
|
});
|
|
23315
23613
|
if (criticAction === "retry") {
|
|
23316
23614
|
state.suppressRepetitionRetry = true;
|
|
@@ -23322,7 +23620,8 @@ class Agent {
|
|
|
23322
23620
|
maxIterations: config.maxToolIterations,
|
|
23323
23621
|
logger: logger3,
|
|
23324
23622
|
contextManager,
|
|
23325
|
-
slog
|
|
23623
|
+
slog,
|
|
23624
|
+
onMeta
|
|
23326
23625
|
});
|
|
23327
23626
|
if (auditVerdict === null || auditVerdict.action === "pass") {
|
|
23328
23627
|
state.finalAnswerAccepted = true;
|
|
@@ -34932,6 +35231,40 @@ var init_box = __esm(() => {
|
|
|
34932
35231
|
});
|
|
34933
35232
|
|
|
34934
35233
|
// src/modules/setup/answers.ts
|
|
35234
|
+
function securityDetailFromConfig(s) {
|
|
35235
|
+
return {
|
|
35236
|
+
bash: {
|
|
35237
|
+
blacklist: [...s.bash.blacklist],
|
|
35238
|
+
whitelist: [...s.bash.whitelist],
|
|
35239
|
+
logCommands: s.bash.logCommands
|
|
35240
|
+
},
|
|
35241
|
+
paths: { denied: [...s.paths.denied], allowed: [...s.paths.allowed] },
|
|
35242
|
+
flags: {
|
|
35243
|
+
dangerousFlags: [...s.bash.dangerousFlags],
|
|
35244
|
+
dangerousOperators: [...s.bash.dangerousOperators]
|
|
35245
|
+
},
|
|
35246
|
+
network: {
|
|
35247
|
+
enabled: s.network.enabled,
|
|
35248
|
+
deniedDomains: [...s.network.deniedDomains],
|
|
35249
|
+
allowedDomains: [...s.network.allowedDomains],
|
|
35250
|
+
requestTimeout: s.network.requestTimeout,
|
|
35251
|
+
maxResponseSize: s.network.maxResponseSize
|
|
35252
|
+
},
|
|
35253
|
+
advanced: {
|
|
35254
|
+
maxRecursionDepth: s.maxRecursionDepth,
|
|
35255
|
+
maxFileOperations: s.maxFileOperations,
|
|
35256
|
+
maxRequestsPerMinute: s.rateLimits.maxRequestsPerMinute,
|
|
35257
|
+
maxParallelTasks: s.rateLimits.maxParallelTasks,
|
|
35258
|
+
contentScanEnabled: s.contentScan.enabled,
|
|
35259
|
+
sessionEncryptionEnabled: s.sessionEncryption.enabled,
|
|
35260
|
+
auditNotifierEnabled: s.auditNotifier.enabled,
|
|
35261
|
+
auditNotifierMinSeverity: s.auditNotifier.minSeverity ?? "medium",
|
|
35262
|
+
auditNotifierEventTypes: [...s.auditNotifier.eventTypes ?? []],
|
|
35263
|
+
auditNotifierMaxRetries: s.auditNotifier.maxRetries ?? 3,
|
|
35264
|
+
auditNotifierWebhookTimeout: s.auditNotifier.webhookTimeout ?? 5000
|
|
35265
|
+
}
|
|
35266
|
+
};
|
|
35267
|
+
}
|
|
34935
35268
|
function buildSetupDisplay() {
|
|
34936
35269
|
const providers = BUILTIN_PROVIDERS.map((spec) => {
|
|
34937
35270
|
const option = { type: spec.type, label: spec.label };
|
|
@@ -35321,20 +35654,62 @@ function applyAnswersToConfig(config, a) {
|
|
|
35321
35654
|
config.provider.entries = next;
|
|
35322
35655
|
config.provider.active = updated.label || updated.type;
|
|
35323
35656
|
}
|
|
35324
|
-
|
|
35325
|
-
config.security.enabled = true;
|
|
35326
|
-
config.security.bash.enabled = true;
|
|
35327
|
-
config.security.bash.blockDangerousFlags = a.securityFlagsBlock || config.security.bash.blockDangerousFlags;
|
|
35328
|
-
if (a.securityBashBlock) {
|
|
35329
|
-
config.security.bash.blacklist = [
|
|
35330
|
-
...new Set([...config.security.bash.blacklist, ...SECURITY_BASH_BLOCKLIST])
|
|
35331
|
-
];
|
|
35332
|
-
}
|
|
35333
|
-
config.security.paths.enabled = a.securityPathsRestrict;
|
|
35334
|
-
}
|
|
35657
|
+
applySecurityAnswers(config, a);
|
|
35335
35658
|
return config;
|
|
35336
35659
|
}
|
|
35337
|
-
|
|
35660
|
+
function applySecurityAnswers(config, a) {
|
|
35661
|
+
if (!config.security)
|
|
35662
|
+
return;
|
|
35663
|
+
config.security.enabled = true;
|
|
35664
|
+
config.security.bash.enabled = true;
|
|
35665
|
+
config.security.bash.blockDangerousFlags = a.securityFlagsBlock || config.security.bash.blockDangerousFlags;
|
|
35666
|
+
if (a.securityBashBlock) {
|
|
35667
|
+
config.security.bash.blacklist = [
|
|
35668
|
+
...new Set([...config.security.bash.blacklist, ...SECURITY_BASH_BLOCKLIST])
|
|
35669
|
+
];
|
|
35670
|
+
}
|
|
35671
|
+
config.security.paths.enabled = a.securityPathsRestrict;
|
|
35672
|
+
if (a.securityDetail)
|
|
35673
|
+
applySecurityDetail(config.security, a.securityDetail);
|
|
35674
|
+
}
|
|
35675
|
+
function readPath(obj, path) {
|
|
35676
|
+
let cur = obj;
|
|
35677
|
+
for (const key of path.split(".")) {
|
|
35678
|
+
if (cur === null || typeof cur !== "object")
|
|
35679
|
+
return;
|
|
35680
|
+
cur = cur[key];
|
|
35681
|
+
}
|
|
35682
|
+
return cur;
|
|
35683
|
+
}
|
|
35684
|
+
function writePath(obj, path, value) {
|
|
35685
|
+
const keys = path.split(".");
|
|
35686
|
+
let cur = obj;
|
|
35687
|
+
for (let i = 0;i < keys.length - 1; i++) {
|
|
35688
|
+
if (cur[keys[i]] === null || typeof cur[keys[i]] !== "object")
|
|
35689
|
+
cur[keys[i]] = {};
|
|
35690
|
+
cur = cur[keys[i]];
|
|
35691
|
+
}
|
|
35692
|
+
cur[keys[keys.length - 1]] = value;
|
|
35693
|
+
}
|
|
35694
|
+
function isFieldValue(v, kind) {
|
|
35695
|
+
if (kind === "list")
|
|
35696
|
+
return Array.isArray(v) && v.every((x) => typeof x === "string");
|
|
35697
|
+
if (kind === "bool")
|
|
35698
|
+
return typeof v === "boolean";
|
|
35699
|
+
if (kind === "int")
|
|
35700
|
+
return typeof v === "number" && Number.isInteger(v) && v > 0;
|
|
35701
|
+
return typeof v === "string" && SEVERITIES2.includes(v);
|
|
35702
|
+
}
|
|
35703
|
+
function applySecurityDetail(security, detail) {
|
|
35704
|
+
const target = security;
|
|
35705
|
+
for (const field of DETAIL_FIELDS) {
|
|
35706
|
+
const value = readPath(detail, field.from);
|
|
35707
|
+
if (!isFieldValue(value, field.kind))
|
|
35708
|
+
continue;
|
|
35709
|
+
writePath(target, field.to, Array.isArray(value) ? [...value] : value);
|
|
35710
|
+
}
|
|
35711
|
+
}
|
|
35712
|
+
var SECURITY_BASH_BLOCKLIST, SEVERITIES2, DETAIL_FIELDS;
|
|
35338
35713
|
var init_apply = __esm(() => {
|
|
35339
35714
|
SECURITY_BASH_BLOCKLIST = [
|
|
35340
35715
|
"rm",
|
|
@@ -35354,6 +35729,32 @@ var init_apply = __esm(() => {
|
|
|
35354
35729
|
"shutdown",
|
|
35355
35730
|
"reboot"
|
|
35356
35731
|
];
|
|
35732
|
+
SEVERITIES2 = ["low", "medium", "high", "critical"];
|
|
35733
|
+
DETAIL_FIELDS = [
|
|
35734
|
+
{ from: "bash.blacklist", to: "bash.blacklist", kind: "list" },
|
|
35735
|
+
{ from: "bash.whitelist", to: "bash.whitelist", kind: "list" },
|
|
35736
|
+
{ from: "bash.logCommands", to: "bash.logCommands", kind: "bool" },
|
|
35737
|
+
{ from: "paths.denied", to: "paths.denied", kind: "list" },
|
|
35738
|
+
{ from: "paths.allowed", to: "paths.allowed", kind: "list" },
|
|
35739
|
+
{ from: "flags.dangerousFlags", to: "bash.dangerousFlags", kind: "list" },
|
|
35740
|
+
{ from: "flags.dangerousOperators", to: "bash.dangerousOperators", kind: "list" },
|
|
35741
|
+
{ from: "network.enabled", to: "network.enabled", kind: "bool" },
|
|
35742
|
+
{ from: "network.deniedDomains", to: "network.deniedDomains", kind: "list" },
|
|
35743
|
+
{ from: "network.allowedDomains", to: "network.allowedDomains", kind: "list" },
|
|
35744
|
+
{ from: "network.requestTimeout", to: "network.requestTimeout", kind: "int" },
|
|
35745
|
+
{ from: "network.maxResponseSize", to: "network.maxResponseSize", kind: "int" },
|
|
35746
|
+
{ from: "advanced.maxRecursionDepth", to: "maxRecursionDepth", kind: "int" },
|
|
35747
|
+
{ from: "advanced.maxFileOperations", to: "maxFileOperations", kind: "int" },
|
|
35748
|
+
{ from: "advanced.maxRequestsPerMinute", to: "rateLimits.maxRequestsPerMinute", kind: "int" },
|
|
35749
|
+
{ from: "advanced.maxParallelTasks", to: "rateLimits.maxParallelTasks", kind: "int" },
|
|
35750
|
+
{ from: "advanced.contentScanEnabled", to: "contentScan.enabled", kind: "bool" },
|
|
35751
|
+
{ from: "advanced.sessionEncryptionEnabled", to: "sessionEncryption.enabled", kind: "bool" },
|
|
35752
|
+
{ from: "advanced.auditNotifierEnabled", to: "auditNotifier.enabled", kind: "bool" },
|
|
35753
|
+
{ from: "advanced.auditNotifierMinSeverity", to: "auditNotifier.minSeverity", kind: "severity" },
|
|
35754
|
+
{ from: "advanced.auditNotifierEventTypes", to: "auditNotifier.eventTypes", kind: "list" },
|
|
35755
|
+
{ from: "advanced.auditNotifierMaxRetries", to: "auditNotifier.maxRetries", kind: "int" },
|
|
35756
|
+
{ from: "advanced.auditNotifierWebhookTimeout", to: "auditNotifier.webhookTimeout", kind: "int" }
|
|
35757
|
+
];
|
|
35357
35758
|
});
|
|
35358
35759
|
|
|
35359
35760
|
// src/cli/setup-order.ts
|
|
@@ -51823,6 +52224,11 @@ function makeForwarder(s, sink = (ev) => broadcast(ev, s), opts = {}) {
|
|
|
51823
52224
|
schedule();
|
|
51824
52225
|
},
|
|
51825
52226
|
onMeta(chunk, kind) {
|
|
52227
|
+
if (kind === "retract") {
|
|
52228
|
+
flush();
|
|
52229
|
+
sink({ type: "retract", ts: Date.now(), reason: stripAnsi2(chunk) });
|
|
52230
|
+
return;
|
|
52231
|
+
}
|
|
51826
52232
|
const next = kind ?? "status";
|
|
51827
52233
|
if (buf.meta && (buf.metaKind ?? "status") !== next)
|
|
51828
52234
|
flush();
|
|
@@ -53633,9 +54039,11 @@ function servePreview(res, absReal, buf) {
|
|
|
53633
54039
|
|
|
53634
54040
|
// src/modules/webui/setup-api.ts
|
|
53635
54041
|
init_i18n();
|
|
54042
|
+
init_registry_extra();
|
|
53636
54043
|
init_apply();
|
|
53637
54044
|
init_answers();
|
|
53638
54045
|
init_discovery();
|
|
54046
|
+
init_provider_context();
|
|
53639
54047
|
function str3(rec, key) {
|
|
53640
54048
|
const v = rec[key];
|
|
53641
54049
|
return typeof v === "string" ? v.trim() : "";
|
|
@@ -53645,6 +54053,76 @@ function positiveInt(rec, key) {
|
|
|
53645
54053
|
const n = typeof v === "number" ? v : typeof v === "string" ? Number(v) : NaN;
|
|
53646
54054
|
return Number.isInteger(n) && n > 0 ? n : null;
|
|
53647
54055
|
}
|
|
54056
|
+
var SEVERITIES3 = ["low", "medium", "high", "critical"];
|
|
54057
|
+
var MAX_DETAIL_LIST = 200;
|
|
54058
|
+
var MAX_DETAIL_ITEM = 200;
|
|
54059
|
+
function strList(v) {
|
|
54060
|
+
if (!Array.isArray(v))
|
|
54061
|
+
return;
|
|
54062
|
+
const out = v.filter((x) => typeof x === "string").map((x) => x.trim()).filter((x) => x !== "" && x.length <= MAX_DETAIL_ITEM).slice(0, MAX_DETAIL_LIST);
|
|
54063
|
+
return out;
|
|
54064
|
+
}
|
|
54065
|
+
function optBool(v) {
|
|
54066
|
+
return typeof v === "boolean" ? v : undefined;
|
|
54067
|
+
}
|
|
54068
|
+
function optInt(v) {
|
|
54069
|
+
const n = typeof v === "number" ? v : typeof v === "string" ? Number(v) : NaN;
|
|
54070
|
+
return Number.isInteger(n) && n > 0 ? n : undefined;
|
|
54071
|
+
}
|
|
54072
|
+
function parseSecurityDetail(v) {
|
|
54073
|
+
if (!v || typeof v !== "object")
|
|
54074
|
+
return;
|
|
54075
|
+
const rec = v;
|
|
54076
|
+
const detail = {};
|
|
54077
|
+
const bash = rec.bash;
|
|
54078
|
+
if (bash && typeof bash === "object") {
|
|
54079
|
+
detail.bash = {
|
|
54080
|
+
blacklist: strList(bash.blacklist),
|
|
54081
|
+
whitelist: strList(bash.whitelist),
|
|
54082
|
+
logCommands: optBool(bash.logCommands)
|
|
54083
|
+
};
|
|
54084
|
+
}
|
|
54085
|
+
const paths = rec.paths;
|
|
54086
|
+
if (paths && typeof paths === "object") {
|
|
54087
|
+
detail.paths = { denied: strList(paths.denied), allowed: strList(paths.allowed) };
|
|
54088
|
+
}
|
|
54089
|
+
const flags = rec.flags;
|
|
54090
|
+
if (flags && typeof flags === "object") {
|
|
54091
|
+
detail.flags = {
|
|
54092
|
+
dangerousFlags: strList(flags.dangerousFlags),
|
|
54093
|
+
dangerousOperators: strList(flags.dangerousOperators)
|
|
54094
|
+
};
|
|
54095
|
+
}
|
|
54096
|
+
const network = rec.network;
|
|
54097
|
+
if (network && typeof network === "object") {
|
|
54098
|
+
detail.network = {
|
|
54099
|
+
enabled: optBool(network.enabled),
|
|
54100
|
+
deniedDomains: strList(network.deniedDomains),
|
|
54101
|
+
allowedDomains: strList(network.allowedDomains),
|
|
54102
|
+
requestTimeout: optInt(network.requestTimeout),
|
|
54103
|
+
maxResponseSize: optInt(network.maxResponseSize)
|
|
54104
|
+
};
|
|
54105
|
+
}
|
|
54106
|
+
const adv = rec.advanced;
|
|
54107
|
+
if (adv && typeof adv === "object") {
|
|
54108
|
+
const rawSev = adv.auditNotifierMinSeverity;
|
|
54109
|
+
const sev = typeof rawSev === "string" && SEVERITIES3.includes(rawSev) ? rawSev : undefined;
|
|
54110
|
+
detail.advanced = {
|
|
54111
|
+
maxRecursionDepth: optInt(adv.maxRecursionDepth),
|
|
54112
|
+
maxFileOperations: optInt(adv.maxFileOperations),
|
|
54113
|
+
maxRequestsPerMinute: optInt(adv.maxRequestsPerMinute),
|
|
54114
|
+
maxParallelTasks: optInt(adv.maxParallelTasks),
|
|
54115
|
+
contentScanEnabled: optBool(adv.contentScanEnabled),
|
|
54116
|
+
sessionEncryptionEnabled: optBool(adv.sessionEncryptionEnabled),
|
|
54117
|
+
auditNotifierEnabled: optBool(adv.auditNotifierEnabled),
|
|
54118
|
+
auditNotifierMinSeverity: sev,
|
|
54119
|
+
auditNotifierEventTypes: strList(adv.auditNotifierEventTypes)?.filter((e) => AUDIT_EVENT_TYPES.includes(e)),
|
|
54120
|
+
auditNotifierMaxRetries: optInt(adv.auditNotifierMaxRetries),
|
|
54121
|
+
auditNotifierWebhookTimeout: optInt(adv.auditNotifierWebhookTimeout)
|
|
54122
|
+
};
|
|
54123
|
+
}
|
|
54124
|
+
return detail;
|
|
54125
|
+
}
|
|
53648
54126
|
function parseSetupAnswers(rec) {
|
|
53649
54127
|
const provider = str3(rec, "provider");
|
|
53650
54128
|
const apiBase = str3(rec, "apiBase");
|
|
@@ -53674,15 +54152,17 @@ function parseSetupAnswers(rec) {
|
|
|
53674
54152
|
locale,
|
|
53675
54153
|
securityBashBlock: rec.securityBashBlock,
|
|
53676
54154
|
securityFlagsBlock: rec.securityFlagsBlock,
|
|
53677
|
-
securityPathsRestrict: rec.securityPathsRestrict
|
|
54155
|
+
securityPathsRestrict: rec.securityPathsRestrict,
|
|
54156
|
+
securityDetail: parseSecurityDetail(rec.securityDetail)
|
|
53678
54157
|
};
|
|
53679
54158
|
}
|
|
53680
54159
|
function handleSetupGet(res, s) {
|
|
54160
|
+
const defaults = s.setup?.defaults ?? { contextWindow: 0, maxToolIterations: 0 };
|
|
53681
54161
|
sendJson(res, 200, {
|
|
53682
54162
|
required: s.setupRequired,
|
|
53683
54163
|
languages: s.setup?.languages ?? [],
|
|
53684
54164
|
providers: s.setup?.providers ?? [],
|
|
53685
|
-
defaults: s.
|
|
54165
|
+
defaults: s.config?.security ? { ...defaults, security: securityDetailFromConfig(s.config.security) } : defaults
|
|
53686
54166
|
});
|
|
53687
54167
|
}
|
|
53688
54168
|
function handleSetupScan(req, res, s) {
|
|
@@ -53728,6 +54208,31 @@ function handleSetupTest(req, res, s) {
|
|
|
53728
54208
|
readBodyError(req, res, err);
|
|
53729
54209
|
});
|
|
53730
54210
|
}
|
|
54211
|
+
function handleSetupContext(req, res, s) {
|
|
54212
|
+
readJsonBody(req, s.maxBodyBytes).then(async (rec) => {
|
|
54213
|
+
if (!rec) {
|
|
54214
|
+
sendJson(res, 400, { ok: false, error: "invalid JSON" });
|
|
54215
|
+
return;
|
|
54216
|
+
}
|
|
54217
|
+
const baseUrl = str3(rec, "baseUrl");
|
|
54218
|
+
const model = str3(rec, "model");
|
|
54219
|
+
if (!baseUrl || !model) {
|
|
54220
|
+
sendJson(res, 400, { ok: false, error: "invalid-context-request" });
|
|
54221
|
+
return;
|
|
54222
|
+
}
|
|
54223
|
+
const probe = await probeProviderContext(baseUrl, model, {
|
|
54224
|
+
apiKey: str3(rec, "apiKey") || undefined,
|
|
54225
|
+
providerType: str3(rec, "providerType") || undefined
|
|
54226
|
+
});
|
|
54227
|
+
if (!probe) {
|
|
54228
|
+
sendJson(res, 200, { ok: false });
|
|
54229
|
+
return;
|
|
54230
|
+
}
|
|
54231
|
+
sendJson(res, 200, { ok: true, contextWindow: probe.actual, model: probe.model });
|
|
54232
|
+
}).catch((err) => {
|
|
54233
|
+
readBodyError(req, res, err);
|
|
54234
|
+
});
|
|
54235
|
+
}
|
|
53731
54236
|
function handleSetupLocale(req, res, s) {
|
|
53732
54237
|
readJsonBody(req, s.maxBodyBytes).then((rec) => {
|
|
53733
54238
|
if (!rec) {
|
|
@@ -53800,6 +54305,10 @@ function handleSetupApi(req, res, reqUrl, s) {
|
|
|
53800
54305
|
handleSetupTest(req, res, s);
|
|
53801
54306
|
return true;
|
|
53802
54307
|
}
|
|
54308
|
+
if (p === "/api/setup/context" && req.method === "POST") {
|
|
54309
|
+
handleSetupContext(req, res, s);
|
|
54310
|
+
return true;
|
|
54311
|
+
}
|
|
53803
54312
|
if (p === "/api/setup/locale" && req.method === "POST") {
|
|
53804
54313
|
handleSetupLocale(req, res, s);
|
|
53805
54314
|
return true;
|
|
@@ -53877,6 +54386,7 @@ var API_ROUTES = {
|
|
|
53877
54386
|
"GET /api/models": (_req, res, s) => handleModelsGet(res, s),
|
|
53878
54387
|
"POST /api/command": (req, res, s) => handleCommandPost(req, res, s),
|
|
53879
54388
|
"POST /api/interrupt": (_req, res, s) => handleInterruptPost(res, s),
|
|
54389
|
+
"POST /api/index": (_req, res, s) => handleIndexPost(res, s),
|
|
53880
54390
|
"POST /api/mcp": (req, res, s) => handleMcpCreate(req, res, s),
|
|
53881
54391
|
"GET /api/lsp": (_req, res, s) => handleLspGet(res, s)
|
|
53882
54392
|
};
|
|
@@ -53918,6 +54428,14 @@ function handleInterruptPost(res, s) {
|
|
|
53918
54428
|
s.bridge?.interrupt();
|
|
53919
54429
|
sendJson(res, 200, { ok: true });
|
|
53920
54430
|
}
|
|
54431
|
+
function handleIndexPost(res, s) {
|
|
54432
|
+
if (!s.refreshIndex) {
|
|
54433
|
+
sendJson(res, 503, { error: "unavailable" });
|
|
54434
|
+
return;
|
|
54435
|
+
}
|
|
54436
|
+
Promise.resolve(s.refreshIndex()).catch(() => {});
|
|
54437
|
+
sendJson(res, 202, { ok: true });
|
|
54438
|
+
}
|
|
53921
54439
|
function handleRequest(req, res, assets, s) {
|
|
53922
54440
|
let reqUrl;
|
|
53923
54441
|
try {
|
|
@@ -54178,8 +54696,16 @@ async function startWebUi(deps) {
|
|
|
54178
54696
|
state.plugins = deps.plugins;
|
|
54179
54697
|
state.workspace = deps.baseDir;
|
|
54180
54698
|
state.agentsMd = deps.agentsMd;
|
|
54181
|
-
if (deps.indexer)
|
|
54699
|
+
if (deps.indexer) {
|
|
54182
54700
|
state.projectMapStats = () => deps.indexer?.stats() ?? null;
|
|
54701
|
+
if (deps.indexer.refresh) {
|
|
54702
|
+
const refresh = deps.indexer.refresh.bind(deps.indexer);
|
|
54703
|
+
state.refreshIndex = async () => {
|
|
54704
|
+
await refresh();
|
|
54705
|
+
broadcast({ type: "state", ts: Date.now(), busy: state.busy, usage: state.usage }, state);
|
|
54706
|
+
};
|
|
54707
|
+
}
|
|
54708
|
+
}
|
|
54183
54709
|
state.saveConfig = (config) => saveConfig(config, CONFIG_FILE, deps.configDir);
|
|
54184
54710
|
installWebPromptIO({
|
|
54185
54711
|
state,
|
|
@@ -54223,10 +54749,7 @@ async function startWebUi(deps) {
|
|
|
54223
54749
|
contextProbe: probe,
|
|
54224
54750
|
workspace: deps.baseDir,
|
|
54225
54751
|
instructions: deps.agentsMd,
|
|
54226
|
-
projectMap: (
|
|
54227
|
-
const s = deps.indexer?.stats();
|
|
54228
|
-
return s ? { indexed: true, ...s } : { indexed: false, files: 0, symbols: 0, tokens: 0 };
|
|
54229
|
-
})()
|
|
54752
|
+
projectMap: deps.indexer?.mapInfo?.()
|
|
54230
54753
|
}, state.busy),
|
|
54231
54754
|
addPendingImage: (url2) => {
|
|
54232
54755
|
deps.agent.contextManager?.addPendingImage?.({
|